【问题标题】: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(服务器也是如此),但这使得处理网络问题变得更加容易。它是一个反应器,而不是一个前摄器。