代码之家  ›  专栏  ›  技术社区  ›  Ernelli

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

  •  20
  • Ernelli  · 技术社区  · 14 年前

    在流套接字上发送数据的标准方法始终是调用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。理查德·史蒂文斯在他的标准参考书中假设了这一点 network programming ,但更高级的示例使用他自己的writen(write all data)函数,而不是调用write。

    现在,我认为这仍然或多或少是不可靠的,因为如果send不能传输所有数据或接受底层缓冲区中的数据,并且套接字阻塞,那么send应该在整个send请求被接受时阻塞并返回。

    我的意思是,在上面的代码示例中,如果send返回的数据较少,那么会发生什么呢?它将被再次调用,并发出一个新的请求。上次通话后有什么变化?在最大几百个CPU周期已经过去,所以缓冲区仍然是满的。如果send现在接受数据,为什么以前不能接受呢?

    否则,我们将以一个低效的循环结束upp,在这个循环中,我们试图在一个不能接受数据的套接字上发送数据,然后继续尝试,否则呢?

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

    3 回复  |  直到 14 年前
        1
  •  22
  •   Nikolai Fetissov    13 年前

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