5

我想发送一个字节数组 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循环不会结束,有什么想法可以解决这个问题吗?

非常感谢

4

4 回答 4

2

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

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

干得好。我们再次开始构建 TCP ......

于 2013-01-18T16:54:44.087 回答
2

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

于 2013-01-18T12:37:21.023 回答
0

在 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-02-19T08:50:08.197 回答
0

当然 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);
}
于 2013-01-18T15:30:01.043 回答