【问题标题】:C: Last Bytes of UDP packets lostC: UDP 数据包的最后一个字节丢失
【发布时间】:2014-11-26 08:49:40
【问题描述】:

我正在编写一个基于 UDP 的流媒体服务器,遇到了一个奇怪的问题,我确定这只是一个简单的错误,但我找不到解决方案。服务器执行以下操作:

FILE* infile = fopen(inf, "rb");
register int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
int16_t buffer[LENGTH+HEADER];
struct header header;
int16_t data[LENGTH];
uint32_t number = 0;

/*set socket options etc. */

while(!feof(infile)){
    fread(data, 1, LENGTH, infile);
    /* if i write the contents of data to a file here, the error below occurs*/
    fwrite(data, 1, LENGTH, testfile);
    /*create a header, encode everything with htons/htonl*/


    if(sendto(sock, buffer, LENGTH+HEADER, 0, (struct sockaddr*) &to,
                    sizeof to) < 0)
         /*die*/            
}

这似乎有效。我可以解码它并且数据没有损坏。但是,我编写了一个可以工作(或者更确切地说:不工作)的测试客户端,如下所示:

struct sockaddr_in si_other, si_me;
register int s;
unsigned int slen = sizeof(si_other);
int16_t buf[LENGTH+HEADER];
int16_t data[LENGTH];
FILE* file = fopen(of, "wb");

/*open socket, set options, etc. */

while(1){
    if(recvfrom(s, (char *)buf, LENGTH+HEADER, 0, (struct sockaddr*) &si_other,
                 &slen) < 0)
        /*die*/    
    decode(buf, data);
    /* If I write the decoded data to a file here, the error below occurs */
    fwrite(data, 1, LENGTH, testfile);
    if(is_empty(data) == 0){
        printf("End signal received.\n");
        break;
    }
} 

现在我的问题。当我使用diff &lt;(xxd test_before) &lt;(xxd test_after) 检查十六进制的测试文件时,我得到了这个(示例差异行):

< 03d5ff0: f3fd f3fd 99fe 99fe 40ff 40ff e7ff e7ff  ........@.@.....
---
> 03d5ff0: f3fd f3fd 0000 0000 0000 0000 0000 0000  ................

这意味着数据包数据的最后 12 个字节丢失了。其他一切都很好。

这只是测试代码,所以它并不重要(我猜),但它很奇怪,我想知道为什么。

有什么想法吗?

编辑:

我现在已经从答案中尝试了一些方法,到目前为止还没有得到任何结果。我会继续努力。

编辑 2:

代码在不同的机器上工作。我不确定问题是什么,但它似乎对除我以外的任何人都有效。很抱歉占用您的时间,感谢您对如何改进代码等提出的所有友好建议!

提前致谢, 卡森

【问题讨论】:

  • 如果你想发送/接收 LENGTH+HEADER 数量的 int16_t,你必须通过 (LENGTH+HEADER)*sizeof(int16_t) 到 sendto/recvfrom。 (或者在这种特殊情况下只是sizeof buffer
  • 顺便说一句:不要这样做:while(!feof(infile)){ feof() 的工作方式与您的想法不同;它在读取操作失败后返回非零
  • 函数 encode() 最有可能改变数据的长度,尤其是当数据中有一个密钥后。如您的代码中所写, sendto() 和 recvfrom() 函数并未考虑此字节数变化。当它应该是 2*u16 计数时,您的代码给出了 u16 计数。

标签: c sockets stream udp recvfrom


【解决方案1】:

您正在将 2 个字节 buf 转换为 char 并读取其中包含的一半。

recvfrom(s, (char *)buf, LENGTH+HEADER, 0, (struct sockaddr*) &si_other,
             &slen) 

buf 数组有 (LENGTH+HEADER) * 2 字节,你读取了一半

sendreceive 都使用 char 数组。

编辑

这是读取文件的方式:

unsigned char data[LENGTH];

fseek(inf, 0, SEEK_END);
int file_Size = ftell(inf);
fseek(inf, 0, SEEK_SET);
if(file_Size  > LENGTH){
    //file larger than data
}
fread(data, sizeof(unsigned char), file_Size, inf);

【讨论】:

  • 抱歉,该代码用于调试目的。我将其删除并再次测试(也尝试了您的建议)。它不会改变任何东西。数据包长 536 个字节,因此并不能真正解释最后 12 个字节的丢失。
  • @Carson 使用 fread 时首先检查文件的长度,然后在没有 while 循环的情况下全部读取。这就是 fread 的目的。发送前检查您是否阅读了整个文件。
  • 另一方面并没有解释为什么字节被发送,但没有被接收。
【解决方案2】:

您的代码非常不完整,因此很难确定。发送端int16_t buffer[LENGTH+HEADER]中的数据是怎么放的? decode 和 encode 函数是什么样的?

我可以看到一个明显的危险是您的缓冲区是uint16_t 类型,即两个字节。函数sendto()recvfrom() 期望缓冲区长度以字节为单位。换句话说,您的 buf 是您实际告诉sendto() 的长度(以字节为单位)的两倍。因此,您的编码功能可能会使一些数据从发送/接收的视角消失。

大胆猜测,缺少 12 个字节可能是一个合适的 4 字节值或其他数据 htonl() 已处理然后在发送中丢失。

发布更完整的代码,以便更好地猜测。

【讨论】:

    【解决方案3】:

    如编辑 2 中所述,该代码适用于我测试代码的所有机器,但我的机器除外。我不确定问题是什么,但它似乎对我以外的任何人都有效。很抱歉占用您的时间,感谢您对如何改进代码等提出的所有友好建议!

    【讨论】:

    • 你的机器有什么处理器?其他机器是否有相同的 CPU 架构?
    • 他们应该。它的两个英特尔芯片。
    猜你喜欢
    • 1970-01-01
    • 2014-11-11
    • 1970-01-01
    • 2012-01-06
    • 2015-06-30
    • 2019-06-05
    • 2023-03-30
    • 2011-06-02
    • 2018-06-22
    相关资源
    最近更新 更多