參考博客:html
①setsockopt()函數使用詳解:http://blog.csdn.net/tody_guo/article/details/5972588linux
②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)或socket
(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 */
};
有下列三種狀況:
一、設置 l_onoff爲0,則該選項關閉,l_linger的值被忽略,等於內核缺省狀況,close調用會當即返回給調用者,若是可能將會傳輸任何未發送的數據;
二、設置 l_onoff爲非0,l_linger爲0,則套接口關閉時TCP夭折鏈接,TCP將丟棄保留在套接口發送緩衝區中的任何數據併發送一個RST給對方,而不是一般的四分組終止序列,這避免了TIME_WAIT狀態;
三、設置 l_onoff 爲非0,l_linger爲非0,當套接口關閉時內核將拖延一段時間(由l_linger決定)。若是套接口緩衝區中仍殘留數據,進程將處於睡眠狀態,直 到(a)全部數據發送完且被對方確認,以後進行正常的終止序列(描述字訪問計數爲0)或(b)延遲時間到。此種狀況下,應用程序檢查close的返回值是很是重要的,若是在數據發送完並被確認前時間到,close將返回EWOULDBLOCK錯誤且套接口發送緩衝區中的任何數據都丟失。close的成功返回僅告訴咱們發送的數據(和FIN)已由對方TCP確認,它並不能告訴咱們對方應用進程是否已讀了數據。若是套接口設爲非阻塞的,它將不等待close完成。
更具體的描述以下:
一、若設置了SO_LINGER(亦即linger結構中的l_onoff域設爲非零),並設置了零超時間隔,則closesocket()不被阻塞當即執行,不管是否有排隊數據未發送或未被確認。這種關閉方式稱爲「強制」或「失效」關閉,由於套接口的虛電路當即被複位,且丟失了未發送的數據。在遠端的recv()調用將以WSAECONNRESET出錯。
二、若設置了SO_LINGER並肯定了非零的超時間隔,則closesocket()調用阻塞進程,直到所剩數據發送完畢或超時。這種關閉稱爲「優雅」或「從容」關閉。請注意若是套接口置爲非阻塞且SO_LINGER設爲非零超時,則closesocket()調用將以WSAEWOULDBLOCK錯誤返回。
三、若在一個流類套接口上設置了SO_DONTLINGER(也就是說將linger結構的l_onoff域設爲零),則closesocket()調用當即返回。可是,若是可能,排隊的數據將在套接口關閉前發送。請注意,在這種狀況下WINDOWS套接口實現將在一段不肯定的時間內保留套接口以及其餘資源,這對於想用因此套接口的應用程序來講有必定影響。
SO_DONTLINGER 若爲真,則SO_LINGER選項被禁止。
SO_LINGER延遲關閉鏈接 struct linger上面這兩個選項影響close行爲;
選項 間隔 關閉方式 等待關閉與否 SO_DONTLINGER 不關心 優雅 否 SO_LINGER 零 強制 否 SO_LINGER 非零 優雅 是