【问题标题】:Scenario in multi-threaded socket communication多线程socket通信中的场景
【发布时间】:2017-04-26 18:35:00
【问题描述】:

我有套接字客户端应用程序,在应用程序启动期间创建套接字(与服务器建立连接)并启动两个并行运行的线程。

Thread-1:使用 read 方法连续读取套接字(阻塞直到接收到数据)

Thread-2:连续写入数据。

在写socket时,如果thread-2收到IO异常,则丢弃已有的socket,创建新的socket,开始通信。由于线程 2 丢弃了套接字,线程 1 收到空指针异常。 我们有什么策略来处理这个

【问题讨论】:

  • 这不是“异步”,这是多线程的。不是一回事。

标签: java multithreading sockets


【解决方案1】:

线程 2 需要在关闭套接字之前关闭输入。这将导致线程接收并结束流,这将导致它关闭套接字并退出。然后线程 2 可以创建另一个套接字并启动另一个读取线程。

【讨论】:

    【解决方案2】:

    您开始遇到与proactor 系统设计风格相关的问题。解决这个问题需要两个线程之间进行一些通信。选择这种沟通是什么是它变得混乱的地方。它必须是阻止 thread1 尝试读取套接字的东西。我不太擅长 Java,但在 C 中,这意味着使用信号。

    我建议你避免使用信号,即使在 Java 中有等价物。

    一个更好的选择是在调用select()(或任何Java 等效项)时阻塞thread1,在套接字和管道上等待。 Thread2 在要关闭套接字时写入管道,thread1 从 select() 返回,将响应写入管道中的 thread2,然后再次调用 select(),但仅在管道上。 Thread2 读取该响应,关闭套接字,打开一个新套接字,向管道发送其他内容以再次唤醒 thread1,现在可以返回到select(),但这次是在管道和新套接字上。这实现了thread1和thread2之间的执行集合; thread2 可以关闭旧套接字并打开一个新套接字,因为它知道(通过管道通信)thread1 何时不使用套接字。

    这有点乱。而且也变得更像reactor 设计模式。在这种情况下,也可以只使用一个线程使用select() 来选择是否读取套接字作为它正在执行的任何循环的一部分。这个单线程将在数据可用时读取数据,而不是进行阻塞读取以希望数据到达。如果套接字写入出现问题并且需要更换套接字,它就会这样做;没有其他线程可以同步。假设您的套接字连接到网络上的远程服务器(而不是同一台机器上的服务),以太网的速度仍然是主要的瓶颈;反应堆式系统并不慢。

    一般来说,使用reactor 系统样式处理网络故障要容易得多,因为您没有承诺执行其他线程知道不合适的操作的线程。不幸的是,大多数编程环境都是proactor,例如 Windows、Boost ASIO、RabbitMQ 等。Proactor 系统在出现问题之前都很好,之后通常需要丢弃整个过程,因为它很容易变得异常复杂程序员来整理所有无聊的回调和异步 IO。

    如果可以,一个选择是使用 ZeroMQ。这要求您在任何地方都使用 ZeroMQ(服务器也是如此),但这使得处理网络问题变得更加容易。它是一个反应器,而不是一个前摄器。

    【讨论】: