15

我目前正在为一个项目编写幼稚的网络代码,一位伙伴向我暗示,当我以迭代方式从服务器向所有客户端发送一包信息时,当其中一个客户端没有正确响应时,我可能会出现严重的延迟.

他以拖钓而闻名,所以我在实现一个辅助线程时持怀疑态度,该线程现在负责将数据发送到客户端,有一个队列,服务器只需将包添加到该队列上,然后由线程读取以发送数据。

我现在思考后的问题是天气与否 Java Socket 的 OutputStream 实际上将他想要发送的内容排入队列,从而预先消除了对队列的需要。仅当服务器阻塞时,只要他没有从客户端收到已收到发送对象的响应,就会出现严重问题的可能性。

谢谢。

4

3 回答 3

12

您的朋友是对的,但它更多地与协议的工作方式有关。大大简化了发送到客户端的数据包需要确认。如果客户端没有响应(无法读取传入的数据,计算机负载过重等),服务器将不会收到确认并停止发送数据。TCP/IP 中内置的这一机制可防止通信的一端发送大量数据,而无需确保另一端接收到这些数据。反过来,这避免了重新发送大量数据的要求。

在 Java 中,这表现为阻塞写入OutputStream. 在客户端准备好接收数据之前,底层 TCP/IP 堆栈/操作系统不允许发送更多数据。

你可以很容易地测试这个!我实现了接受连接但无法读取传入数据的简单服务器:

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            final ServerSocket serverSocket = new ServerSocket(4444);
            final Socket clientSocket = serverSocket.accept();
            final InputStream inputStream = clientSocket.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}).start();

还有一个简单的客户端,它只以 4K 批次发送尽可能多的数据:

final Socket client = new Socket("localhost", 4444);
final OutputStream outputStream = client.getOutputStream();
int packet = 0;
while(true) {
    System.out.println(++packet);
    outputStream.write(new byte[1024 * 4]);
}

客户端循环在 95 次迭代后挂在我的计算机上(您的里程可能会有所不同)。但是,如果我从inputStream服务器线程中读取-循环继续进行。

于 2012-05-13T19:44:00.953 回答
4

当然,当您写入套接字时,此写入会被缓冲。Socket 对象具有setSendBufferSize()设置此缓冲区大小的方法。如果您的写入可以缓存在此缓冲区中,那么当然,您可以立即在以下套接字上进行迭代。否则这个缓冲区需要立即刷新到客户端。因此,在冲洗期间,您将被阻塞。如果要避免在刷新缓冲区时被阻塞,则必须SocketChannel在非阻塞 I/O 中使用。无论如何,并发写入多个套接字的最佳选择是使用不同的线程管理每个套接字,以便可以同时执行所有写入。

于 2012-05-13T19:54:48.517 回答
2

OutputStream 正在阻塞。它可能有一些缓冲,但是如果服务器从不消耗字节(任何固定缓冲区最终都会填满),这对您没有多大帮助。所以你的朋友是对的,你需要在单独的线程中编写,或者使用更高级的东西,比如 nio。

在阅读方面,您可以使用 available() 来避免阻塞。写入端不存在匹配的调用。我希望有。

于 2012-05-13T19:49:09.710 回答