非阻IO与EWOULDBLOCK EAGAIN
生活随笔
收集整理的這篇文章主要介紹了
非阻IO与EWOULDBLOCK EAGAIN
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
非阻塞讀寫
默認 socket 是阻塞的,讀寫函數?read,?readv,?recv,?recvfrom,?recvmsg 以及?write,?writev,?send,?sendto,?sendmsg 都有可能會阻塞。可以將 socket 描述字設為非阻塞,這樣,當 socket 描述字未就緒時,調用以上讀寫函數將會返回 EWOULDBLOCK 或 EAGAIN 。
UNPv1 給出了一個?非阻塞socket + select?的例子。有人對此提出疑問:
假如fd1是一個阻塞socket,我將它加入select的readset中,然后用select去偵聽fd1上是否有數據到來。我感覺這和非阻塞 socket的性質是一樣的,因為它不會阻塞在fd1的recv 函數上,因為之前select已經判定到fd1可讀,所以recv 就會返回不會阻塞。 那么為什么大家總要還要創建一個非阻塞的socket加入select中呢?
有人給出的答案是:
select 只能說明 socket 可讀或者可寫,不能說明能讀入或者能寫出多少數據。比如,socket 的寫緩沖區有 10 個字節的空閑空間,這時監視的 select 返回,然后在該 socket 上進行寫操作。但是如果要寫入 100 字節,如果 socket 沒有設置非阻塞,調用 write 就會阻塞在那里。而更為要緊的是,在多個 socket 的情況下,讀寫一個socket 時阻塞,會影響到其他的 socket 。
UNPv1 上的例子如下:
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(max(STDIN_FILENO, STDOUT_FILENO), sockfd) + 1;
20 for ( ; ; ) {
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 }這個例子中用到的兩個 buffer 如下:
由于上面這個例子 buffer 管理 太過復雜,作者又給出了?多進程 方式來替代 上面的 非阻塞 + select 。
對于同一個 socket ,一個進程讀,另一個進程寫。
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 }
非阻塞 connect
TCP socket 被設為非阻塞后調用 connect ,connect 函數會立即返回 EINPROCESS ,但 TCP 的 3 次握手繼續進行。之后可以用 select 檢查 連接是否建立成功。非阻塞 connect 有3 種用途:
1. 在3 次握手的同時做一些其他的處理。
2. 可以同時建立多個連接。
3. 在利用 select 等待的時候,可以給 select 設定一個時間,從而可以縮短 connect 的超時時間。
使用非阻塞 connect 需要注意的問題是:
1. 很可能 調用 connect 時會立即建立連接(比如,客戶端和服務端在同一臺機子上),必須處理這種情況。
2. Posix 定義了兩條與 select 和 非阻塞 connect 相關的規定:
1)連接成功建立時,socket 描述字變為可寫。(連接建立時,寫緩沖區空閑,所以可寫)
2)連接建立失敗時,socket 描述字既可讀又可寫。 (由于有未決的錯誤,從而可讀又可寫)
UNPv1 給出的 非阻塞 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 }
注意事項:
1. 如果在調用 select 之前,連接已經建立成功,并且有數據發送過來了,這時套接字將是即可讀又可寫,和連接失敗時是一樣的。所以我們必須用 getsockopt 來檢查套接字的狀態。
2. 由于 socket 可寫并不能說明連接是否成功建立,可以用以下幾種方法取代 getsockopt 來檢查連接到底是不是成功建立。
1)調用 getpeername ,如果調用失敗,返回 ENOTCONN ,表示連接失敗。可以用 getsockopt (SO_ERROR) 獲取 socket 上待處理的錯誤。
2)調用 read ,長度參數為 0 , 如果read 失敗,表明 connect 失敗,而且 read 返回的 errno 指明了連接失敗的原因。如果連接成功,read 返回 0 。
3)在調用 connect 一次,這是應該失敗,如果錯誤號為 EISCONN ,表明連接已經成功建立。
被中斷的 connect
在阻塞的 socket 上 調用 connect ,在 TCP 3 次握手完成之前被信號中斷,如果 connect 不被重啟,將返回 EINTR 。但是不能再調用 connect 來完成連接,這樣做會返回 EADDRINUSE 。
這時需要做的是調用 select ,如同非阻塞 socket 上調用 select 一樣。select 返回時表明連接成功(socket 可寫)或連接失敗(socket 可讀可寫)。
非阻塞 accept
當用 select 監視 listening socket 時, 如果有新連接到來,select 返回, 該 listening socket 變為可讀。然后我們 accept 接收該連接。
問題是:accept 時,將 listening socket 設置為 非阻塞 的必要性是什么?
首先說明一下 已完成3次握手的連接在 accept 之前 被 異常終止(Aborted )時發生的情況,如下圖:
一個連接被異常終止時執行的動作取決于實現:
1. 基于 Berkeley 的實現完全由內核處理該異常終止的連接, 應用進程看不到。
2. 基于 SVR4 的實現,在連接異常終止后調用 accept 時,通常會給應用進程返回 EPROTO 錯誤。但是 Posix 指出應該返回 ECONNABORTED 。Posix 認為當發生致命的協議相關的錯誤時,返回 EPROTO 錯誤。而 異常終止一個連接并非致命錯誤,從而返回 ECONNABORTED ,與 EPROTO 區分開來,這樣隨后可以繼續調用 accept 。?
現在假設是基于 Berkeley 的實現,在 select 返回后,accept 調用之前,如果連接被異常終止,這時 accept 調用可能會由于沒有已完成的連接而阻塞,直到有新連接建立。對于服務進程而言,在被 accept 阻塞的這一段時間內,將不能處理其他已就緒的 socket 。
解決上面這個問題有兩種方法:
1. 在用 select 監視 listening socket 時,總是將 listening socket 設為非阻塞模式。
2. 忽略 accept 返回的以下錯誤:
??? EWOULDBLOCK(基于 berkeley 實現,當客戶端異常終止連接時)、ECONNABORTED(基于 posix 實現,當客戶端異常終止連接時)、EPROTO(基于 SVR4 實現,當客戶端異常終止連接時)以及 EINTR 。
默認 socket 是阻塞的,讀寫函數?read,?readv,?recv,?recvfrom,?recvmsg 以及?write,?writev,?send,?sendto,?sendmsg 都有可能會阻塞。可以將 socket 描述字設為非阻塞,這樣,當 socket 描述字未就緒時,調用以上讀寫函數將會返回 EWOULDBLOCK 或 EAGAIN 。
UNPv1 給出了一個?非阻塞socket + select?的例子。有人對此提出疑問:
假如fd1是一個阻塞socket,我將它加入select的readset中,然后用select去偵聽fd1上是否有數據到來。我感覺這和非阻塞 socket的性質是一樣的,因為它不會阻塞在fd1的recv 函數上,因為之前select已經判定到fd1可讀,所以recv 就會返回不會阻塞。 那么為什么大家總要還要創建一個非阻塞的socket加入select中呢?
有人給出的答案是:
select 只能說明 socket 可讀或者可寫,不能說明能讀入或者能寫出多少數據。比如,socket 的寫緩沖區有 10 個字節的空閑空間,這時監視的 select 返回,然后在該 socket 上進行寫操作。但是如果要寫入 100 字節,如果 socket 沒有設置非阻塞,調用 write 就會阻塞在那里。而更為要緊的是,在多個 socket 的情況下,讀寫一個socket 時阻塞,會影響到其他的 socket 。
UNPv1 上的例子如下:
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(max(STDIN_FILENO, STDOUT_FILENO), sockfd) + 1;
20 for ( ; ; ) {
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 }這個例子中用到的兩個 buffer 如下:
由于上面這個例子 buffer 管理 太過復雜,作者又給出了?多進程 方式來替代 上面的 非阻塞 + select 。
對于同一個 socket ,一個進程讀,另一個進程寫。
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 }
非阻塞 connect
TCP socket 被設為非阻塞后調用 connect ,connect 函數會立即返回 EINPROCESS ,但 TCP 的 3 次握手繼續進行。之后可以用 select 檢查 連接是否建立成功。非阻塞 connect 有3 種用途:
1. 在3 次握手的同時做一些其他的處理。
2. 可以同時建立多個連接。
3. 在利用 select 等待的時候,可以給 select 設定一個時間,從而可以縮短 connect 的超時時間。
使用非阻塞 connect 需要注意的問題是:
1. 很可能 調用 connect 時會立即建立連接(比如,客戶端和服務端在同一臺機子上),必須處理這種情況。
2. Posix 定義了兩條與 select 和 非阻塞 connect 相關的規定:
1)連接成功建立時,socket 描述字變為可寫。(連接建立時,寫緩沖區空閑,所以可寫)
2)連接建立失敗時,socket 描述字既可讀又可寫。 (由于有未決的錯誤,從而可讀又可寫)
UNPv1 給出的 非阻塞 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 }
注意事項:
1. 如果在調用 select 之前,連接已經建立成功,并且有數據發送過來了,這時套接字將是即可讀又可寫,和連接失敗時是一樣的。所以我們必須用 getsockopt 來檢查套接字的狀態。
2. 由于 socket 可寫并不能說明連接是否成功建立,可以用以下幾種方法取代 getsockopt 來檢查連接到底是不是成功建立。
1)調用 getpeername ,如果調用失敗,返回 ENOTCONN ,表示連接失敗。可以用 getsockopt (SO_ERROR) 獲取 socket 上待處理的錯誤。
2)調用 read ,長度參數為 0 , 如果read 失敗,表明 connect 失敗,而且 read 返回的 errno 指明了連接失敗的原因。如果連接成功,read 返回 0 。
3)在調用 connect 一次,這是應該失敗,如果錯誤號為 EISCONN ,表明連接已經成功建立。
被中斷的 connect
在阻塞的 socket 上 調用 connect ,在 TCP 3 次握手完成之前被信號中斷,如果 connect 不被重啟,將返回 EINTR 。但是不能再調用 connect 來完成連接,這樣做會返回 EADDRINUSE 。
這時需要做的是調用 select ,如同非阻塞 socket 上調用 select 一樣。select 返回時表明連接成功(socket 可寫)或連接失敗(socket 可讀可寫)。
非阻塞 accept
當用 select 監視 listening socket 時, 如果有新連接到來,select 返回, 該 listening socket 變為可讀。然后我們 accept 接收該連接。
問題是:accept 時,將 listening socket 設置為 非阻塞 的必要性是什么?
首先說明一下 已完成3次握手的連接在 accept 之前 被 異常終止(Aborted )時發生的情況,如下圖:
一個連接被異常終止時執行的動作取決于實現:
1. 基于 Berkeley 的實現完全由內核處理該異常終止的連接, 應用進程看不到。
2. 基于 SVR4 的實現,在連接異常終止后調用 accept 時,通常會給應用進程返回 EPROTO 錯誤。但是 Posix 指出應該返回 ECONNABORTED 。Posix 認為當發生致命的協議相關的錯誤時,返回 EPROTO 錯誤。而 異常終止一個連接并非致命錯誤,從而返回 ECONNABORTED ,與 EPROTO 區分開來,這樣隨后可以繼續調用 accept 。?
現在假設是基于 Berkeley 的實現,在 select 返回后,accept 調用之前,如果連接被異常終止,這時 accept 調用可能會由于沒有已完成的連接而阻塞,直到有新連接建立。對于服務進程而言,在被 accept 阻塞的這一段時間內,將不能處理其他已就緒的 socket 。
解決上面這個問題有兩種方法:
1. 在用 select 監視 listening socket 時,總是將 listening socket 設為非阻塞模式。
2. 忽略 accept 返回的以下錯誤:
??? EWOULDBLOCK(基于 berkeley 實現,當客戶端異常終止連接時)、ECONNABORTED(基于 posix 實現,當客戶端異常終止連接時)、EPROTO(基于 SVR4 實現,當客戶端異常終止連接時)以及 EINTR 。
總結
以上是生活随笔為你收集整理的非阻IO与EWOULDBLOCK EAGAIN的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 日本版GPS助力自动驾驶:行驶不用摄像头
- 下一篇: 长江养老添享年周末产生收益吗?周末收益在