阻塞,非阻塞connect()和accept()
非阻塞connect()和accept()
一.select()函式
select()函式準備好讀的條件:
1>.套介面有資料可讀
2>.該連線的讀這一半關閉(也就是接收了FIN的TCP連線)。對這樣的套介面進行讀操作將不阻塞並返回0(也就是返回EOF)。
3>.該套介面是一個偵聽套介面且已完成的連線數不為0。
4>.其上有一個套介面錯誤待處理,對這樣的套介面的讀操作將不阻塞並返回-1,並設定errno,可以通過設定SO_ERROR選項呼叫getsockopt函式獲得。
select()函式準備好寫的條件:
1>.套介面有可用於寫的空間。
2>.該連線的寫這一半關閉,對這樣的套介面進行寫操作將產生SIGPIPE訊號。
3>.該套介面使用非阻塞的方式connect建立連線,並且連線已經非同步建立,或則connect已經以失敗告終。
4>.其上有一個套介面錯誤待處理。
- 1
- 2
- 3
- 4
- 5
二.accept()函式
1.阻塞模式
阻塞模式下呼叫accept()函式,而且沒有新連線時,程序會進入睡眠狀態。
2.非阻塞模式
非阻塞模式下呼叫accept()函式,而且沒有新連線時,將返回EWOULDBLOCK錯誤。
非阻塞模式select() + accept()
sockfd = listen_tcp(); //socket()、bind()、listen()
FD_SET(sockfd, rset);
while(1){
ret = select(sockfd + 1, rset, NULL, NULL, timeout); // 等待某個事件發生:或是新連線、或是資料、或是FIN、或是RST到達 if(select()返回TIMEOUT){ //select()超時 printf("日誌列印"); sleep(1); continue; } else if(FD_ISSET(sockfd,&rset)){ //判斷控制代碼是否可讀,返回真代表可讀,可讀代表有新連線。 connfd = accept(sockfd, ...); } else if(select()返回錯誤){ return -1; } pthread_create(thread_recv_data, connfd, ...); // 建立執行緒處理新連線. close();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
}
三.connect()函式
1.阻塞模式
客戶端呼叫connect()函式將激發TCP的三路握手過程,但僅在連線建立成功或出錯時才返回。返回的錯誤可能有以下幾種情況:
1>.如果TCP客戶端沒有接收到SYN分節的響應,則返回ETIMEDOUT,阻塞模式的超時時間在75秒(4.4BSD核心)到幾分鐘之間。
2>.如果對客戶的SYN的響應時RST,則表明該伺服器主機在我們指定的埠上沒有程序在等待與之連線(例如伺服器程序也許沒有啟動),這稱為硬錯,客
- 1
- 2
- 3
戶一接收到RST,馬上就返回錯誤ECONNREFUSED.
3>.如果某客戶發出的SYN在中間的路由器上引發了一個目的地不可達ICMP錯誤,多次嘗試傳送失敗後返回錯誤號為EHOSTUNREACH或ENETUNREACH.
- 1
附加:產生RST的三種情況,一是SYN到達某埠但此埠上沒有正在偵聽的伺服器、二是TCP想取消一個已有連線、三是TCP接收了一個根本不存在的連線上的
分節。
2.非阻塞工作模式
呼叫connect()函式會立刻返回EINPROCESS錯誤,但TCP通訊的三路握手過程正在進行,所以可以使用select函式來檢查這個連線是否建立成功。
源自Berkeley的實現有兩條與select函式和非阻塞相關的規則:
- 1
1>.當連線成功建立時,描述字變成可寫。
2>.當連線建立出錯時,描述字變成即可讀又可寫。getsockopt()函式的errno == 0表示只可寫。
非阻塞模式 select() + connect()
while(1){
ret = connect();
if(errno == EINPROCESS){ //此時TCP的三路握手繼續進行
select(...) //等待某個事件發生:或是新連線、或是超時
if(FD_ISSET(sockfd,&wset) ){ //判斷控制代碼可寫,不能代表建立連線成功。
getsockopt(...);
if(errno == 0){ //建立連線成功
}
}
else if(select()返回TIMEOUT){
sleep(1);
continue();
}
}
else if(ret == -1){
//Connect failed
}
pthread_create(thread_send_log, ...); //建立執行緒處理新連線
close();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
}
https://blog.csdn.net/bobkentblog/art