Java套接字:没有可用缓冲区(达到了最大连接数?)

5
我有一个大问题,我开发了一个客户端-服务器应用程序。客户端线程向服务器发送一个序列化对象,服务器会发送回一个序列化对象。目前我正在使用一个服务器和10个客户端线程,在约30秒后,每个客户端线程都会收到以下错误消息(IOException):

没有可用的缓冲区(已达到最大连接数?):连接

如果我查看netstat,我会看到有很多连接被创建,并且不断增长,所有连接都处于TIME_WAIT状态。我不知道为什么。我在服务器和客户端中每次都在finally块中关闭套接字。以下是一些代码:
在服务器中,我在socketHandlerThread中:
ServerSocket serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(5000);
while(true) {
       Socket socket = serverSocket.accept();
}

然后将新套接字放入LinkedBlockingQueue中,工作线程获取套接字并执行以下操作:

try {
      outputStream = new ObjectOutputStream(new BufferedOutputStream(socket.getOutputStream()));
      outputStream.flush();
      inStream = new ObjectInputStream(new BufferedInputStream(socket.getInputStream()));
      ClientRequest clientRequest = (ClientRequest) inStream.readObject();
...
      outputStream.writeObject(serverResponse);
      outputStream.flush();
} catch....
} finally {
                if (inStream != null) {
                    inStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
                if (socket != null) {
                    socket.close();
                }
}

在客户端,我有以下代码:
    try {
        socket = new Socket(host, port);
        outputStream = new ObjectOutputStream(new BufferedOutputStream(socket.getOutputStream()));
        outputStream.flush();
        inputStream = new ObjectInputStream(new BufferedInputStream(socket.getInputStream()));
        outputStream.writeObject(request);
        outputStream.flush();
        Object serverResponse = inputStream.readObject();
   } catch....
   } finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
            if (socket != null) {
                socket.close();
            }
   }

有人可以帮忙吗?我真的不知道哪里出了错。看起来套接字没有关闭,但我不知道为什么。

可能是因为我在服务器端将套接字放在队列中,导致套接字被复制了吗?

编辑:如果我将客户端和服务器分别放在运行Linux AMI的不同Amazon EC2经典实例上,则可以正常工作。这可能与Windows有关,或者问题只是我在同一台机器(我的本地PC)上运行客户端和服务器所致?

有人看到我的代码中有错误吗?

编辑2:如上所述,在EC2实例上它可以工作,但是如果使用netstat,它仍然显示大量的TIME_WAIT行。

以下是屏幕截图:

https://drive.google.com/file/d/0BzERdJrwWrNCWjhReGhpR2FBMUU/view?usp=sharing

https://drive.google.com/file/d/0BzERdJrwWrNCOG1TWGo5YmxlaTg/view?usp=sharing

第一张屏幕截图来自Windows。 "WARTEND"表示 "WAITING"(它是德语)。

第二张屏幕截图来自Amazon EC2(左边是客户端机器,右边是服务器机器)。


只是一个想法--为什么不使用RMI? RMI处理套接字线程问题。 - edharned
netstat 究竟在说什么?“等待”不是端口状态。 - user207421
@EJP,我已经更新了我的问题。 - machinery
你正在尝试太快地建立太多连接,导致本地端口耗尽。如果这是由于程序错误造成的,请修复该错误。(为什么不重用现有的连接!)如果您真的需要建立这么多连接,则需要捕获此情况,稍等一会儿,然后重试。 - David Schwartz
你可以尝试在服务器端关闭套接字之前加入 Thread.sleep(3000);,看看会发生什么? - biziclop
显示剩余5条评论
1个回答

3

连接在两端都关闭后,会进入TIME-WAIT状态。出于数据完整性的考虑,该状态会持续几分钟。

如果服务器上的缓冲问题是由于TIME-WAIT状态引起的,则解决方法是使服务器成为首先接收关闭请求的对等方。这将把TIME-WAIT状态转移到客户端,从而变得无害。

您可以通过将服务器端请求处理放入循环中来实现此操作,以便它可以处理每个连接的多个请求,并且只有在流结束时才关闭套接字。

for (;;)
{
    try
    {
        ClientRequest clientRequest = (ClientRequest) inStream.readObject();
        ...
        outputStream.writeObject(serverResponse);
        outputStream.flush();
    }
    catch (EOFException exc)
    {
        break;
    }
}

如果您在客户端实现连接池,将大大减少连接数,进一步降低缓冲问题的发生率。


你在德国的Windows机器上遇到的问题是你在同一主机上运行服务器和客户端,并创建了大量连接,这不是一个现实的测试。 - user207421
客户端与服务器建立连接,收到响应后关闭连接。然后建立新的连接,如此循环......因此,我有很多连接。您的解决方案很好,但我不允许更改此设置(无连接池)。无论如何,服务器是否仍应等待流结束,然后再关闭?它只接收一个对象。时间等待状态在客户端为什么是良性的?如果客户端有太多的时间等待,客户端将无法创建新的连接。 - machinery
但你应该像真的拥有对安全性的关注一样编写你的服务器,这才是关键。只需在请求/响应处理程序周围放置一个循环,当捕获到“EOFException”时终止即可。 - user207421
我会采用你的连接池解决方案。;) 我会在stackoverflow上开一个跟进帖子来解决这个问题。 - machinery
只有一个简短的最后问题。在我的上述代码中,客户端和服务器关闭inputStream、outputStream和socket的顺序是否正确? - machinery
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接