參考博客:
①setsockopt()函數使用詳解:http://blog.csdn.net/tody_guo/article/details/5972588
②setsockopt :SO_LINGER 選項設置:http://blog.csdn.net/factor2000/article/details/3929816
③TIME_WAIT狀態的作用:http://www.cnblogs.com/li-hao/archive/2011/12/08/2280678.html
在學習linux下c網絡編程時,標准的C/S架構的網絡體系模式時,沒有注意connect的非阻塞模式,最近看項目代碼時,發現原來connect非阻塞模式還有這么大的作用。但從程序客戶端的角度,優化大量客戶端連接服務器的性能。讓我突然想起曾經面試時的問題,TCP三次握手與四次揮手與你寫的程序有什么關系。現在就知道了。
關於:TCP三次握手與四次揮手,請看http://www.cnblogs.com/cz-blog/p/4431385.html。我之前的博客。
說明:由於程序用select等待連接完成,可以設置一個select等待時間限制,從而縮短connect超時時間。多數實現中,connect的超時時間在75秒到幾分鍾之間。有時程序希望在等待一定時間內結束,使用非阻塞connect可以防止阻塞75秒,在多線程網絡編程中,尤其必要。 例如有一個通過建立線程與其他主機進行socket通信的應用程序,如果建立的線程使用阻塞connect與遠程通信,當有幾百個線程並發的時候,由於網絡延遲而全部阻塞,阻塞的線程不會釋放系統的資源,同一時刻阻塞線程超過一定數量時候,系統就不再允許建立新的線程(每個進程由於進程空間的原因能產生的線程有限),如果使用非阻塞的connect,連接失敗使用select等待很短時間,如果還沒有連接后,線程立刻結束釋放資源,防止大量線程阻塞而使程序崩潰。
int tcp_connect(char *host, int port) { int sock, flags; struct sockaddr_in rsock; struct hostent * hostinfo; struct in_addr * addp; struct linger stLinger = { 1, 2 }; memset ((char *)&rsock,0,sizeof(rsock)); if ((hostinfo = gethostbyname(host)) == NULL) { return -1; } //步驟一:socket sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { return -1; } //步驟二:填充 addp = (struct in_addr *)*(hostinfo->h_addr_list); rsock.sin_addr = *addp; rsock.sin_family = AF_INET; rsock.sin_port = htons(port); int ret = 0, error = -1, slen = sizeof(int); timeval tm; fd_set set; unsigned long ul = 1; ioctl(sock, FIONBIO, &ul); //設置為非阻塞模式 //步驟三:connect,此時socket設置為非阻塞,connect調用后,無論連接是否建立立即返回-1,
if (connect(sock, (struct sockaddr *)(&rsock), sizeof(rsock)) == -1) { //表示此時tcp三次握手仍舊進行,如果errno不是EINPROGRESS,則說明連接錯誤,程序結束 if (errno != EINPROGRESS) { ret = 0; } else { tm.tv_sec = 5; tm.tv_usec = 0; FD_ZERO(&set); FD_SET(sock, &set); //監聽寫集合 if (select(sock+1, NULL, &set, NULL, &tm) > 0) { //過調用 getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&error,&len); 函數返回值來判斷是否發生錯誤 //rror返回0則表示連接成功! getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&slen); if (error == 0) { ret = 1; } else { ret = 0; } } else { ret = 0; } } } else {//客戶端和服務器已經建立連接 ret = 1; } ul = 0; ioctl(sock, FIONBIO, &ul); //設置為阻塞模式 //ret =1:表示正常建立連接 if (!ret) { close(sock); return -1; } //linger :徘徊的意思。SO_LINGER:表示經歷time_wait階段,且時間是stLinger中第二個參數指定的值。 flags = setsockopt(sock, SOL_SOCKET, SO_LINGER, &stLinger, sizeof(struct linger)); if (flags == -1) { close(sock); return -1; } return sock;
setsockopt 設置 SO_LINGER 選項:作用就是在close關閉時,保證發送數據發送到對方后,再徹底關閉連接。
當套接口關閉時內核將拖延一段時間(由l_linger決定)。如果套接口緩沖區中仍殘留數據,進程將處於睡眠狀態,直到
(a)所有數據發送完且被對方確認,之后進行正常的終止序列(描述字訪問計數為0)或
(b)延遲時間到。
此種情況下,應用程序檢查close的返回值是非常重要的,因為要區分兩種狀態:
如果在數據發送完並被確認前時間到,close將返回EWOULDBLOCK錯誤且套接口發送緩沖區中的任何數據都丟失。
如果數據發送完並被確認后,指定的時間才到,close的成功返回僅告訴我們發送的數據(和FIN)已由對方TCP確認,它並不能告訴我們對方應用進程是否已讀了數據。如果套接口設為非阻塞的,它將不等待close完成。
此選項指定函數close對面向連接的協議如何操作(如TCP)。內核缺省close操作是立即返回,如果有數據殘留在套接口緩沖區中則系統將試着將這些數據發送給對方。
SO_LINGER選項用來改變此缺省設置。使用如下結構:
struct linger {
int l_onoff; /* 0 = off, nozero = on */
int l_linger; /* linger time */
};
有下列三種情況:
1、設置 l_onoff為0,則該選項關閉,l_linger的值被忽略,等於內核缺省情況,close調用會立即返回給調用者,如果可能將會傳輸任何未發送的數據;
2、設置 l_onoff為非0,l_linger為0,則套接口關閉時TCP夭折連接,TCP將丟棄保留在套接口發送緩沖區中的任何數據並發送一個RST給對方,而不是通常的四分組終止序列,這避免了TIME_WAIT狀態;
3、設置 l_onoff 為非0,l_linger為非0,當套接口關閉時內核將拖延一段時間(由l_linger決定)。如果套接口緩沖區中仍殘留數據,進程將處於睡眠狀態,直 到(a)所有數據發送完且被對方確認,之后進行正常的終止序列(描述字訪問計數為0)或(b)延遲時間到。此種情況下,應用程序檢查close的返回值是非常重要的,如果在數據發送完並被確認前時間到,close將返回EWOULDBLOCK錯誤且套接口發送緩沖區中的任何數據都丟失。close的成功返回僅告訴我們發送的數據(和FIN)已由對方TCP確認,它並不能告訴我們對方應用進程是否已讀了數據。如果套接口設為非阻塞的,它將不等待close完成。
更具體的描述如下:
1、若設置了SO_LINGER(亦即linger結構中的l_onoff域設為非零),並設置了零超時間隔,則closesocket()不被阻塞立即執行,不論是否有排隊數據未發送或未被確認。這種關閉方式稱為“強制”或“失效”關閉,因為套接口的虛電路立即被復位,且丟失了未發送的數據。在遠端的recv()調用將以WSAECONNRESET出錯。
2、若設置了SO_LINGER並確定了非零的超時間隔,則closesocket()調用阻塞進程,直到所剩數據發送完畢或超時。這種關閉稱為“優雅”或“從容”關閉。請注意如果套接口置為非阻塞且SO_LINGER設為非零超時,則closesocket()調用將以WSAEWOULDBLOCK錯誤返回。
3、若在一個流類套接口上設置了SO_DONTLINGER(也就是說將linger結構的l_onoff域設為零),則closesocket()調用立即返回。但是,如果可能,排隊的數據將在套接口關閉前發送。請注意,在這種情況下WINDOWS套接口實現將在一段不確定的時間內保留套接口以及其他資源,這對於想用所以套接口的應用程序來說有一定影響。
SO_DONTLINGER 若為真,則SO_LINGER選項被禁止。
SO_LINGER延遲關閉連接 struct linger上面這兩個選項影響close行為;
選項 間隔 關閉方式 等待關閉與否
SO_DONTLINGER 不關心 優雅 否
SO_LINGER 零 強制 否
SO_LINGER 非零 優雅 是