【问题标题】:Handling TCP Packet Fragmentation处理 TCP 数据包碎片
【发布时间】:2014-04-09 22:15:52
【问题描述】:

嗯,对我来说,数据包碎片化是如何发生的似乎很模糊,因为我的本地测试从来没有发生过这种情况,而且我不知道在通知我的应用程序我实际上收到了一些信息之前要如何处理碎片化的数据包。

这是我从连接的客户端套接字接收的方式

var asynchronousState = (AsynchronousState) ar.AsyncState; // AsynchronousState is an entity that Holds each connected socket's Buffer, IPEndPoint and so on...
try {
    Socket socket = asynchronousState.Socket;
    int length = socket.EndReceive(ar);
    if (0 != length) {
        if (null != ClientReceive) {
            var bytes = new byte[length];
            Array.Copy(asynchronousState.Buffer, bytes, length);
            ClientReceive(asynchronousState, bytes);
            Array.Clear(asynchronousState.Buffer, 0, asynchronousState.Buffer.Length);
        }
        if (socket.Connected) {
            socket.BeginReceive(asynchronousState.Buffer, 0, asynchronousState.Buffer.Length, SocketFlags.None, HandleAsyncReceive, asynchronousState);
            return;
        }
    }
    DisposeSocket(asynchronousState);
}
catch (SocketException exception) {
    if (exception.SocketErrorCode != SocketError.Disconnecting &&
        exception.SocketErrorCode != SocketError.NotConnected &&
        exception.SocketErrorCode != SocketError.ConnectionReset &&
        exception.SocketErrorCode != SocketError.ConnectionAborted &&
        exception.SocketErrorCode != SocketError.Shutdown) {
        Console.WriteLine(exception);
        Core.Logger.Log(exception);
    }

    DisposeSocket(asynchronousState);
}

我如何构建我的数据包

public class ExchangeMessage : PacketStructure //Packet Structure is a custom builder similar to `BinaryWrite/Reader` : This returns a byte[] or builds from a byte[]
{
    public int Length;
    public int Type;

    public byte[] PublicKey
    {
        get { return ReadArray(140, 4); }
        set { WriteArray(value, 4); }
    }

    public ExchangeMessage(byte[] receivedPacket) : base(receivedPacket) {}

    public ExchangeMessage(int length, int type) : base(length, type)
    {
        Length = length;
        Type = type;
    }
}

现在,每当我收到期望应用程序能够反序列化或重建我的 Packet 的东西时,我都会调用 ClientReceive 事件处理程序。 What if the packet was fragmented ?

我很困惑,因为通过研究这个主题,我发现其中有很多不同的意见,我对此表示怀疑 If you are building your Packet by yourself, it will not be fragmented!

【问题讨论】:

  • 1) TCP 不使用数据包。您可能对 UDP 感到困惑。 2) 如果您在 TCP 之上构建数据包,您将必须意识到您不会一次获得整个数据包。您将需要有长度字段等来重建数据包。
  • @BobDalgleish:所以我应该将套接字类型更改为 UDP,我很高兴吗?我使用 TCP 是否应该再次调用 EndReceive 以获取更多发送的数据?或者如何处理这种行为?对不起,我完全一无所知。
  • @Bob:当然 TCP 使用数据包。我认为您的意思是边界不会保留给用户。
  • 阅读此相关问题clientStream.Read returns wrong number of bytes。对于二进制数据,我建议写出每条消息的长度,然后循环读取,直到收到完整的消息。
  • @Loki:这与发生在 IP 层的数据包碎片无关。

标签: c# sockets


【解决方案1】:

Sockets API 不会传递数据包片段,只会传递完整的数据包。 TCP/IP 堆栈会缓冲接收到的片段,直到它有一个完整的数据包,如果某些片段丢失,则整个数据包将被丢弃,并且必须完整地重新传输。

这是路径 MTU 检测提高性能的原因之一 - 它可以防止中间通道碎片和相应增加的数据包错误率。发送较小的数据包会导致同样多的错误,但可以通过选择性 ACK 处理整个丢失的数据包,这比丢弃接收到的部分数据包的片段更有效。

如果您使用较低级别的网络 API,您可能会看到数据包片段。


请注意,TCP 数据包与 send() 调用不对应 1:1。 Nagle算法可以将多次写入合并成一个包,大于路径MTU的一次写入会产生多个包。我认为这就是 Bob 在他的评论中所暗示的。


分片、丢包和重传都在 TCP/IP 内部处理。您的应用程序不需要担心它。您的应用程序应将 TCP 套接字视为字节流。

您输入的字节以相同的顺序出现。需要多长时间和一次出来多少无法保证,并且超出您的控制范围。

由于您希望将数据视为具有结构,而不仅仅是字节,因此您必须自己添加该结构。长度前缀和记录分隔符都是执行此操作的常用方法。

例如,HTTP 对命令/响应和元数据使用记录分隔符。 HTTP 命令和所有标头由\r\n 分隔。缺点是如果分隔符出现在数据中,它们需要被转义。 This example is borrowed from wikipedia.

HTTP/1.1 200 OK
Date: Mon, 23 May 2005 22:38:34 GMT
Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)
Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT
ETag: "3f80f-1b6-3e1cb03b"
Content-Type: text/html; charset=UTF-8
Content-Length: 131
Accept-Ranges: bytes
Connection: close

<html>
<head>
  <title>An Example Page</title>
</head>
<body>
  Hello World, this is a very simple HTML document.
</body>
</html>

也使用长度前缀。 Content-Length 标头给出了有效负载的长度。这允许任何字节出现在有效负载中。

【讨论】:

  • 所以你的意思是我不需要担心Fragmentation?如果我在重建我的消息(或数据包)时遇到了一些错误,我只是重新请求它?。
  • 如果你在我的接收函数中看到我 EndReceive,然后检查长度是否大于 0 我只是通知我的处理程序我收到了一条消息,处理程序通过它切换它是我写的 Type .
  • @Rune:分片、丢包和重传都在 TCP/IP 内部处理。您的应用程序不需要担心它。您的应用程序应将 TCP 套接字视为字节流。您输入的字节以相同的顺序出现。不保证需要多长时间以及一次出来多少。
  • 因此得出结论,任何在 TCP 上中继的大型程序使用的最佳情况是什么,以便在某种程度上保证它们处理(整个消息),就好像我将不完整的消息传递给我的消息处理它可能会导致异常。
  • 示例将不胜感激。
猜你喜欢
  • 1970-01-01
  • 2010-10-02
  • 1970-01-01
  • 2020-06-06
  • 2014-04-08
  • 2017-04-05
  • 2014-09-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多