Please indicate the source: http://blog.youkuaiyun.com/gaoxiangnumber1
Welcome to my github: https://github.com/gaoxiangnumber1
16.1 Introduction
- By default, sockets are blocking. 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:
- Input operations. These include 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 a single byte of data, or a full TCP segment of data arrives. If we want to wait until some fixed amount of data is available, we can call 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 nonblocking socket, if the input operation cannot be satisfied(at least one byte of data for TCP socket or a complete datagram for UDP socket), we see an immediate return with an error of EWOULDBLOCK.
- Output operations. These include write, writev, send, sendto, and sendmsg functions.
- For a TCP socket,(Section 2.11)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.
- 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.)
- There is no actual UDP socket send buffer(Section 2.11). The kernel 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.
- Accepting incoming connections(accept function).
- If accept is called for a blocking socket and a new connection is not available, the process is put to sleep.
- If accept is called for a nonblocking socket and a new connection is not available, the error EWOULDBLOCK is returned instead.
- Initiating outgoing connections(connect function for TCP). 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.
- Section 2.6: 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. So, a TCP connect always blocks the calling process for at least one 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. Connections that can be established immediately are 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.
16.2 Nonblocking Reads and Writes: ‘str_cli’ Function(Revisited)
- str_cli function in Sections 6.4, which uses select, still uses blocking I/O. For example, if a line is available on standard input, we read it with read and then send it to the server with writen. But the call to writen can block if the socket send buffer is full. While we are blocked in the call to writen, data could be available for reading from the socket receive buffer. Similarly, if a line of input is available from the socket, we can block in the subsequent call to write, if standard output is slower than the network.
- We maintain two buffers: “to” contains data going from standard input to the server, “fr” contains data arriving from the server going to standard output. Figure 16.1 shows the arrangement of the to buffer and the pointers into the buffer.
- tooptr points to the next byte that must be written to the socket.
toiptr points to the next byte into which data can be read from standard input. - There are(toiptr - tooptr) bytes to be written to the socket. The number of bytes that can be read from standard input is(&to [MAXLINE] - toiptr). As soon as tooptr reaches toiptr, both pointers are reset to the beginning of the buffer.
- Figure 16.2 shows the corresponding arrangement of the fr buffer.
- Figure 16.3 shows the first part of the function.
Set descriptors to nonblocking 10-15
- All three descriptors are set to nonblocking using fcntl: the socket to and from the server, standard input, and standard output.
Initialize buffer pointers 16-19
- The pointers into the two buffers are initialized and the maximum descriptor plus one is calculated, which will be used as the first argument for select.
Main loop: prepare to call select 20
- The main loop is a call to select followed by individual tests of the various conditions we are interested in.
Specify descriptors we are interested in 21-30
- Both descriptor sets are set to 0 and then 2 bits are turned on in each set.
- If we have not read an EOF on standard input, and there is room for at least one byte of data in the to buffer, STDIN_FILENO is turned on in the read set.
- If there is room for at least one byte of data in the fr buffer, sockfd is turned on in the read set.
- If there is data to write to the socket in the to buffer, sockfd is turned on in the write set.
- If there is data in the fr buffer to send to standard output, STDOUT_FILENO is turned on in the write set.
Call select 31
- select is called, waiting for any one of the four possible conditions to be true. We do not specify a timeout for this function.
read from standard input 32-33
- If standard input is readable, we call read. The third argument is the amount of available space in the to buffer.
Handle nonblocking error 34-35
- If an error occurs and it is EWOULDBLOCK, nothing happens.
read returns EOF 36-40
- If read returns 0, we are finished with the standard input. stdineof is set.
- If there is no more data in the to buffer to send(tooptr = toiptr), shutdown sends a FIN to the server.
- If there is still data in the to buffer to send, FIN cannot be sent until the buffer is written to the socket.
read returns data 41-45
- When read returns data, we increment toiptr accordingly. We turn on the bit corresponding to the socket in the write set, to cause the test for this bit to be true later in the loop, causing a write to be attempted to the socket.
- We have a few alternatives here.
- Do nothing because select will test for writability of the socket the next time it is called. But this requires another loop around and another call to select when we already know that we have data to write to the socket.
- Duplicate the code that writes to the socket here, but wasteful.
- Create a function that writes to the socket and call that function instead of duplicating the code, but that function needs to share three of the local variables with str_cli, which would necessitate making these variables global.
read from socket 48-64
- If we encounter an EOF from the server, this is okay if we have encountered an EOF on the standard input, but it is not expected otherwise.
- If read returns data, friptr is incremented and the bit for standard output is turned on in the write descriptor set, to try to write the data in the next part of the function.
write to standard output 65-68
- If standard output is writable and the number of bytes to write is greater than 0, write is called.
write OK 69-75
- If the write is successful, froptr is incremented by the number of bytes written. If the output pointer has caught up with the input pointer, both pointers are reset to point to the beginning of the buffer.
write to socket 77-91
- When the output pointer catches up with the input pointer, not only are both pointers reset to the beginning of the buffer, but if we encountered an EOF on standard input, the FIN can be sent to the server.
- gf_time(Figure 16.6) function returns a string containing the current time, including microseconds, in the following format: 12:34:56.123456
- All the calls to fprintf in str_cli write to standard error, allowing us to separate standard output(lines echoed by server) from our diagnostic output. We can then run our client and tcpdump and take this diagnostic output along with the tcpdump output and sort the two outputs together, ordered by the time. This lets us see what happens in our program and correlate it with the corresponding TCP action.
- In this figure, we do not show the ACK segments. Note when the program outputs “wrote N bytes to stdout”, the write has returned, possibly causing TCP to send one or more segments of data.
- We can see from this timeline the dynamics of a client/server exchange. Using nonblocking I/O lets the program take advantage of these dynamics, reading or writing when the operation can take place. We let the kernel tell us when an I/O operation can occur by using the select function.
- We can time our nonblocking version using the same 2,000-line file and the same server(a 175-ms RTT from the client) in Section 6.7. The clock time is now 6.9 seconds, compared to 12.3 seconds for the version in Section 6.7. Nonblocking I/O reduces the overall time for this example that sends a file to the server.
A Simpler Version of str_cli
- Whenever we find the need to use nonblocking I/O, it will be simpler to split the application into either processes(using fork) or threads(Chapter 26).
- Figure 16.10: str_cli immediately calls fork to split into a parent and child. The child copies lines from the server to standard output and the parent copies lines from standard input to the server, as shown in Figure 16.9.
- Note that the TCP connection is full-duplex and the parent and child are sharing the same socket descriptor(parent writes to the socket and child reads from the socket). There is only one socket, one socket receive buffer, and one socket send buffer, but this socket is referenced by two descriptors(one in the parent and one in the child).
- We need to worry about the termination sequence. Normal termination occurs when the EOF on standard input is encountered. The parent reads this EOF and calls shutdown to send a FIN.(The parent cannot call close. See Exercise 16.1.) But when this happens, the child needs to continue copying from the server to the standard output, until it reads an EOF on the socket.
- It is also possible for the server process to terminate prematurely(Section 5.12); if this occurs, the child will read an EOF on the socket. If this happens, the child must tell the parent to stop copying from the standard input to the socket(see Exercise 16.2). In Figure 16.10, the child sends the SIGTERM signal to the parent, in case the parent is still running(see Exercise 16.3). Another way to handle this would be for the child to terminate and have the parent catch SIGCHLD, if the parent is still running.
- The parent calls pause when it has finished copying, which puts it to sleep until a signal is caught. Even though our parent does not catch any signals, this puts the parent to sleep until it receives the SIGTERM signal from the child. The default action of this signal is to terminate the process, which is fine for this example.
- The reason we make the parent wait for the child is to measure clock time for str_cli. Normally, the child finishes after the parent, but since we measure the clock time using the shell’s time command, the measurement ends when the parent terminates.
- Comparison: Nonblocking version managed 4 different I/O streams at the same time, and since all four were nonblocking, we had to concern ourselves with partial reads and writes for all four streams. In the fork version, each process handles only two I/O streams, copying from one to the other. There is no need for nonblocking I/O because if there is no data to read from the input stream, there is nothing to write to the corresponding output stream.
Timing of str_cli
- We summarize the clock time required for these versions, along with a version using threads(Figure 26.2), when copying 2,000 lines from a Solaris client to a server with an RTT of 175 ms:
354.0 sec, stop-and-wait(Figure 5.5)
12.3 sec, select and blocking I/O(Figure 6.13)
6.9 sec, nonblocking I/O(Figure 16.3)
8.7 sec, fork(Figure 16.10)
8.5 sec, threaded version(Figure 26.2)
16.3 Nonblocking ‘connect’
- When a TCP socket is set to nonblocking and then connect is called, connect returns immediately with an error of EINPROGRESS but the TCP three-way handshake continues. We then check for a successful/unsuccessful completion of the connection’s establishment using select. There are three uses for a nonblocking connect:
- We can overlap other processing with the three-way handshake.
A connect takes one RTT to complete(Section 2.6) and this can be range from a few milliseconds on a LAN to a few seconds on a WAN. There might be other processing we wish to perform during this time. - We can establish multiple connections at the same time using this technique.
This is popular with Web browsers(Section 16.5). - We can specify a time limit for select to shorten the timeout for connect.
Section 14.2 talks about other ways to place timeouts on socket operations.
- We can overlap other processing with the three-way handshake.
- Even though the socket is nonblocking, if the server to which we are connecting is on the same host, the connection is normally established immediately when we call connect. We must handle this scenario.
- Berkeley-derived implementations have the following two rules regarding select and nonblocking connects:
- When the connection completes successfully, the descriptor becomes writable.
- When the connection establishment encounters an error, the descriptor becomes both readable and writable.
- These two rules regarding select fall out from rules in Section 6.3 about the conditions that make a descriptor ready. A TCP socket is writable if there is available space in the send buffer(which will always be the case for a connecting socket since we have not yet written anything to the socket) and the socket is connected(which occurs only when the three-way handshake completes). A pending error causes a socket to be both readable and writable.
16.4 Nonblocking ‘connect:’ Daytime Client
- Figure 16.11: connect_nonb performs a nonblocking connect. We replace the call to connect in Figure 1.5 with
if(connect_nonb(sockfd, (SA *)&servaddr, sizeof(servaddr), 0) < 0)
err_sys("connect error");
- The first three arguments are the normal arguments to connect, and the fourth argument is the number of seconds to wait for the connection to complete. 0 implies no timeout on the select and the kernel will use its normal TCP connection establishment timeout.
Set socket nonblocking 9-14
- We call fcntl to set the socket to nonblocking.
- We initiate the nonblocking connect. The error we expect is EINPROGRESS, indicating that the connection has started, but is not yet complete. Any other error is returned to the caller.
Overlap processing with connection establishment 15
- At this point, we can do whatever we want while we wait for the connection to complete.
Check for immediate completion 16-17
- If the nonblocking connect returns 0, the connection is complete. This can occur when the server is on the same host as the client.
Call select 18-24
- We zero out rset, turn on the bit corresponding to sockfd in this descriptor set, and then copy rset into wset. We also initialize the timeval structure and then call select. If the caller specifies a fourth argument of 0(uses the default timeout), we must specify a null pointer as the final argument to select and not a timeval structure with a value of 0(which means do not wait at all).
Handle timeouts 25-28
- If select returns 0, the timer expired, and we return ETIMEDOUT to the caller. We close the socket to prevent the three-way handshake from proceeding any further.
Check for readability or writability 29-34
- If the descriptor is readable or writable, we call getsockopt to fetch the socket’s pending error(SO_ERROR).
- If the connection completed successfully, this value will be 0.
- If the connection encountered an error, this value is the errno value corresponding to the connection error.
- We encounter our first portability problem. If an error occurred, Berkeley-derived implementations of getsockopt return 0 with the pending error returned in our variable error. But Solaris causes getsockopt itself to return -1 with errno set to the pending error. Our code handles both scenarios.
Turn off nonblocking and return 36-42
- We restore the file status flags and return. If our error variable is nonzero from getsockopt, that value is stored in errno and the function returns -1.
- There are portability problems with various socket implementations and nonblocking connects.
- First, it is possible for a connection to complete and for data to arrive from a peer before select is called. In this case, the socket will be both readable and writable on success, the same as if the connection had failed. Our code in Figure 16.11 handles this scenario by calling getsockopt and checking the pending error for the socket.
- Next is determining whether the connection completed successfully or not, if we cannot assume that writability is the only way success is returned. Solutions would replace our call to getsockopt in Figure 16.11.
- Call getpeername instead of getsockopt. If this fails with ENOTCONN, the connection failed and we must then call getsockopt with SO_ERROR to fetch the pending error for the socket.
- Call read with a length of 0. If the read fails, the connect failed and the errno from read indicates the reason for the connection failure. When a connection succeeds, read should return 0.
- Call connect again. It should fail, and if the error is EISCONN, the socket is already connected and the first connection succeeded.
Interrupted connect
- What happens if our call to connect on a normal blocking socket is interrupted before TCP’s three-way handshake completes?
- Assuming the connect is not automatically restarted, it returns EINTR. But we cannot call connect again to wait for the connection to complete. Doing so will return EADDRINUSE.
- What we must do in this scenario is call select. select returns when the connection completes successfully(making the socket writable) or when the connection fails(making the socket readable and writable).
16.5 Nonblocking ‘connect:’ Web Client
- Web client establishes an HTTP connection with a Web server and fetches a home page that usually has references to other Web pages. Instead of fetching these other pages serially, the client can fetch more than one at the same time using nonblocking connects. Figure 16.12 shows an example of establishing multiple connections in parallel.
- For Web clients, the first connection is done by itself, followed by connections for the references found in the data from that first connection(Figure 16.13).
- Optimization: The client can start parsing the data that is returned for the first connection before the first connection completes and initiate additional connections as soon as it knows that additional connections are needed.
- Our program will read up to 20 files from a Web server. We specify as command-line arguments the maximum number of parallel connections, the server’s hostname, and each of the filenames to fetch from the server. A typical execution of our program is
$ web 3 www.foobar.com / image1.gif image2.gif \
image3.gif image4.gif image5.gif \
image6.gif image7.gif
- The command-line arguments specify 3 simultaneous connections: server’s hostname, filename for home page(/, the server’s root page), and seven files to then read. These seven files would normally be referenced on the home page, and a Web client would read the home page and parse the HTML to obtain these filenames.
- Figure 16.14 is our web.h header that each file includes.
Define file structure 2-13
- The program reads up to MAXFILES files from the Web server. We maintain a file structure with information about each file: name(copied from the command-line argument), hostname or IP address of the server to read the file from, socket descriptor being used for the file, and a set of flags to specify what we are doing with this file(connecting, reading, or done).
Define globals and function prototypes 14-20
- We define the global variables and function prototypes for the functions that we will describe shortly. Figure 16.15 shows the first part of the main program.
Process command-line arguments 11-17
- The file structures are filled in with the relevant information from the command-line arguments.
Read home page 18
- home_page creates a TCP connection, sends a command to the server, and then reads the home page. This is the first connection, which is done by itself, before we start establishing multiple connections in parallel.
Initialize globals 19-23
- Two descriptor sets, one for reading and one for writing, are initialized. maxfd is the maximum descriptor for select(initialize to -1 since descriptors are non-negative), nlefttoread is the number of files remaining to be read(when this reaches 0, we are finished), nlefttoconn is the number of files that still need a TCP connection, and nconn is the number of connections currently open(which can never exceed the first command-line argument). Figure 16.16 shows the home_page function that is called once when the main function begins.
Establish connection with server 7
- tcp_connect establishes a connection with the server.
Send HTTP command to server, read reply 8-17
- An HTTP GET command is issued for the home page(often named /). The reply is read(we do not do anything with the reply) and the connection is closed.
- start_connect(Figure 16.17) initiates a nonblocking connect.
Create socket, set to nonblocking 7-13
- We call host_serv function(Figure 11.9) to look up and convert the hostname and service name, returning a pointer to an array of addrinfo structures.
- We use only the first structure. A TCP socket is created and the socket is set to nonblocking.
Initiate nonblocking connect 14-22
- The nonblocking connect is initiated and the file’s flag is set to F_CONNECTING. The socket descriptor is turned on in both the read set and the write set since select will wait for either condition as an indication that the connection has finished. We also update maxfd, if necessary.
Handle connection complete 23-24
- If connect returns successfully, the connection is already complete and the function write_get_cmd sends a command to the server.
- We set the socket to nonblocking for the connect, but never reset it to its default blocking mode. This is fine because we write only a small amount of data to the socket(the GET command in the next function) and we assume that this command is much smaller than the socket send buffer. Even if write returns a short count because of the nonblocking flag, our writen function handles this. Leaving the socket as nonblocking has no effect on the subsequent reads that are performed because we always call select to wait for the socket to become readable.
- Figure 16.18 shows the function write_get_cmd, which sends an HTTP GET command to the server.
Build command and send it 7-9
- The command is built and written to the socket.
Set flags 10-13
- The file’s F_READING flag is set, which also clears the F_CONNECTING flag(if set).
- This indicates to the main loop that this descriptor is ready for input. The descriptor is also turned on in the read set and maxfd is updated, if necessary.
- We now return to the main function in Figure 16.19, picking up where we left off in Figure 16.15. This is the main loop of the program: As long as there are more files to process(nlefttoread is greater than 0), start another connection if possible and then use select on all active descriptors, handling both nonblocking connection completions and the arrival of data.
Initiate another connection, if possible 24-35
- If we are not at the specified limit of simultaneous connections, and there are additional connections to establish, find a file that we have not yet processed(indicated by a f_flags of 0) and call start_connect to initiate the connection. The number of active connections is incremented(nconn) and the number of connections remaining to be established is decremented(nlefttoconn).
select: wait for something to happen 36-37
- select waits for either readability or writability. Descriptors that have a nonblocking connect in progress will be enabled in both sets, while descriptors with a completed connection that are waiting for data from the server will be enabled in just the read set.
Handle all ready descriptors 39-55
- We now process each element in the array of file structures to determine which descriptors need processing. If the F_CONNECTING flag is set and the descriptor is on in either the read set or the write set, the nonblocking connect is finished. As we described with Figure 16.11, we call getsockopt to fetch the pending error for the socket. If this value is 0, the connection completed successfully. In that case, we turn off the descriptor in the write set and call write_get_cmd to send the HTTP request to the server.
See if descriptor has data 56-67
- If the F_READING flag is set and the descriptor is ready for reading, we call read. If the connection was closed by the other end, we close the socket, set the F_DONE flag, turn off the descriptor in the read set, and decrement the number of active connections and the total number of connections to be processed.
- There are two optimizations that we do not perform in this example(to avoid complicating it even more). First, we could terminate the for loop in Figure 16.19 when we finish processing the number of descriptors that select said were ready.
- Next, we could decrease the value of maxfd when possible, to save select from examining descriptor bits that are no longer set. Since the number of descriptors this code deals with at any one time is probably less than 10, and not in the thousands, it is doubtful that either of these optimizations is worth the additional complications.
Performance of Simultaneous Connections
- What is the performance gain in establishing multiple connections at the same time? Figure 16.20 shows the clock time required to fetch a Web server’s home page, followed by nine image files from that server. The RTT to the server is about 150 ms.
- The home page size was 4,017 bytes and the average size of the 9 image files was 1,621 bytes. TCP’s segment size was 512 bytes. We also include in this figure, for comparison, values for a version of this program that we will develop in Section 26.9 using threads.
- Most of the improvement is obtained with three simultaneous connections(the clock time is halved), and the performance increase is much less with four or more simultaneous connections.
- We provide this example using simultaneous connects because it is a nice example using nonblocking I/O and one whose performance impact can be measured. It is also a feature used by a popular Web application, the Netscape browser. There are pitfalls in this technique if there is any congestion in the network. Chapter 21 of TCPv1 describes TCP’s slow-start and congestion avoidance algorithms in detail. When multiple connections are established from a client to a server, there is no communication between the connections at the TCP layer. That is, if one connection encounters a packet loss, the other connections to the same server are not notified, and it is highly probable that the other connections will soon encounter packet loss unless they slow down. These additional connections are sending more packets into an already congested network. This technique also increases the load at any given time on the server.
16.6 Nonblocking ‘accept’
- Chapter 6: A listening socket is returned as readable by select when a completed connection is ready to be accepted. If we are using select to wait for incoming connections, we should not need to set the listening socket to nonblocking because if select tells us that the connection is ready, accept should not block.
- There is a timing problem here. To see this problem, modify echo client(Figure 5.4) to establish the connection and then send an RST to the server. Figure 16.21 shows this new version.
Set SO_LINGER socket option 16-19
- Once the connection is established, we set SO_LINGER socket option, setting the l_onoff flag to 1 and the l_linger time to 0. Section 7.5: this causes an RST to be sent on a TCP socket when the connection is closed. We then close the socket.
- Next, modify TCP server from Figures 6.21 and 6.22 to pause after select returns that the listening socket is readable, but before calling accept. In the following code from the beginning of Figure 6.22, the two lines preceded by a plus sign are new:
if (FD_ISSET(listenfd, &rset))/* new client connection */
{
+ printf("listening socket readable\n");
+ sleep(5);
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
- We are simulating a busy server that cannot call accept as soon as select returns that the listening socket is readable. Normally, this slowness on the part of the server is not a problem(this is why a queue of completed connections is maintained), but when combined with the RST from the client, after the connection is established, we can have a problem.
- In Section 5.11, we noted that when the client aborts the connection before the server calls accept, Berkeley-derived implementations do not return the aborted connection to the server, while other implementations should return ECONNABORTED but often return EPROTO instead. Consider the following example of a Berkeley-derived implementation:
- The client establishes the connection and then aborts it as in Figure 16.21.
- select returns readable to the server process, but it takes the server a short time to call accept.
- Between the server’s return from select and its calling accept, the RST is received from the client.
- The completed connection is removed from the queue and we assume that no other completed connections exist.
- The server calls accept, but since there are no completed connections, it blocks.
- The server will remain blocked in the call to accept until some other client establishes a connection. But in the meantime, assuming a server like Figure 6.22, the server is blocked in the call to accept and will not handle any other ready descriptors.
- This problem is somewhat similar to the denial-of-service attack described in Section 6.8, but with this new bug, the server breaks out of the blocked accept as soon as another client establishes a connection.
- The fix for this problem is as follows:
- Always set a listening socket to nonblocking when you use select to indicate when a connection is ready to be accepted.
- Ignore the following errors on the subsequent call to accept: EWOULDBLOCK(for Berkeley-derived implementations, when the client aborts the connection), ECONNABORTED(for POSIX implementations, when the client aborts the connection), EPROTO(for SVR4 implementations, when the client aborts the connection), and EINTR(if signals are being caught).
16.7 Summary
- Our example of nonblocking reads and writes in Section 16.2 took our str_cli echo client and modified it to use nonblocking I/O on the TCP connection to the server.
- select is normally used with nonblocking I/O to determine when a descriptor is readable or writable. This version of our client is the fastest version that we show, although the code modifications are nontrivial. We then showed that it is simpler to divide the client into two pieces using fork; we will employ the same technique using threads in Figure 26.2.
- Nonblocking connects let us do other processing while TCP’s three-way handshake takes place, instead of being blocked in the call to connect. Unfortunately, these are also nonportable, with different implementations having different ways of indicating that the connection completed successfully or encountered an error. We used nonblocking connects to develop a new client, which is similar to a Web client that opens multiple TCP connections at the same time to reduce the clock time required to fetch numerous files from a server. Initiating multiple connections like this can reduce the clock time, but is also “network-unfriendly” with regard to TCP’s congestion avoidance.
Exercises
Please indicate the source: http://blog.youkuaiyun.com/gaoxiangnumber1
Welcome to my github: https://github.com/gaoxiangnumber1