我试图在高水平上理解recv()。recv以块为单位接收数据;但我还是不明白它到底是怎么处理的。例子:
char buffer[1000];
int received= recv(sock, buffer, sizeof(buffer), 0)
这是否意味着如果我收到大量文件,缓冲区,如果通过袜子可能例如反映连接存储500字节接收的变量,然后在一个循环中得到另一个300字节,和所有800字节的数据将存储在缓冲区的循环(迷失在接收到的变量,除非占了),还是缓冲需要一个指针来跟踪它最后收到的数据存储在下一代?
recv
没有上下文。它只知道它有一些地址(指针)要写入和一些最大大小-然后它会尝试这个。它总是以给定的地址开始书写。例如,如果希望在先前接收到的数据之后添加数据,可以简单地将指针指向先前数据之后的位置,而不是缓冲区的开始。当然,应该调整允许读取的最大大小,以防止缓冲区溢出。
您问"recv()是如何工作的?",因此有必要简要地研究一下一个更简单的函数,它基本上做同样的事情——read()
。
recv()
的作用方式与read()
的作用方式大致相同。主要的区别是recv()
允许你在最后一个参数中传递标志——但是你并没有使用这些标志。
我的建议是-在尝试使用recv()
从网络套接字读取之前-在纯文本文件上练习使用read()
。
两个函数都返回读取的字节数——除非发生错误,在这种情况下它们将返回-1。您应该经常检查这种情况,并适当地处理。
两个函数也可以返回小于请求的字节数。在recv()
的情况下——从套接字读取——这可能是因为另一端还没有发送所有需要的数据。在使用read()
从文件中读取的情况下,可能是因为您已经到达了文件的末尾。
无论如何……
您将需要跟踪当前偏移在您的缓冲区-并在每次读取时更新它。因此声明一个文件作用域变量offset
。
static off_t offset; static char buffer[1000];
然后-当你的'循环'正在运行-增加每次读取后的偏移量…
while (1) {
size_t max_len = sizeof(buffer) - offset;
ssize_t count = recv(sock, buffer+offset, max_len, 0);
if (count == -1) {
switch (errno) {
case EAGAIN:
usleep(20000);
break;
default:
perror("Failed to read from socket");
close(sock);
break;
}
}
if (count == 0) {
puts("Looks like connection has been closed.");
break;
}
offset += count;
if (offset >= expected_len) {
puts("Got the expected amount of data. Wrapping up ...");
}
}
指出:
- 使用这种方法,您需要事先知道预期的数据量,或者使用特殊的分隔符来标记消息 的结束。
max_len
变量指示缓冲区中剩余的空间-并且(也许不用说)您不应该尝试读取比此 更多的字节recv()
命令的目标是buffer+offset
-不是buffer
。- 如果
recv()
返回0,AFAIK这表示另一端已经执行了"有序关机"。 - 如果
recv()
返回-1,您确实需要检查返回代码。EAGAIN
是非致命的-只是意味着你需要再试一次。