检测 TCP/IP 数据包丢失

Bat*_*tty 2 java sockets networking tcp

我通过套接字代码进行 tcp 通信,例如:

public void openConnection() throws Exception
{
   socket = new Socket();
   InetAddress iNet = InetAddress.getByName("server");
   InetSocketAddress sock = new InetSocketAddress(iNet, Integer.parseInt(port));
   socket.connect(sock, 0);

   out = new PrintWriter(socket.getOutputStream(), true);
   in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
Run Code Online (Sandbox Code Playgroud)

并发送方法为:

synchronized void send(String message)
{
 try
 {
    out.println(message);
 }
 catch (Exception e)
 {
    throw new RuntimeException(this.getClass() + ": Error Sending Message: "
        + message, e);
 }
}
Run Code Online (Sandbox Code Playgroud)

我从网页上读到,TCP/IP 不保证数据包的传递,它会重试,但如果网络太忙,数据包可能会被丢弃([链接])。1

在系统之间传输数据时可能会丢弃数据包,主要有两个原因:

  • 网络利用率高并导致拥塞
  • 网络硬件或连接器故障

TCP 旨在能够在网络上丢弃数据包时做出反应。当数据包成功传送到其目的地时,目的地系统将确认消息发送回源系统。如果在某个时间间隔内未收到此确认,则可能是因为目标系统从未收到过该数据包,或者是因为包含该确认的数据包本身已丢失。在任一情况下,如果源系统在给定时间内未收到确认,则源系统假定目标系统从未收到该消息并重新传输该消息。不难看出,如果网络性能差,首先丢包,这些重传报文增加的负载只会进一步增加网络的负载,这意味着更多的数据包将丢失。此行为可能会导致非常迅速地在网络上创建危急情况。

有什么办法可以检测到目的地是否成功接收到数据包,我不确定这out.println(message);会引发任何异常,因为这是非阻塞调用。它会将消息放入缓冲区并返回以让 TCP/IP 完成其工作。

有什么帮助吗?

Bru*_*uno 5

TCP 旨在能够在网络上丢弃数据包时做出反应。

正如您的引述所说,TCP 旨在自动对您在本文中提到的事件做出反应。因此,您在此级别无需执行任何操作,因为这将由您正在使用的 TCP 实现(例如在操作系统中)处理。

TCP 有一些特性可以为您完成一些工作,但是您对它们的局限性感到怀疑是正确的(许多人认为 TCP 是一种有保证的交付协议,没有上下文)。

在 Linux 内核邮件列表(“ Re: Client 收到 TCP 数据包但没有 ACK ”)上有一个有趣的讨论

在您的用例中,实际上,这意味着您应该将您的 TCP 连接视为一个数据流,在每个方向上(典型的错误是假设如果您从头开始发送 n 个字节,您将读取 n 个字节在另一端读取单个缓冲区),并处理可能的异常。

java.io.IOException正确处理s(特别是 中的子类java.net)将涵盖您描述的级别的错误情况:如果您遇到错误情况,请制定重试策略(取决于应用程序及其用户打算做什么)。也依赖超时(不要将套接字设置为永远阻塞)。

应用协议也可以设计成在接收命令或请求时发送它们自己的确认。

这是将职责分配给不同层的问题。TCP 堆栈实现将处理您提到的丢包问题,并在无法自行修复时抛出错误/异常。它的职责是与远程 TCP 堆栈进行通信。由于在大多数情况下您希望您的应用程序与远程应用程序通信,因此需要额外的确认。通常,需要设计应用程序协议来处理这些情况。(在某些情况下,您可以向上移动多个层,具体取决于哪个实体负责处理请求/命令。)