【问题标题】:detecting TCP/IP packet loss检测 TCP/IP 数据包丢失
【发布时间】:2024-01-21 21:37:01
【问题描述】:

我通过套接字代码进行 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()));
}

发送方法为:

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

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

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

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

TCP 被设计为能够在网络上丢弃数据包时做出反应。当数据包成功传送到其目的地时,目标系统将确认消息发送回源系统。如果在某个时间间隔内没有收到此确认,那可能是因为目标系统从未收到数据包,或者因为包含确认的数据包本身丢失了。在任何一种情况下,如果源系统在给定时间内没有收到确认,则源系统假定目标系统从未收到该消息并重新传输它。很容易看出,如果网络性能不佳,首先会丢失数据包,而这些重传消息所增加的负载只会进一步增加网络负载,这意味着将丢失更多数据包。这种行为可能会导致很快在网络上产生危急情况。

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

有什么帮助吗?

【问题讨论】:

  • 整个重传过程是在不同的层完成的。您无法通过 Java 访问它。它已经为你完成了。
  • 但是能保证重传会成功吗?能不失败吗?
  • 这就是你的 TCP。它将继续重试(或多或少)。如果在一定时间后不成功,您可能会收到超时错误。
  • 是的,关于那个超时,我在哪里可以得到那个超时?在java还是其他地方?
  • 它将传播到 Java,是的。查看Socket javadoc。

标签: java sockets networking tcp


【解决方案1】:

TCP 被设计为能够在网络上丢弃数据包时做出反应。

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

TCP 有一些功能可以为您完成一些工作,但您想知道它们的局限性是对的(许多人认为 TCP 是一种有保证的交付协议,没有上下文)。

关于这个有一个interesting discussion on the Linux Kernel Mailing List ("Re: Client receives TCP packets but does not ACK")

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

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

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

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

【讨论】:

  • 是的,但我的疑问是,哪里会抛出异常/error,因为out.println(mesaage)是非阻塞调用,发送消息后它会返回而不关心它是否到达目的地?跨度>
  • 不确定你的代码 sn-p 周围有什么,但这看起来像阻塞 I/O(也有无限超时)。无论如何,您仍然会遇到异常。
【解决方案2】:

TCP/IP 不会丢弃数据包。 TCP 实现中的拥塞控制算法负责重传。假设有稳定的数据流被发送,接收者将确认它收到的序列号返回给发送者。发送者可以使用确认来推断需要重新发送哪些数据包。发送者持有数据包直到它们被确认。

作为一个应用程序,除非 TCP 实现提供了接收拥塞通知的机制,否则它所能做的最好的事情就是为事务可以完成建立一个超时。如果在事务完成之前发生超时,应用程序可以声明网络过于拥塞,应用程序无法成功。

【讨论】:

    【解决方案3】:

    编写您需要的代码。如果您需要确认,请实施它们。如果您希望发件人知道收件人收到了信息,请让收件人发送某种确认。

    从应用程序的角度来看,TCP 提供了一个双向字节流。您可以通过简单地指定传达您需要传达的信息的字节流来传达您想要传达的任何信息。

    不要试图让 TCP 做任何其他事情。不要试图“教 TCP”你的协议。

    【讨论】:

    • 我不是要教 TCP 任何东西,而是要学习 TCP。 :P
    • 你是。您已经创建了一条消息。您正在询问该消息何时超时。 TCP 不知道什么是“消息”(记住,创建了这个“消息”)。您正在尝试将其作为一种消息协议来理解消息何时发生。如果您需要确认消息或超时,则必须对其进行编码。 TCP 对应用程序消息一无所知。