Non-blocking read/write
The default socket is blocked, and the read/write functionRead,Readv,Recv,Recvfrom,Recvmsg andWrite,Writev,Send,Sendto,Sendmsg may be blocked. You can set the socket description to non-blocking. In this way, when the socket description is not ready, the ewouldblock will be returned when the above read/write function is called.
Or eagain.
Unpv1 providesNon-blocking Socket + select. Some people have doubts about this:
If fd1 is a blocking socket, I add it to the select readset, and then use select to listen for data on fd1. I think this is the same as the non-blocking Socket because it will not block the fd1 Recv function. Because the SELECT statement has been determined to be fd1 readable, The Recv will return and will not block it. SoWhy do you always need to create a non-blocking socket and add it to the SELECT statement??
The answer is:
Select can only indicate whether the socket is readable or writable, but not how much data can be read or written. For example, the socket write buffer has 10 bytes of free space, then the monitored select returns, and then write on the socket. But if you want to write 100 bytes, if the socket is not set to non-blocking, calling write will block it. More importantly,When multiple sockets exist, blocking occurs when one socket is read or written, affecting other sockets.
The example on unpv1 is as follows:
1 # include "unp. H"
2 void
3 str_cli (File * FP, int sockfd)
4 {
5 Int maxfdp1, Val, stdineof;
6 ssize_t N, nwritten;
7 fd_set rset, Wset;
8 Char to [maxline], FR [maxline];
9 char * toiptr, * tooptr, * friptr, * froptr;
10 val = fcntl (sockfd, f_getfl, 0 );
11 fcntl (sockfd, f_setfl, Val | o_nonblock );
12 val = fcntl (stdin_fileno, f_getfl, 0 );
13 fcntl (stdin_fileno, f_setfl, Val | o_nonblock );
14 val = fcntl (stdout_fileno, f_getfl, 0 );
15 fcntl (stdout_fileno, f_setfl, Val | o_nonblock );
16 toiptr = tooptr = to;/* initialize buffer pointers */
17 friptr = froptr = fr;
18 stdineof = 0;
19 maxfdp1 = max (stdin_fileno, stdout_fileno), sockfd) + 1;
20 (;;){
21 fd_zero (& rset );
22 fd_zero (& Wset );
23 if (stdineof = 0 & toiptr <& to [maxline])
24 fd_set (stdin_fileno, & rset);/* read from stdin */
25 if (friptr <& FR [maxline])
26 fd_set (sockfd, & rset);/* read from socket */
27 if (tooptr! = Toiptr)
28 fd_set (sockfd, & Wset);/* Data to write to socket */
29 If (froptr! = Friptr)
30 fd_set (stdout_fileno, & Wset);/* Data to write to stdout */
31 select (maxfdp1, & rset, & Wset, null, null );
32 If (fd_isset (stdin_fileno, & rset )){
33 If (n = read (stdin_fileno, toiptr, & to [maxline]-toiptr) <0 ){
34 if (errno! = Ewouldblock)
35 err_sys ("read error on stdin ");
36} else if (n = 0 ){
37 fprintf (stderr, "% s: EOF on stdin \ n", gf_time ());
38 stdineof = 1;/* all done with stdin */
39 if (tooptr = toiptr)
40 Shutdown (sockfd, shut_wr);/* Send fin */
41} else {
42 fprintf (stderr, "% s: Read % d bytes from stdin \ n", gf_time (),
43 N );
44 toiptr + = N;/* # Just read */
45 fd_set (sockfd, & Wset);/* try and write to socket below */
46}
47}
48 if (fd_isset (sockfd, & rset )){
49 If (n = read (sockfd, friptr, & FR [maxline]-friptr) <0 ){
50 if (errno! = Ewouldblock)
51 err_sys ("read error on socket ");
52} else if (n = 0 ){
53 fprintf (stderr, "% s: EOF on socket \ n", gf_time ());
54 if (stdineof)
55 return;/* normal termination */
56 else
57 err_quit ("str_cli: Server terminated prematurely ");
58} else {
59 fprintf (stderr, "% s: Read % d bytes from socket \ n ",
60 gf_time (), N );
61 friptr + = N;/* # Just read */
62 fd_set (stdout_fileno, & Wset);/* try and write below */
63}
64}
65 if (fd_isset (stdout_fileno, & Wset) & (n = friptr-froptr)> 0 )){
66 If (nwritten = write (stdout_fileno, froptr, n) <0 ){
67 If (errno! = Ewouldblock)
68 err_sys ("write error to stdout ");
69} else {
70 fprintf (stderr, "% s: wrote % d bytes to stdout \ n ",
71 gf_time (), nwritten );
72 froptr + = nwritten;/* # Just written */
73 If (froptr = friptr)
74 froptr = friptr = fr;/* back to beginning of buffer */
75}
76}
77 If (fd_isset (sockfd, & Wset) & (n = toiptr-tooptr)> 0 )){
78 If (nwritten = write (sockfd, tooptr, n) <0 ){
79 if (errno! = Ewouldblock)
80 err_sys ("write error to socket ");
81} else {
82 fprintf (stderr, "% s: wrote % d bytes to socket \ n ",
83 gf_time (), nwritten );
84 tooptr + = nwritten;/* # Just written */
85 if (tooptr = toiptr ){
86 toiptr = tooptr = to;/* back to beginning of buffer */
87 If (stdineof)
88 Shutdown (sockfd, shut_wr);/* Send fin */
89}
90}
91}
92}
93}
1 # include "unp. H"
2 # include <time. h>
3 char *
4 gf_time (void)
5 {
6 struct timeval TV;
7 static char STR [30];
8 char * PTR;
9 If (gettimeofday (& TV, null) <0)
10 err_sys ("gettimeofday error ");
11 PTR = ctime (& TV. TV _sec );
12 strcpy (STR, & PTR [11]);
13/* Fri Sep 13 00:00:00 1986 \ n \ 0 */
14/* 0123456789012345678901234 5 */
15 snprintf (STR + 8, sizeof (STR)-8, ". % 06ld", TV. TV _usec );
16 return (STR );
17} The two buffers used in this example are as follows:
As the buffer management in the above example is too complex, the author also givesMulti-process modeTo replace the above non-blocking + select.
For the same socket, one process reads and the other process writes.
1 # include "unp. H"
2 void
3 str_cli (File * FP, int sockfd)
4 {
5 pid_t PID;
6 char sendline [maxline], recvline [maxline];
7 if (pid = fork () = 0) {/* Child: Server-> stdout */
8 while (Readline (sockfd, recvline, maxline)> 0)
9 fputs (recvline, stdout );
10 kill (getppid (), sigterm);/* In case parent still running */
11 exit (0 );
12}
13/* parent: stdin-> server */
14 While (fgets (sendline, maxline, FP )! = NULL)
15 writen (sockfd, sendline, strlen (sendline ));
16 Shutdown (sockfd, shut_wr);/* EOF on stdin, send fin */
17 pause ();
18 return;
19}
Non-blocking connect
When TCP socket is set to non-blocking, connect is called. The connect function immediately returns einprocess, but the TCP three handshakes continue. Then you can use select to check whether the connection is established successfully. Non-blocking connect has three purposes:
1. Do some other processing at the same time of three handshakes.
2. Multiple connections can be established at the same time.
3. When using select to wait, you can set a time for select to shorten the connect timeout time.
Note the following when using non-blocking CONNECT:
1. It is very likely that a connection will be established immediately when you call connect (for example, the client and the server are on the same machine). This situation must be handled.
2. POSIX defines two rules related to select and non-blocking CONNECT:
1) when the connection is established successfully, the socket description is writable. (When a connection is established, the write buffer is idle, so it can be written)
2) When the connection fails to be established, the socket description is both readable and writable. (Readable and writable due to pending errors)
Unpv1 provides an example of non-blocking CONNECT:
1 # include "unp. H"
2 int
3 connect_nonb (INT sockfd, const Sa * saptr, socklen_t Salen, int nsec)
4 {
5 Int flags, N, error;
6 socklen_t Len;
7 fd_set rset, Wset;
8 struct timeval tval;
9 flags = fcntl (sockfd, f_getfl, 0 );
10 fcntl (sockfd, f_setfl, flags | o_nonblock );
11 error = 0;
12 if (n = connect (sockfd, saptr, salen) <0)
13 if (errno! = Einprogress)
14 return (-1 );
15/* do whatever we want while the CONNECT is taking place .*/
16 if (n = 0)
17 goto done;/* connect completed immediately */
18 fd_zero (& rset );
19 fd_set (sockfd, & rset );
20 Wset = rset;
21 tval. TV _sec = nsec;
22 tval. TV _usec = 0;
23 if (n = select (sockfd + 1, & rset, & Wset, null,
24 nsec? & Tval: NULL) = 0 ){
25 close (sockfd);/* timeout */
26 errno = etimedout;
27 return (-1 );
28}
29 If (fd_isset (sockfd, & rset) | fd_isset (sockfd, & Wset )){
30 Len = sizeof (error );
31 if (getsockopt (sockfd, sol_socket, so_error, & error, & Len) <0)
32 return (-1);/* Solaris pending Error */
33} else
34 err_quit ("select error: sockfd not set ");
35 done:
36 fcntl (sockfd, f_setfl, flags);/* restore file Status flags */
37 If (error ){
38 close (sockfd);/* Just In Case */
39 errno = error;
40 return (-1 );
41}
42 return (0 );
43}
Note:
1. If the connection is established successfully and data is sent before the Select call, the socket can be read and writable, which is the same as when the connection fails. Therefore, we must use getsockopt to check the socket status.
2. Because the socket is writable and does not indicate whether the connection is successfully established, you can replace getsockopt with the following methods to check whether the connection is successfully established.
1) Call getpeername. If the call fails, enotconn is returned, indicating that the connection fails. You can use getsockopt (so_error) to obtain the error to be processed on the socket.
2) Call read. The length parameter is 0. If read fails, connect fails, and errno returned by read indicates the cause of connection failure. If the connection is successful, read returns 0.
3) The connection should fail once. If the error code is eisconn, the connection has been established successfully.
Interrupted connect
Call connect on the blocked socket and the signal is interrupted before the TCP handshake is completed. If connect is not restarted, eintr is returned. However, you cannot call connect to complete the connection. In this case, eaddrinuse is returned.
In this case, you need to call select, just as if you call select on a non-blocking socket. When the SELECT statement is returned, the connection is successful (socket writable) or connection fails (socket readable writable ).
Non-blocking accept
When you use select to monitor the listening socket, if a new connection arrives, select returns, and the listening socket becomes readable. Then we accept to receive the connection.
Question: What is the necessity to set the listening socket to non-blocking during accept?
First, describe the situation that the three handshakes have been terminated before accept (aborted), for example:
The action executed when a connection is terminated abnormally depends on the implementation:
1. the Berkeley-based implementation is completely handled by the kernel to terminate the connection, and the application process cannot see it.
2. Based on the svr4 implementation, when an accept is called after the connection is terminated due to an exception, an eproto error is usually returned to the application process. POSIX indicates that econnaborted should be returned. POSIX determines that an eproto error is returned when a fatal protocol-related error occurs. However, terminating a connection with an exception is not a fatal error, and econnaborted is returned, which is distinguished from eproto. In this way, you can continue to call accept.
Now it is assumed that the implementation is based on the Berkeley. After the select return, if the connection is terminated abnormally before the Accept call is called, The accept call may be blocked due to no established connection, until a new connection is established.For service processes, other ready sockets cannot be processed during the period of time when the accept is blocked.
There are two ways to solve this problem:
1. When using select to monitor the listening socket, the listening socket is always set to non-blocking mode.
2. Ignore the following error returned by accept:
Ewouldblock (implemented based on Berkeley, when the client terminates the connection abnormally), econnaborted (implemented based on POSIX, when the client terminates the connection abnormally), eproto (implemented based on svr4, when the client terminates the connection abnormally) and eintr.