文章目录
- 1. 阻塞模式
- 四种阻塞API调用
- 2. 非阻塞模式
- 3. 优缺点对比
阻塞(blocking)、非阻塞(non-blocking):
Windows套接字在阻塞和非阻塞两种模式下执行I/O操作。在阻塞模式下,在I/O操作完成前,执行的操作函数一直等候而不会立即返回,该函数所在的线程会阻塞在这里。相反,在非阻塞模式下,套接字函数会立即返回,而不管I/O是否完成,该函数所在的线程会继续运行。
1. 阻塞模式
阻塞模式的套接字上,调用任何一个Windows Sockets API都会耗费等待时间。
比如当调用recv()函数时,系统首先查是否有准备好的数据。如果数据没有准备好,那么系统就处于等待状态。当数据准备好后,将数据从系统内核缓冲区复制到用户空间,然后该函数返回。在套接应用程序中,当调用recv()函数时,未必用户空间就已经存在数据,那么此时recv()函数就会处于等待状态。
四种阻塞API调用
- 输入操作
recv()
、recvfrom()
函数。以阻塞套接字为参数调用该函数接收数据。如果此时套接字缓冲区内没有数据可读,则调用线程在数据到来前一直睡眠。
- 输出操作
send()
、sendto()
函数。以阻塞套接字为参数调用该函数发送数据。如果套接字缓冲区没有可用空间,线程会一直睡眠,直到有空间。
- 接收函数
accept()
函数。以阻塞套接字为参数调用该函数,等待接受对方的连接请求。如果此时没有连接请求,线程就会进入睡眠状态。
- 外出连接
connect()
。对于TCP连接,客户端以阻塞套接字为参数,调用该函数向服务器发起连接。该函数在收到服务器的应答前,不会返回。这意味着TCP连接总会等待至少到服务器的一次往返时间。
注意:
并不是所有Windows SocketsAPI
以阻塞套接字为参数调用都会发生阻塞。例如,以阻塞模式的套接字为参数调用bind()
、listen()
函数时,函数会立即返回。
2. 非阻塞模式
把套接字设置为非阻塞模式,即通知系统内核:在调用SocketsAPI
时,应让函数立即返回,并返回一个错误代码。
比如,一个非阻塞模式套接字多次调用recv()
函数的过程。前三次调用recv()
函数时,内核数据还没有准备好。因此,该函数立即返回WSAEWOULDBLOCK
错误代码。第四次调用recv()
函数时,数据已经准备好,被复制到应用程序的缓冲区中,recv()
函数返回成功指示,应用程序开始处理数据。
当使用socket()
函数和WSASocket()
函数创建套接字时默认都是阻塞的。在创建套接字之后,windows
下通过调用ioctlsocket()
函数(Linux下fcntl
),将该套接字设置为非阻塞模式。
套接字设置为非阻塞模式后,在调用Windows Sockets APl
函数时,调用函数会立即返回。大多数情况下,这些函数调用都会调用“失败”,并返回WSAEWOULDBLOCK
错误代码。说明请求的操作在调用期间内没有时间完成。通常,应用程序需要重复调用该函数,直到获得成功返回代码。但特例bind()
函数不会返回该错误代码。
除了使用ioctlsocket()
函数将套接字,设置为非阻塞模式之外,还可以使用WSAAsyncselect()
和WSAEventselect()
函数,当调用该函数时,套接字会自动地设置为非阻塞方式。
由于使用非阻塞套接字在调用函数时,会经常返回WSAEWOULDBLOCK
错误。所以在任何时候,都应仔细检查返回代码并作好对“失败”的准备。应用程序连续不断地调用这个函数,直到它返回成功指示为止。上面的程序清单中,在While循环体内不断地调用recv()
函数,以读入1024个字节的数据。这种做法很浪费系统资源。
3. 优缺点对比