【问题标题】:Handle packet loss UDP socket programming in C在 C 中处理丢包 UDP 套接字编程
【发布时间】:2013-01-18 12:29:52
【问题描述】:

我想发送一个字节数组 X 次,直到达到缓冲区大小。我的客户端发送字节数组,直到缓冲区大小已满。但问题是数据包丢失,服务器只能收到缓冲区大小的 8/10。

客户:

while(writeSize < bufferSize)
{
    bytes_sent += sendto(servSocket, package, sizeof(package), 0, (struct sockaddr *)&server, sizeof(server));
    writeSize+= sizeof(package);    
}

服务器:

while(packetSize < bufferSize)
{
    bytes_recv += recvfrom(servSocket, package, sizeof(package), 0, (struct sockaddr*)&client, &size);
    packetSize += sizeof(package);
    printf("Bytes recieved: %d\n", bytes_recv);
}

我实在想不出解决这个问题的办法。当客户端已经发送了所有的包,而服务器出现包丢失时,while循环不会结束,有什么想法可以解决这个问题吗?

非常感谢

【问题讨论】:

  • TCP?它会解决这个问题。
  • erm,如果可靠的协议很重要,请使用 TCP。否则,您将不得不在应用程序级别(通过 UDP)实现某种形式的重传处理。前者比后者简单得多...
  • 任务是实现一个 UDP 服务器/客户端,很抱歉没有在帖子中包含它。我已经完成了 TCP 服务器/客户端。
  • 您确定丢包吗?您的服务器代码不太正确 - 在循环的每次迭代中,packetSize 应该增加 bytes_recv
  • 您缺少对系统调用的错误检查。万一 recvfrom()sendto() 失败,你的字节计数就会搞砸。

标签: c sockets udp


【解决方案1】:

哼哼。您正在使用 UDP。对于这个协议,如果需要的话,丢弃数据包是完全可以的。因此,就“发送的将到达”而言,它是不可靠的。您在客户端中要做的是检查您需要的所有数据包是否到达,如果没有,请礼貌地与您的服务器交谈以重新发送您没有收到的数据包。实现这些东西并不是那么容易,你最终可能会得到一个解决方案,它的性能可能比使用普通的旧 tcp 套接字要低,所以我的建议是首先切换到 tcp 连接。

【讨论】:

  • 我忘了包括我的任务是构建一个 UDP 服务器/客户端,因此更改为 TCP 不是一种选择。如果不是所有的包都到达也没关系,我想要一种退出while循环的方法,如果我没有到达服务器端的bufferSize。
【解决方案2】:

如果您必须使用 UDP 来传输较大的数据块,那么设计一个小的应用程序级协议来处理可能的数据包丢失和重新排序(这是 TCP 为您所做的一部分)。我会选择这样的:

  • 数据报小于 MTU(加上 IP 和 UDP 标头)的大小(例如 1024 字节)以避免 IP 碎片。
  • 每个数据报的固定长度标头包括数据长度序列号,因此您可以将数据重新拼接在一起,并检测丢失、重复和重新订购零件。
  • 确认来自接收方已成功接收和整理的内容。
  • 超时重传,当这些 ack 没有在适当的时间内到达时。

给你。我们又开始构建 TCP ...

【讨论】:

    【解决方案3】:

    当然 TCP 会是更好的选择,但如果你只有 UDP 选项,我会将数据包装到一个带有序列和 buf_length 字段的结构中。

    这绝对不是最佳解决方案...它只是处理序列号以确保数据完全到达并以正确的顺序。如果没有,请停止接收数据(它可以做一些更聪明的事情,比如要求发送方重新发送片段并重建数据,尽管顺序错误,但它会变得非常复杂)

    struct udp_pkt_s {
        int     seq;
        size_t  buf_len;
        char    buf[BUFSIZE];
    };
    

    发送方:

    struct udp_pkt_s udp_pkt;
    
    udp_pkt.seq = 0;
    
    while(writeSize < bufferSize)
    {
        // switch to the next package block
        // ...
    
        memcpy(udp_pkt.buf, package);
        udp_pkt.buf_len = sizeof(package);
        udp_pkt.seq++;
    
        bytes_sent += sendto(servSocket, &udp_pkt, sizeof(udp_pkt_s), 0, (struct sockaddr *)&server, sizeof(server));
        writeSize += sizeof(package);    
    }
    

    接收方:

    last_seq = 0;
    
    while(packetSize < bufferSize)
    {
        bytes_recv += recvfrom(servSocket, &udp_pkt, sizeof(udp_pkt_s), 0, (struct sockaddr*)&client, &size);
    
        // break it if there's a hole
        if (udp_pkt.seq != (last_seq + 1))
            break;
    
        last_seq = udp_pkt.seq;
    
        packetSize += udp_pkt.buf_len;
    
        printf("Bytes recieved: %d\n", udp_pkt.buf_len);
    }
    

    【讨论】:

      【解决方案4】:

      sendto 后可以等待sendto 缓冲区归零,每次sendto 调用后可以使用以下api。

      void waitForSendBuffer(void)
      {
      int outstanding = 0;
      while(1)
      {
        ioctl(socketFd_, SIOCOUTQ, &outstanding);
        if(outstanding == 0)
          break;
        printf("going for sleep\n");
        usleep(1000);
      }
      }
      

      【讨论】:

        猜你喜欢
        • 2014-11-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-24
        • 2012-12-08
        相关资源
        最近更新 更多