为什么假设send可能返回的数据少于阻塞套接字上传输的请求数据?

在流套接字上发送数据的标准方法一直是调用send和一大块数据进行写入,检查返回值以查看是否所有数据都已发送,然后再次调用send直到整个消息被接受为止。

例如,这是一个常见方案的简单示例:

 int send_all(int sock,unsigned char * buffer,int len){
  不存在

   while(len> 0){
     nsent = send(sock,buffer,len,0);
     if(nsent == -1)//错误
      返回-1;

     buffer + = nsent;
     len  -  = nsent;
   }
  返回0;  //好的,发送了所有数据
 }

甚至BSD手册也提到了这一点

…如果套接字上没有可用的消息空间来保存要传输的消息,则send() 通常会阻塞

这表明我们应该假设send可以在不发送所有数据的情况下返回。 现在我发现这个相当破碎,但即使是W. Richard Stevens在他的标准参考书中假定这是关于网络编程的 ,而不是在开头的章节中,但更高级的例子使用他自己的写(写所有数据)函数而不是调用write。

现在我认为这仍然或多或少地被破坏,因为如果send无法传输所有数据或接受底层缓冲区中的数据并且套接字阻塞,则send应该阻塞并在整个发送请求被接受时返回。

我的意思是,在上面的代码示例中,如果发送返回的数据发送的数量较少,将会发生的情况是,将使用新请求再次调用它。 自上次通话以来有什么变化? 最多几百个CPU周期已经过去,因此缓冲区仍然是满的。 如果发送现在接受数据,为什么它不能接受它?

否则,我们将以低效循环结束upp,我们试图在无法接受数据并继续尝试的套接字上发送数据,否则?

因此,如果需要,似乎解决方法会导致代码效率极低,在这种情况下,应该避免阻塞套接字,而应该使用非阻塞套接字和select。

上面描述中缺少的是,在Unix中,系统调用可能会被信号中断。 这正是阻止send(2)可能返回短计数的原因。

对于涉及设备驱动程序的高效传输机制而言,这是非常重要的要求。 堆的王者是TCP,它确保无论你投掷什么,它都会最好地通过网络获取数据。 它做得很好,它有9个9的交付保证。 它通常需要地震或某人绊倒电源线以达不到这一承诺。

这意味着,当您向套接字写入数据时,您不必担心它将无法传递。 这意味着内核驱动程序可以冒昧地说“我有足够的空间来存储它,带上它!” 并让用户模式程序快速返回更重要的事情,比如保持UI更新。

好事。 如果由于某种原因你的交付保证需要比那个更强,可能是12个9,那么你不应该使用TCP。 它已经完成,“可靠的UDP”谷歌很好。 虽然它往往会一次又一次地重新发明。 祝好运!

从本质上讲,这种行为只会增加灵活性。

考虑如果内部发送缓冲区有另外300个字节的空间会发生什么,并且您要求send()发送800个字节。

往往会发生的是它接受前300个字节并将它们放入套接字缓冲区,然后返回。 如果你真的想要阻塞直到所有800字节都消失了,那么你只需要为最后500字节重新调用send() ,它将阻塞(因为发送缓冲区现在已满)。 另一方面,如果您现在想要做其他事情,并尝试稍后发送剩余的500个字节,您也可以这样做。