当两个客户端向服务器发送对象时,ObjectOutputStream writeObject 挂起

Posted

技术标签:

【中文标题】当两个客户端向服务器发送对象时,ObjectOutputStream writeObject 挂起【英文标题】:ObjectOutputStream writeObject hangs when two clients send objects to server 【发布时间】:2016-10-18 12:22:35 【问题描述】:

我正在编写客户端/服务器应用程序,其中多个客户端连接到服务器并通过 TCP 连接以高速率连续向服务器发送序列化对象。

我在客户端使用 ObjectOutputStream.writeObject,在服务器使用 ObjectInputStream.readObject。

服务器应用程序使用 serverSocket.accept() 在单个端口上接受客户端连接,并将 Socket 传递给新线程以读取对象。

当单个客户端连接并发送大约 25K 个对象/秒时 - 一切正常。一旦我启动第二个客户端,在很短的时间之后,一个或两个客户端都挂在其中一台服务器的 ObjectOutputStream.writeObject 上,而相应的服务器挂起在 ObjectInputStream.readObject 上。 双方都没有抛出异常。

如果速率非常低,假设总共 10-20/s - 它不会挂起,但在 100-1000/s 时会挂起。

在客户端机器上使用netstat -an可以看到对应链接的send-Q大概有30K左右。在服务器端,receive-Q 也是 ~30K。

在本地 Windows 上运行客户端/服务器时,我观察到类似的情况 - 客户端挂起,但服务器继续处理传入的对象,一旦赶上,客户端解锁并继续发送对象。

在本地,在 windows 上,服务器比客户端慢,但在 linux 上,运行在不同机器上的服务器实例数量足以满足客户端产生的速度。

有什么线索吗?

客户端代码片段:

Socket socket = new Socket(address, port);
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
while(true)

     IMessage msg = createMsg();
     outputStream.writeObject(msg);
     outputStream.flush();
     outputStream.reset();

接受连接的服务器代码:

while(active)

    Socket socket = serverSocket.accept();
    SocketThread socketThread = new SocketThread(socket);
    socketThread.setDaemon(true);
    socketThread.start();

服务器代码读取对象:

public class SocketThread extends Thread 
    
        Socket socket;
        public SocketThread(Socket socket)
        
            this.socket = socket;
        

        @Override
        public void run() 
            try 
                ObjectInputStream inStream = new ObjectInputStream(socket.getInputStream());
                while(true)
                
                    IMessage msg = (IMessage)inStream.readObject();
                    if(msg == null)
                        continue;
                    
                    List<IMessageHandler> handlers = handlersMap.get(msg.getClass());
                    for(IMessageHandler handler : handlers)
                        handler.onMessage(msg);
                    
                
             catch (IOException | ClassNotFoundException e) 
                e.printStackTrace();
            
        
    

【问题讨论】:

NB readObject() 不会返回 null,除非您发送 null。如果您不打算这样做,那么空测试毫无意义。 【参考方案1】:

您刚刚描述了发送方超过接收方时 TCP 的操作。接收方告诉发送方停止发送,所以发送方停止发送。当您使用阻塞 I/O 时,客户端会在内部阻塞 send()

这里没有问题要解决。

【讨论】:

不,它没有。服务器愉快地使用单个客户端处理 25K/s,但使用两个客户端冻结在 1K/s。 所以你在服务器上有并发问题。【参考方案2】:

问题是服务器端的处理程序使用了一些非线程安全的资源(如 Jedis 连接),所以它都是服务器端的堆栈。

线程安全解决了这个问题。

【讨论】:

所以你在服务器上遇到了并发问题,正如我在 21 个月前所说的那样。 那你为什么要告诉我们一些我在 21 个月前已经告诉过你的事情? 为你找到了一些可以讨论这个的论坛socialanxietysupport.com/forum/f24/human-behaviour-2122330/#/…

以上是关于当两个客户端向服务器发送对象时,ObjectOutputStream writeObject 挂起的主要内容,如果未能解决你的问题,请参考以下文章

向特定用户发送通知

当服务器向客户端发送消息时,android本机活页夹崩溃

如果需要两个cookie发往客服端,需要的代码是

如何处理生成它的对象内部的 SIGPIPE 错误?

在 Tigase 中向客户端发送自定义消息

TCP的三次握手和四次挥手学习