我正在使用新设计的协议实现一台服务器。根据协议,客户端发送标头,后跟数据。标头包含元信息,包括数据大小。
我们提供示例客户端,但客户端程序也可以由第三方编写。因此,我们不能完全依赖标头中提供的数据字段的大小。
现在,我面临一个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 大得多)我尝试在调用中使用 MSG_DONTWAIT 标志
recv()
,但结果相同。我尝试使用
read()
系统调用代替recv()
,结果是一样的。当我取消注释第 6 行时,它工作得很好!!(但这一行(和第 16 行)仅用于调试目的。我不能将其保留在最终版本中)
如果我在 中使用 MSG_WAITALL 标志
recv()
,它可以工作,但是在读取最后一个块时阻塞,因为最后一个块大小小于 SOCKET_CHUNK_SIZE (604697 = 147 * 4096 + 2585)。因此,除非我依赖于客户端标头中提供的大小并在recv()
.
客户端提供的数据也可以是二进制的,因此我们不能将某种指示作为数据结束。
欢迎任何有想法/解决方案的人。正如我所提到的,我们有解决方案 - 依赖于客户端标头 - 但只有当我找不到任何其他方法时,我才会更喜欢它。
拉维