recv()表示大量数据不起作用

时间:2013-07-15 05:11:41

标签: c sockets network-programming recv

我正在使用新设计的协议实现一台服务器。根据协议,客户端发送标头后跟数据。标题包含元信息,包括数据大小

我们提供样本客户端,但客户端程序也可以由第三方编写。因此,我们不能完全依赖标题中提供的数据字段。

现在,我遇到recv()系统调用问题。

      #define SOCKET_CHUNK_SIZE 4096
      void * value;

 1    value = (void *) malloc(hdr.size);
 2    total_bytes_read = 0;
 3    while(total_bytes_read < hdr.size) {
 4        n = recv(newsockfd, value + total_bytes_read, SOCKET_CHUNK_SIZE, 0);
 5
 6        //fprintf(stderr, " %ld + %d = %ld\n", total_bytes_read, n, total_bytes_read + n);
 7
 8        total_bytes_read += n;
 9
10        if(n == 0 || n < SOCKET_CHUNK_SIZE)
11            break;
12        if(n < 0)
13            send_error_response(newsockfd);
14    }
15
16    fprintf(stderr, "%ld", total_bytes_read);

这适用于少量数据(如9420字节),但失败的数量较大。

观察:

让客户端发送大量数据,如604697字节(hdr.size):

  1. recv()只能读取65280个字节。即第16行的fprintf打印65280.(我在我的机器上检查了SSIZE_MAX,它是2147483647所以它远远大于SOCKET_CHUNK_SIZE)

  2. 我尝试在recv()调用中使用MSG_DONTWAIT标志,但结果相同。

  3. 我尝试使用read()系统调用代替recv(),结果相同。

  4. 当我取消注释第6行时,它完美运行!! (但是这一行(和第16行)仅用于调试目的。我无法将其保留在最终版本中)

  5. 如果我在recv()中使用MSG_WAITALL标志,它可以工作,但在读取最后一个块时阻塞,因为最后一个块大小小于SOCKET_CHUNK_SIZE(604697 = 147 * 4096 + 2585)。所以,我不能使用此标志,除非我依赖客户端标头中提供的 size 并在recv()中进行更改。

  6. 客户端提供的数据也可以是二进制数据,因此我们不能将某种指示作为数据末尾。

    欢迎任何有想法/解决方案的人。正如我所提到的,我们有解决方案 - 依赖客户端标题 - 但只有在我找不到任何其他方式时我才会喜欢它。

    拉​​维

1 个答案:

答案 0 :(得分:2)

几乎所有的观察结果都是完全可以解释的:

  1. 我想跳过那个,因为我不确定我是否正确,因为我现在认为这是次要的错误。
  2. recv不保证填充整个缓冲区(除非您设置MSG_WAITALL)。它在收到一些字节后返回。因此,如果未设置MSG_WAITALL,则第10行中条件的第二部分将阻止您接收更多数据。设置MSG_WAITALL使得recv仅在填充整个缓冲区后返回(在您的情况下为SOCKET_CHUNK_SIZE)。由于您的有效负载的大小并不总是SOCKET_CHUNK_SIZE的倍数,因此您的上一次recv调用将挂起,直到连接终止。
  3. 这是由于上面提到的10号线的情况。
  4. 坚持使用recv,这是做到这一点的正确方法
  5. 我想取消注释第6行会改变执行时间,即偶然比SOCKET_CHUNK_SIZE更多的数据到达套接字。
  6. 因此,从我的角度来看,最好的方法是在没有MSG_WAITALL标志的情况下使用recv并接受小于SOCKET_CHUNK_SIZE的接收块。