我正在使用新设计的协议实现一台服务器。根据协议,客户端发送标头后跟数据。标题包含元信息,包括数据大小。
我们提供样本客户端,但客户端程序也可以由第三方编写。因此,我们不能完全依赖标题中提供的数据字段。
现在,我遇到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):
recv()
只能读取65280个字节。即第16行的fprintf打印65280.(我在我的机器上检查了SSIZE_MAX,它是2147483647所以它远远大于SOCKET_CHUNK_SIZE)
我尝试在recv()
调用中使用MSG_DONTWAIT标志,但结果相同。
我尝试使用read()
系统调用代替recv()
,结果相同。
当我取消注释第6行时,它完美运行!! (但是这一行(和第16行)仅用于调试目的。我无法将其保留在最终版本中)
如果我在recv()
中使用MSG_WAITALL标志,它可以工作,但在读取最后一个块时阻塞,因为最后一个块大小小于SOCKET_CHUNK_SIZE(604697 = 147 * 4096 + 2585)。所以,我不能使用此标志,除非我依赖客户端标头中提供的 size 并在recv()
中进行更改。
客户端提供的数据也可以是二进制数据,因此我们不能将某种指示作为数据末尾。
欢迎任何有想法/解决方案的人。正如我所提到的,我们有解决方案 - 依赖客户端标题 - 但只有在我找不到任何其他方式时我才会喜欢它。
拉维
答案 0 :(得分:2)
几乎所有的观察结果都是完全可以解释的:
因此,从我的角度来看,最好的方法是在没有MSG_WAITALL标志的情况下使用recv并接受小于SOCKET_CHUNK_SIZE的接收块。