在流套接字上发送数据的标准方法始终是调用 send 并写入一大块数据,检查返回值以查看是否发送了所有数据,然后再次调用 send 直到整个消息被接受。
例如,这是一个常见方案的简单示例:
int send_all(int sock, unsigned char *buffer, int len) {
int nsent;
while(len > 0) {
nsent = send(sock, buffer, len, 0);
if(nsent == -1) // error
return -1;
buffer += nsent;
len -= nsent;
}
return 0; // ok, all data sent
}
甚至 BSD 手册页也提到了这一点
...如果套接字上没有可用的消息空间来保存要传输的消息,则 send()通常会阻塞...
这表明我们应该假设 send 可能会返回而不发送所有数据。现在我发现这相当糟糕,但即使是 W. Richard Stevens 在他的标准参考书中也假设了这一点网络编程 http://www.kohala.com/start/unpv12e.html,不是在开始的章节中,而是更高级的例子使用他自己的writen(写入所有数据)函数而不是调用write。
现在我认为这仍然或多或少被破坏了,因为如果 send 无法传输所有数据或接受底层缓冲区中的数据并且套接字正在阻塞,那么 send 应该在整个发送请求被接受时阻塞并返回。
我的意思是,在上面的代码示例中,如果 send 返回时发送的数据较少,会发生什么情况,它将被新请求再次调用。自上次通话以来发生了什么变化?最多已经过去了几百个 CPU 周期,因此缓冲区仍然是满的。如果现在发送接受数据为什么之前不能接受?
否则,我们最终会陷入一个低效的循环,我们试图在无法接受数据并继续尝试的套接字上发送数据,否则呢?
因此,如果需要的话,解决方法似乎会导致代码效率严重低下,在这种情况下,应该完全避免阻塞套接字,而应该使用非阻塞套接字和 select 来代替。