【问题标题】:Indefinite stale of TCP packet receptionTCP 数据包接收无限期失效
【发布时间】:2018-06-05 04:52:15
【问题描述】:

部署环境: 我在 windows 10 操作系统上使用 JAVA 创建了一个 TCP 服务器。我的 TCP 客户端程序是用 VC++ 编写的,在 windows 7 操作系统上运行(我无法控制这部分代码,它对我来说是一个黑盒子)。

我的 TCP 服务器代码是这样的:

Socket s = ss.accept();
s.setReceiveBufferSize(2000);
s.setSendBufferSize(2000);
s.setTcpNoDelay(true);
s.setKeepAlive(true);
new TcpConnectionHandler(s,this.packetHandler); 

下面是 TCP 连接处理程序 sn-p:

InputStream incomingPacketBuffer = this.clientSocket.getInputStream();
OutputStream outgoingPacketBuffer = this.clientSocket.getOutputStream();
int bufferLen=0;
byte inBuffer[] = new byte[this.clientSocket.getReceiveBufferSize()];
byte outBuffer[] = new byte[this.clientSocket.getSendBufferSize()]; 
while(this.clientSocket.isConnected())
{
    bufferLen = incomingPacketBuffer.read(inBuffer);
    if(bufferLen>0)
    {
        outBuffer = (byte[]) this.packetHandlerModule.invoke(this.packetHandler,Arrays.copyOf(inBuffer, bufferLen));
    }
    if(outBuffer != null)
    {
        if(this.clientSocket.isConnected())
        {
            outgoingPacketBuffer.write(outBuffer);
            outgoingPacketBuffer.flush();
        }
    }
}
this.clientSocket.close();

通信是基于数据包的,协议/解析由packetHandler 处理。

我尝试过的另外两个变体:

  1. 我试图在回复发送回客户端时关闭套接字。即收到一包数据后,回复客户端,关闭连接。

  2. 在使用read方法之前,我使用了inputStream.available

我面临的问题:

大多数情况下,TCP 服务器会在一秒钟内回复传入的数据包。如果服务器在一段空闲时间后收到一个数据包,则服务器不会回复该数据包。有时即使正在进行主动通信,也不会发送回复。其次,即使客户端套接字关闭了连接,isConnected 函数也会返回 true。

调试尝试:

  1. 我使用 teraterm 发送数据包并检查它。行为是一样的。只要我一个接一个地发送数据包,我就没有问题。如果一个数据包没有得到回复,那么之后发送的每个数据包都不会得到服务器的回复。

  2. 当我在服务器控制台按Ctrl+C 时,所有从teraterm 发送的数据包都被TCP 服务器处理并返回回复。在此之后,服务器在一段时间内正常工作。

  3. 我用wireshark检查了数据包流。当回复正常发回时,连同客户端请求的ACK(SYN、SYN+ACK、ACK、PSH、PSH+ACK、FYN、FYN+ACK、ACK)一起发送。当回复过时(可能不是正确的术语,它卡在 inputStream.available 或 inputStream.read 中),服务器只发送 ACK 数据包(SYN、SYN+ACK、ACK、PSH、ACK)。

  4. 我在stackexchange中查看了许多论坛和其他线程,了解了Nagle的算法,应用程序必须注意TCP中的分组,TCP可能会以8+12或15+5或任何此类方式接收10+10个数据包。服务器代码负责打包,setKeepAlive 设置为 true(从服务器发送数据包时没有问题)。

简而言之:“有时,即使有传入的数据包,TCP 读取调用也会被长时间阻塞。当按下 Ctrl+C 时,它们正在被处理。”

PS:我刚开始在 stackexchange 上发布查询,如果在制定查询的方式上有任何问题,请告诉我。

PPS:很抱歉发了这么长的帖子。

更新

  1. EJB 的评论帮助我确定了对等断开连接。

  2. 我使用 Ubuntu 16.04 作为服务器的操作系统进行了另一个设置。已经3天了,windows系统偶尔出现这个问题。 Ubuntu 16.04 从未过时。

【问题讨论】:

  • isConnected() 在你测试它的时候不可能是假的。这不是对对等断开连接的测试,但 read() 返回 -1 是,而您忽略了这一点。
  • @EJB,谢谢。这有助于我正确关闭连接。
  • 我确定它对您有所帮助,但它并未反映在您问题的代码中。 “无限期陈旧”是什么意思?
  • 客户端向服务器发送了一个数据包。服务器上的 Wireshark 显示一个 ack 被发送回客户端。但是没有回复。在调试模式下,我了解到读取调用从未返回。如果我在命令提示符下按 Ctrl+C,则读取返回并正在处理数据包。我在 Ubuntu 16.04 上运行了另一个服务器实例,到目前为止我还没有遇到任何问题。我的要求是它应该在Windows系统中运行。

标签: java windows sockets networking tcp


【解决方案1】:

需要考虑的一些事项;

  • TCP 缓冲区大小通常至少为 8K,我认为您不能将它们缩小到 2000 字节,或者如果可以,我认为这不是一个好主意。
  • byte[] 的大小在 2K 左右并不重要,您也可以选择一个值。
  • 您不必多次创建缓冲区。

所以简而言之我会尝试。

Socket s = ss.accept();
s.setTcpNoDelay(true);
s.setKeepAlive(true);
new TcpConnectionHandler(s,this.packetHandler);

try {
    InputStream in = this.clientSocket.getInputStream();
    OutputStream out = this.clientSocket.getOutputStream();
    int bufferLen = 0;
    byte[] buffer = new byte[2048];
    while ((bufferLen = in.read(buffer)) > 0) {
        out.write(buffer, 0, bufferLen); // not buffered so no need to flush
    }
} finally {
    this.clientSocket.close();
}

有时,即使有传入数据包,TCP 读取调用也会被长时间阻塞。

将编写一个测试 Java 客户端来查看这不是由于 Java 中的行为造成的。

【讨论】:

  • 感谢您的意见。我知道不需要每次都创建缓冲区。但主要问题仍然存在。我的数据包的最大大小限制为 200 字节,因此到目前为止 2k 足以处理。正如我已经提到的,我使用 teraterm 通过 TCP 协议连接到我的服务器。还有,在随机时间我得到这个块。我认为这里的关键是终端中的 Ctrl+C。当我中断终端时,数据包正在被送达。
  • @Raavanan 上面的代码应该做你想做的,在这种情况下我会写一个java客户端来证明这一点。不知道你的终端程序有没有异常。
  • 不是终端的问题,我用终端检查过其他情况。无论如何,我想操作系统也会有所不同。请查看更新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-27
  • 2014-06-07
  • 1970-01-01
  • 2016-12-16
  • 2011-09-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多