非阻塞I/O 複用

By default, sockets are blocking. This means that when we issue a socket call that cannot be completed immediately, our process is put to sleep, waiting for the condition to be true. We can divide the socket calls that may block into four categories:ios

  1. Input operations— These include the read, readv, recv, recvfrom, and recvmsg functions. If we call one of these input functions for a blocking TCP socket (the default), and there is no data available in the socket receive buffer, we are put to sleep until some data arrives. Since TCP is a byte stream, we will be awakened when "some" data arrives: It could be a single byte of data, or it could be a full TCP segment of data. If we want to wait until some fixed amount of data is available, we can call our own function readn (Figure 3.15) or specify the MSG_WAITALL flag (Figure 14.6).緩存

    Since UDP is a datagram protocol, if the socket receive buffer is empty for a blocking UDP socket, we are put to sleep until a UDP datagram arrives.服務器

    With a nonblocking socket, if the input operation cannot be satisfied (at least one byte of data for a TCP socket or a complete datagram for a UDP socket), we see an immediate return with an error of EWOULDBLOCK.app

  2. Output operations— These include the write, writev, send, sendto, and sendmsg functions. For a TCP socket, we said in Section 2.11 that the kernel copies data from the application's buffer into the socket send buffer. If there is no room in the socket send buffer for a blocking socket, the process is put to sleep until there is room.socket

    With a nonblocking TCP socket, if there is no room at all in the socket send buffer, we return immediately with an error of EWOULDBLOCK. If there is some room in the socket send buffer, the return value will be the number of bytes the kernel was able to copy into the buffer. (This is called a short count.)ide

    We also said in Section 2.11 that there is no actual UDP socket send buffer. The kernel just copies the application data and moves it down the stack, prepending the UDP and IP headers. Therefore, an output operation on a blocking UDP socket (the default) will not block for the same reason as a TCP socket, but it is possible for output operations to block on some systems due to the buffering and flow control that happens within the networking code in the kernel.函數

  3. Accepting incoming connections— This is the accept function. If accept is called for a blocking socket and a new connection is not available, the process is put to sleep.this

    If accept is called for a nonblocking socket and a new connection is not available, the error EWOULDBLOCK is returned instead.spa

  4. Initiating outgoing connections— This is the connect function for TCP. (Recall that connect can be used with UDP, but it does not cause a "real" connection to be established; it just causes the kernel to store the peer's IP address and port number.) We showed in Section 2.6 that the establishment of a TCP connection involves a three-way handshake and the connect function does not return until the client receives the ACK of its SYN. This means that a TCP connect always blocks the calling process for at least the RTT to the server.線程

    If connect is called for a nonblocking TCP socket and the connection cannot be established immediately, the connection establishment is initiated (e.g., the first packet of TCP's three-way handshake is sent), but the error EINPROGRESS is returned. Notice that this error differs from the error returned in the previous three scenarios. Also notice that some connections can be established immediately, normally when the server is on the same host as the client. So, even with a nonblocking connect, we must be prepared for connect to return successfully. We will show an example of a nonblocking connect in Section 16.3.

     

    Traditionally, System V has returned the error EAGAIN for a nonblocking I/O operation that cannot be satisfied, while Berkeley-derived implementations have returned the error EWOULDBLOCK. Because of this history, the POSIX specification says either may be returned for this case. Fortunately, most current systems define these two error codes to be the same (check your system's <sys/errno.h> header), so it doesn't matter which one we use. In this text, we use EWOULDBLOCK.

     

Section 6.2 summarized the different models available for I/O and compared nonblocking I/O to other models. In this chapter, we will provide examples of all four types of operations and develop a new type of client, similar to a Web client, that initiates multiple TCP connections at the same time using a nonblocking connect.

 

socket默認是阻塞的。若是咱們對一個socket的操做不能當即返回,當前的進程就會被掛起,等待操做完成。

咱們能夠把socket操做分爲4類:

1. 輸入操做:函數read, readv, recv, recvfrom,和recvmsg。

  對一個阻塞的TCP套接字調用這幾個函數中的任意一個,若是套接字的接收緩存沒有任何數據,當前線程將被掛起,直到有新的數據到達緩存。

   對於非阻塞套接字,若是TCP套接字的接收緩存中沒有數據或者UDP套接字的緩存中沒有一個完整的數據報,輸入操做函數將會當即返回錯誤碼EWOULDBLOCK。

 

2. 輸出操做:函數write, writev, send, sendtosendmsg。

  對於TCP套接字,內核將應用層緩存中的數據拷貝到套接字的發送緩存中。若是阻塞套接字的發送緩存中沒有剩餘空間,線程將被掛起,直到發送緩衝區有空閒。

   對非阻塞的TCP套接字,若是發送緩衝區沒有空閒,輸出函數當即返回錯誤碼EWOULDBLOCK。若是發送緩衝區有少許空閒(小於須要發送的長度),函數返回值是內核從應用層緩存拷貝到套接字發送緩存的字節數。

   UDP套接字沒有發送緩存,內核只是將數據拷貝到協議棧,追加上UDP和IP頭,所以阻塞UDP套接字並不會由於發送緩存不足而被阻塞,可是輸出操做函數可能由於某些系統的緩存或者內核代碼流程被阻塞。

 

3. 接收入向鏈接accpet函數。

    若是對一個阻塞套接字調用accept函數,在沒有新的鏈接請求時,線程將被掛起。

    若是對一個非阻塞套接字調用accept函數,在沒有新的鏈接請求時,函數將返回EWOULDBLOCK。

 

4. 發起外向鏈接:TCP的connect函數。

   阻塞的TCP鏈接的創建須要進行「三次握手」,connect在收到服務器端的SYN ACK以前不會返回(見上圖)。

   非阻塞的TCP鏈接,若是鏈接沒有當即創建,會返回錯誤碼EINPROGRESS(這個錯誤碼與前面三種場景返回的錯誤碼不一樣)。另外,無關服務器和客戶端在同一個機器,鏈接會立刻創建,所以,對非阻塞TCP鏈接,

   須要處理函數返回時鏈接創建的場景。

 

一般,System V的非阻塞IO函數返回錯誤碼EAGAIN;Berkeley-derived 系統返回錯誤碼EWOULDBLOCK。

幸運的時,現代的系統把兩個錯誤碼定義爲同樣的。

相關文章
相關標籤/搜索