使用socket编程实现数据传输的过程中,通常的默认设置假设套接字是阻塞的。每个TCP套接字有一个发送缓冲区,当应用进程调用write操作的时候,内核从应用进程的缓冲区中复制数据到套接字的发送缓冲区中。
如果套接字的发送缓冲区无法完全的容纳应用的应用程序所发送的数据,即应用程序的缓冲区大于套接字发送缓冲区或套接字缓冲区还有其他数据,应用进程将会被挂起并。内核将不从write系统条用返回,直到应用进程缓冲区中的所有数据都复制到套接字发送缓冲区。
所以,从写一个tcp套接字的write调用成功返回仅仅表示可以重新使用应用进程的缓冲区,并不表示对端的应用进程已经接收到了数据。
下面是发送和接收两个方面说明“阻塞”和“非阻塞”模式的区别:
(1)发送操作:write 、writev、send、sendto、sendmsg
对于tcp套接字,内核将从应用进程的缓冲区向该套接字的的发送缓冲区复制数据。对于阻塞的套接字,如果其发送缓冲区中没有空间,进程将挂起,直到有空间为止。
对于一个非阻塞的tcp套接字,如果其发送缓冲区中根本就没有空间,发送函数调用将立即返回一个EWOULDBLOCK错误。如果其缓冲区中有一些空间,返回值将是内核能够复制到该缓冲区中的字节数。
对于UDP套接字,不存在真正的发送缓冲区。内核只是复制应用进程数据并把它沿协议栈向下传送,一次加上UDP头部和IP头部。因此,对一个阻塞的UDP套接字,发送函数调用将不会因为与TCP套接字一样的原因而阻塞,不过有可能会因为其他原因而阻塞。
(2)接受操作 read readv recv recvfrom recvmsg
如果某个进程对一个阻塞的TCP套接字调用这些输入函数之一,而该套接字的接受缓冲区没有数据可以读,该进程将被挂起,直到到达一些数据。tcp是字节流协议,只要到达一些数据,该进程就会被唤醒,这些数据既可能是单个字节,也可以是一个完整的TCP分节中的数据。
UDP是数据包协议,如果一个阻塞的UDP套接字的接受缓冲区为空,对它调用接收函数的进程将被挂起,直到到达一个UDP数据报。
对于非阻塞的套接字,如果接收操作不能被满足(对于UDP套接字即有一个完整的数据报可读),相应的调用将立即返回一个EWOULDBLOCK错误。