• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            tqsheng

            go.....
            隨筆 - 366, 文章 - 18, 評論 - 101, 引用 - 0
            數據加載中……

            linux 客戶端 Socket 非阻塞connect編程(正文)


            linux 客戶端 Socket 非阻塞connect編程(正文)/*開發過程與源碼解析
              開發測試環境:虛擬機CentOS,windows網絡調試助手
              非阻塞模式有3種用途
              1.三次握手同時做其他的處理。connect要花一個往返時間完成,從幾毫秒的局域網到幾百毫秒或幾秒的廣域網。這段時間可能有一些其他的處理要執行,比如數據準備,預處理等。
              2.用這種技術建立多個連接。這在web瀏覽器中很普遍.
              3.由于程序用select等待連接完成,可以設置一個select等待時間限制,從而縮短connect超時時間。多數實現中,connect的超時時間在75秒到幾分鐘之間。有時程序希望在等待一定時間內結束,使用非阻塞connect可以防止阻塞75秒,在多線程網絡編程中,尤其必要。 例如有一個通過建立線程與其他主機進行socket通信的應用程序,如果建立的線程使用阻塞connect與遠程通信,當有幾百個線程并發的時候,由于網絡延遲而全部阻塞,阻塞的線程不會釋放系統的資源,同一時刻阻塞線程超過一定數量時候,系統就不再允許建立新的線程(每個進程由于進程空間的原因能產生的線程有限),如果使用非阻塞的connect,連接失敗使用select等待很短時間,如果還沒有連接后,線程立刻結束釋放資源,防止大量線程阻塞而使程序崩潰。
              目前connect非阻塞編程的普遍思路是:
              在一個TCP套接口設置為非阻塞后,調用connect,connect會在系統提供的errno變量中返回一個EINRPOCESS錯誤,此時TCP的三路握手繼續進行。之后可以用select函數檢查這個連接是否建立成功。以下實驗基于unix網絡編程和網絡上給出的普遍示例,在經過大量測試之后,發現其中有很多方法,在linux中,并不適用。
              我先給出了重要源碼的逐步分析,在最后給出完整的connect非阻塞源碼。
              1.首先填寫套接字結構,包括遠程的ip,通信端口如下: */
              struct sockaddr_in serv_addr;
              serv_addr.sin_family=AF_INET;
              serv_addr.sin_port=htons(9999);
              serv_addr.sin_addr.s_addr = inet_addr("58.31.231.255"); //inet_addr轉換為網絡字節序
              bzero(&(serv_addr.sin_zero),8);
              // 2.建立socket套接字:
              if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
              {
              perror("socket creat error");
              return 1;
              }
              // 3.將socket建立為非阻塞,此時socket被設置為非阻塞模式
              flags = fcntl(sockfd,F_GETFL,0);//獲取建立的sockfd的當前狀態(非阻塞)
              fcntl(sockfd,F_SETFL,flags|O_NONBLOCK);//將當前sockfd設置為非阻塞
              /*4. 建立connect連接,此時socket設置為非阻塞,connect調用后,無論連接是否建立立即返回-1,同時將errno(包含errno.h就可以直接使用)設置為EINPROGRESS, 表示此時tcp三次握手仍舊進行,如果errno不是EINPROGRESS,則說明連接錯誤,程序結束。
              當客戶端和服務器端在同一臺主機上的時候,connect回馬上結束,并返回0;無需等待,所以使用goto函數跳過select等待函數,直接進入連接后的處理部分。*/
              if ( ( n = connect( sockfd, ( struct sockaddr *)&serv_addr , sizeof(struct sockaddr)) ) < 0 )
              {
              if(errno != EINPROGRESS) return 1;
              }
              if(n==0)
              {
              printf("connect completed immediately");
              goto done;
              }
              /* 5.設置等待時間,使用select函數等待正在后臺連接的connect函數,這里需要說明的是使用select監聽socket描述符是否可讀或者可寫,如果只可寫,說明連接成功,可以進行下面的操作。如果描述符既可讀又可寫,分為兩種情況,第一種情況是socket連接出現錯誤(不要問為什么,這是系統規定的,可讀可寫時候有可能是connect連接成功后遠程主機斷開了連接close(socket)),第二種情況是connect連接成功,socket讀緩沖區得到了遠程主機發送的數據。需要通過connect連接后返回給errno的值來進行判定,或者通過調用 getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&error,&len); 函數返回值來判斷是否發生錯誤,這里存在一個可移植性問題,在solaris中發生錯誤返回-1,但在其他系統中可能返回0.我首先按unix網絡編程的源碼進行實現。如下:*/
              FD_ZERO(&rset);
              FD_SET(sockfd,&rset);
              wset = rset;
              tval.tv_sec = 0;
              tval.tv_usec = 300000;
              int error;
              socklen_t len;
              if(( n = select(sockfd+1, &rset, &wset, NULL,&tval)) <= 0)
              {
              printf("time out connect error");
              close(sockfd);
              return -1;
              }
              If ( FD_ISSET(sockfd,&rset) || FD_ISSET(sockfd,&west) )
              {
              len = sizeof(error);
              if( getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&error,&len) <0)
              return 1;
              }
              /* 這里我測試了一下,按照unix網絡編程的描述,當網絡發生錯誤的時候,getsockopt返回-1,return -1,程序結束。網絡正常時候返回0,程序繼續執行。
              可是我在linux下,無論網絡是否發生錯誤,getsockopt始終返回0,不返回-1,說明linux與unix網絡編程還是有些細微的差別。就是說當socket描述符可讀可寫的時候,這段代碼不起作用。不能檢測出網絡是否出現故障。
              我測試的方法是,當調用connect后,sleep(2)休眠2秒,借助這兩秒時間將網絡助手斷開連接,這時候select返回2,說明套接口可讀又可寫,應該是網絡連接的出錯情況。
              此時,getsockopt返回0,不起作用。獲取errno的值,指示為EINPROGRESS,沒有返回unix網絡編程中說的ENOTCONN,EINPROGRESS表示正在試圖連接,不能表示網絡已經連接失敗。
            針對這種情況,unix網絡編程中提出了另外3種方法,這3種方法,也是網絡上給出的常用的非阻塞connect示例:
              a.再調用connect一次。失敗返回errno是EISCONN說明連接成功,表示剛才的connect成功,否則返回失敗。 代碼如下:*/
              int connect_ok;
              connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr) );
              switch (errno)
              {
              case EISCONN: //connect ok
              printf("connect OK \n");
              connect_ok = 1;
              break;
              case EALREADY:
              connect_0k = -1
              break;
              case EINPROGRESS: // is connecting, need to check again
              connect_ok = -1
              break;
              default: 
              printf("connect fail err=%d \n",errno);
              connect_ok = -1;
              break;
              }
              /*如程序所示,根據再次調用的errno返回值將connect_ok的值,來進行下面的處理,connect_ok為1繼續執行其他操作,否則程序結束。
              但這種方法我在linux下測試了,當發生錯誤的時候,socket描述符(我的程序里是sockfd)變成可讀且可寫,但第二次調用connect 后,errno并沒有返回EISCONN,,也沒有返回連接失敗的錯誤,仍舊是EINPROGRESS,而當網絡不發生故障的時候,第二次使用 connect連接也返回EINPROGRESS,因此也無法通過再次connect來判斷連接是否成功。
              b.unix網絡編程中說使用read函數,如果失敗,表示connect失敗,返回的errno指明了失敗原因,但這種方法在linux上行不通,linux在socket描述符為可讀可寫的時候,read返回0,并不會置errno為錯誤。
               c.unix網絡編程中說使用getpeername函數,如果連接失敗,調用該函數后,通過errno來判斷第一次連接是否成功,但我試過了,無論網絡連接是否成功,errno都沒變化,都為EINPROGRESS,無法判斷。
              悲哀啊,即使調用getpeername函數,getsockopt函數仍舊不行。
              綜上方法,既然都不能確切知道非阻塞connect是否成功,所以我直接當描述符可讀可寫的情況下進行發送,通過能否獲取服務器的返回值來判斷是否成功。(如果服務器端的設計不發送數據,那就悲哀了。)
              程序的書寫形式出于可移植性考慮,按照unix網絡編程推薦寫法,使用getsocketopt進行判斷,但不通過返回值來判斷,而通過函數的返回參數來判斷。
              6. 用select查看接收描述符,如果可讀,就讀出數據,程序結束。在接收數據的時候注意要先對先前的rset重新賦值為描述符,因為select會對 rset清零,當調用select后,如果socket沒有變為可讀,則rset在select會被置零。所以如果在程序中使用了rset,最好在使用時候重新對rset賦值。
              程序如下:*/
              FD_ZERO(&rset);
              FD_SET(sockfd,&rset);//如果前面select使用了rset,最好重新賦值
              if( ( n = select(sockfd+1,&rset,NULL, NULL,&tval)) <= 0 )
              {
              close(sockfd);
              return -1;
              } 
              if ((recvbytes=recv(sockfd, buf, 1024, 0)) ==-1)
              {
              perror("recv error!");
              close(sockfd);
              return 1;
              }
              printf("receive num %d\n",recvbytes);
              printf("%s\n",buf);
              */
            非阻塞connect

            在一個TCP套接口被設置為非阻塞之后調用connect,connect會立即返回EINPROGRESS錯誤,表示連接操作正在進行中,但是仍未完成;同時TCP的三路握手操作繼續進行;在這之后,我們可以調用select來檢查這個鏈接是否建立成功;非阻塞connect有三種用途:
            1.我們可以在三路握手的同時做一些其它的處理.connect操作要花一個往返時間完成,而且可以是在任何地方,從幾個毫秒的局域網到幾百毫秒或幾秒的廣域網.在這段時間內我們可能有一些其他的處理想要執行;
            2.可以用這種技術同時建立多個連接.在Web瀏覽器中很普遍;
            3.由于我們使用select來等待連接的完成,因此我們可以給select設置一個時間限制,從而縮短connect的超時時間.在大多數實現中,connect的超時時間在75秒到幾分鐘之間.有時候應用程序想要一個更短的超時時間,使用非阻塞connect就是一種方法;
            非阻塞connect聽起來雖然簡單,但是仍然有一些細節問題要處理:
            1.即使套接口是非阻塞的,如果連接的服務器在同一臺主機上,那么在調用connect建立連接時,連接通常會立即建立成功.我們必須處理這種情況;
            2.源自Berkeley的實現(和Posix.1g)有兩條與select和非阻塞IO相關的規則:
              A:當連接建立成功時,套接口描述符變成可寫;
              B:當連接出錯時,套接口描述符變成既可讀又可寫;
              注意:當一個套接口出錯時,它會被select調用標記為既可讀又可寫;

            非阻塞connect有這么多好處,但是處理非阻塞connect時會遇到很多可移植性問題;

            處理非阻塞connect的步驟:
            第一步:創建socket,返回套接口描述符;
            第二步:調用fcntl把套接口描述符設置成非阻塞;
            第三步:調用connect開始建立連接;
            第四步:判斷連接是否成功建立;
                   A:如果connect返回0,表示連接簡稱成功(服務器可客戶端在同一臺機器上時就有可能發生這種情況);
                   B:調用select來等待連接建立成功完成;
                     如果select返回0,則表示建立連接超時;我們返回超時錯誤給用戶,同時關閉連接,以防止三路握手操作繼續進行下去;
                     如果select返回大于0的值,則需要檢查套接口描述符是否可讀或可寫;如果套接口描述符可讀或可寫,則我們可以通過調用getsockopt來得到套接口上待處理的錯誤(SO_ERROR),如果連接建立成功,這個錯誤值將是0,如果建立連接時遇到錯誤,則這個值是連接錯誤所對應的errno值(比如:ECONNREFUSED,ETIMEDOUT等).
            "讀取套接口上的錯誤"是遇到的第一個可移植性問題;如果出現問題,getsockopt源自Berkeley的實現是返回0,等待處理的錯誤在變量errno中返回;但是Solaris會讓getsockopt返回-1,errno置為待處理的錯誤;我們對這兩種情況都要處理;

            這樣,在處理非阻塞connect時,在不同的套接口實現的平臺中存在的移植性問題,首先,有可能在調用select之前,連接就已經建立成功,而且對方的數據已經到來.在這種情況下,連接成功時套接口將既可讀又可寫.這和連接失敗時是一樣的.這個時候我們還得通過getsockopt來讀取錯誤值;這是第二個可移植性問題;
            移植性問題總結:
            1.對于出錯的套接口描述符,getsockopt的返回值源自Berkeley的實現是返回0,待處理的錯誤值存儲在errno中;而源自Solaris的實現是返回0,待處理的錯誤存儲在errno中;(套接口描述符出錯時調用getsockopt的返回值不可移植)
            2.有可能在調用select之前,連接就已經建立成功,而且對方的數據已經到來,在這種情況下,套接口描述符是既可讀又可寫;這與套接口描述符出錯時是一樣的;(怎樣判斷連接是否建立成功的條件不可移植)

            這樣的話,在我們判斷連接是否建立成功的條件不唯一時,我們可以有以下的方法來解決這個問題:
            1.調用getpeername代替getsockopt.如果調用getpeername失敗,getpeername返回ENOTCONN,表示連接建立失敗,我們必須以SO_ERROR調用getsockopt得到套接口描述符上的待處理錯誤;
            2.調用read,讀取長度為0字節的數據.如果read調用失敗,則表示連接建立失敗,而且read返回的errno指明了連接失敗的原因.如果連接建立成功,read應該返回0;
            3.再調用一次connect.它應該失敗,如果錯誤errno是EISCONN,就表示套接口已經建立,而且第一次連接是成功的;否則,連接就是失敗的;

            被中斷的connect:
            如果在一個阻塞式套接口上調用connect,在TCP的三路握手操作完成之前被中斷了,比如說,被捕獲的信號中斷,將會發生什么呢?假定connect不會自動重啟,它將返回EINTR.那么,這個時候,我們就不能再調用connect等待連接建立完成了,如果再次調用connect來等待連接建立完成的話,connect將會返回錯誤值EADDRINUSE.在這種情況下,應該做的是調用select,就像在非阻塞式connect中所做的一樣.然后,select在連接建立成功(使套接口描述符可寫)或連接建立失敗(使套接口描述符既可讀又可寫)時返回;

             
             

            posted on 2012-12-18 11:44 tqsheng 閱讀(5239) 評論(2)  編輯 收藏 引用

            評論

            # re: linux 客戶端 Socket 非阻塞connect編程(正文)  回復  更多評論   

            對于面向連接的socket類型(SOCK_STREAM,SOCK_SEQPACKET)在讀寫數據之前必須建立連接,首先服務器端socket必須在一個客戶端知道的地址進行監聽,也就是創建socket之后必須調用bind綁定到一個指定的地址,然后調用int listen(int sockfd, int backlog);進行監聽。此時服務器socket允許客戶端進行連接,backlog提示沒被accept的客戶連接請求隊列的大小,系統決定實際的值,最大值定義為SOMAXCONN在頭文件<sys/socket.h>里面。如果某種原因導致服務器端進程未及時accpet客戶連接而導致此隊列滿了的話則新的客戶端連接請求被拒絕(在工作中遇到過此情況,IONA ORBIX(CORBA中間件)由于沒有配置超時時間結果在WIFI網絡中傳輸數據出現異常情況一直阻塞而無機會調用accept接受新的客戶請求,于是最終隊列滿導致新的客戶連接被拒絕)。
              調用listen之后當有客戶端連接到達的時候調用int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);接受客戶端連接建立起連接返回用于連接數據傳送的socket描述符,進行監聽的socket可以用于繼續監聽客戶端的連接請求,返回的socket描述符跟監聽的socket類型一致。如果addr不為NULL,則客戶端發起連接請求的socket地址信息會通過addr進行返回。如果監聽的socket描述符為阻塞模式則accept一直會阻塞直到有客戶發起連接請求,如果監聽的socket描述符為非阻塞模式則如果當前沒有可用的客戶連接請求,則返回-1(errno設置為EAGAIN)。可以使用select函數對監聽的socket描述符進行多路分離,如果有客戶連接請求則select將監聽的socket描述符設置為可讀(注意,如果監聽的socket為阻塞模式而使用select進行多路分離則可能造成select返回可讀但是調用accept會被阻塞住的情況,原因是在調用accept之前客戶端可能主動關閉連接或者發送RST異常關閉連接,因此select最好跟非阻塞socket搭配使用)。
              客戶端調用int connect(int sockfd, const struct sockaddr *addr, socklen_t len);發起對服務器的socket的連接請求,如果客戶端socket描述符為阻塞模式則會一直阻塞到連接建立或者連接失敗(注意阻塞模式的超時時間可能為75秒到幾分鐘之間),而如果為非阻塞模式,則調用connect之后如果連接不能馬上建立則返回-1(errno設置為EINPROGRESS,注意連接也可能馬上建立成功比如連接本機的服務器進程),如果沒有馬上建立返回,此時TCP的三路握手動作在背后繼續,而程序可以做其他的東西,然后調用select檢測非阻塞connect是否完成(此時可以指定select的超時時間,這個超時時間可以設置為比connect的超時時間短),如果select超時則關閉socket,然后可以嘗試創建新的socket重新連接,如果select返回非阻塞socket描述符可寫則表明連接建立成功,如果select返回非阻塞socket描述符既可讀又可寫則表明連接出錯(注意:這兒必須跟另外一種連接正常的情況區分開來,就是連接建立好了之后,服務器端發送了數據給客戶端,此時select同樣會返回非阻塞socket描述符既可讀又可寫,這時可以通過以下方法區分:
              1.調用getpeername獲取對端的socket地址.如果getpeername返回ENOTCONN,表示連接建立失敗,然后用SO_ERROR調用getsockopt得到套接口描述符上的待處理錯誤;
              2.調用read,讀取長度為0字節的數據.如果read調用失敗,則表示連接建立失敗,而且read返回的errno指明了連接失敗的原因.如果連接建立成功,read應該返回0;
              3.再調用一次connect.它應該失敗,如果錯誤errno是EISCONN,就表示套接口已經建立,而且第一次連接是成功的;否則,連接就是失敗的;
              對于無連接的socket類型(SOCK_DGRAM),客戶端也可以調用connect進行連接,此連接實際上并不建立類似SOCK_STREAM的連接,而僅僅是在本地保存了對端的地址,這樣后續的讀寫操作可以默認以連接的對端為操作對象。
              當對端機器crash或者網絡連接被斷開(比如路由器不工作,網線斷開等),此時發送數據給對端然后讀取本端socket會返回ETIMEDOUT或者EHOSTUNREACH 或者ENETUNREACH(后兩個是中間路由器判斷服務器主機不可達的情況)。
              當對端機器crash之后又重新啟動,然后客戶端再向原來的連接發送數據,因為服務器端已經沒有原來的連接信息,此時服務器端回送RST給客戶端,此時客戶端讀本地端口返回ECONNRESET錯誤。
              當服務器所在的進程正常或者異常關閉時,會對所有打開的文件描述符進行close,因此對于連接的socket描述符則會向對端發送FIN分節進行正常關閉流程。對端在收到FIN之后端口變得可讀,此時讀取端口會返回0表示到了文件結尾(對端不會再發送數據)。 
              當一端收到RST導致讀取socket返回ECONNRESET,此時如果再次調用write發送數據給對端則觸發SIGPIPE信號,信號默認終止進程,如果忽略此信號或者從SIGPIPE的信號處理程序返回則write出錯返回EPIPE。
              可以看出只有當本地端口主動發送消息給對端才能檢測出連接異常中斷的情況,搭配select進行多路分離的時候,socket收到RST或者FIN時候,select返回可讀(心跳消息就是用于檢測連接的狀態)。也可以使用socket的KEEPLIVE選項,依賴socket本身偵測socket連接異常中斷的情況。
              發送socket數據有以下方法:
              調用ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);,只能用于建立好了連接的socket(面向連接的SOCK_STREAM或者調用了connect的SOCK_DGRAM)。flags取值如下:
              MSG_DONTROUTE 對數據不進行路由
              MSG_DONTWAIT 不等待數據發送完成
              MSG_EOR 數據包結尾
              MSG_OOB 帶外數據
              注意send函數成功返回并不代表對端一定收到了發送的消息,另外對于數據報協議如果發送的數據大于一個數據報長度則發送失敗(errno設置為EMSGSIZE)。
            2012-12-18 11:46 | tqsheng

            # re: linux 客戶端 Socket 非阻塞connect編程(正文)  回復  更多評論   

            如何設置socket的Connect超時(linux)

            今天發現自己的系統存在很嚴重缺陷,當前臺關閉的時候后臺就無法正常工作,原因 很好定位,后臺的socket連接超時時間過長,系統默認時間好像是75秒,于是找資料,根據下邊文章中的內容解決了,把超時時間設為5秒后,感覺好多 了。看來還有好多東西需要慢慢挖掘阿!


            如何設置socket的Connect超時(linux)

            [From]http://dev.cbw.com/c/c/200510195601_4292587.shtml

            1. 首先將標志位設為Non-blocking模式,準備在非阻塞模式下調用connect函數
            2.調用connect,正常情況下,因為TCP三次 握手需要一些時間;而非阻塞調用只要不能立即完成就會返回錯誤,所以這里會返回EINPROGRESS,表示在建立連接但還沒有完成。
            3.在讀套 接口描述符集(fd_set rset)和寫套接口描述符集(fd_set wset)中將當前套接口置位(用FD_ZERO()、FD_SET()宏),并設置好超時時間(struct timeval *timeout)
            4. 調用select( socket, &rset, &wset, NULL, timeout )
            返回0表示connect超 時
            如果你設置的超時時間大于75秒就沒有必要這樣做了,因為內核中對connect有超時限制就是75秒。


            [From]http://www.ycgczj.com.cn/34733.html
            網 絡編程中socket的分量我想大家都很清楚了,socket也就是套接口,在套接口編程中,提到超時的概念,我們一下子就能想到3個:發送超時,接收超 時,以及select超時(注: select函數并不是只用于套接口的,但是套接口編程中用的比較多),在connect到目標主機的時候,這個超時是不由我們來設置的。不過正常情況下 這個超時都很長,并且connect又是一個阻塞方法,一個主機不能連接,等著connect返回還能忍受,你的程序要是要試圖連接多個主機,恐怕遇到多 個不能連接的主機的時候,會塞得你受不了的。我也廢話少說,先說說我的方法,如果你覺得你已掌握這種方法,你就不用再看下去了,如果你還不了解,我愿意與 你分享。本文是已在Linux下的程序為例子,不過拿到Windows中方法也是一樣,無非是換幾個函數名字罷了。
            Linux中要給 connect設置超時,應該是有兩種方法的。一種是該系統的一些參數,這個方法我不講,因為我講不清楚:P,它也不是編程實現的。另外一種方法就是變相 的實現connect的超時,我要講的就是這個方法,原理上是這樣的:
            1.建立socket
            2.將該socket設置為非阻塞模式
            3. 調用connect()
            4.使用select()檢查該socket描述符是否可寫(注意,是可寫)
            5.根據select()返回的結果 判斷connect()結果
            6.將socket設置為阻塞模式(如果你的程序不需要用阻塞模式的,這步就省了,不過一般情況下都是用阻塞模式的, 這樣也容易管理)
            如果你對網絡編程很熟悉的話,其實我一說出這個過程你就知道怎么寫你的程序了,下面給出我寫的一段程序,僅供參考。
            /******************************
            * Time out for connect()
            * Write by Kerl W
            ******************************/
            #include <sys/socket.h>
            #include <sys/types.h>
            #define TIME_OUT_TIME 20 //connect超時時間20秒
            int main(int argc , char **argv)
            {
            ………………
            int sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if(sockfd < 0) exit(1);
            struct sockaddr_in serv_addr;
            ………//以服務器地址填充結構serv_addr
            int error=-1, len;
            len = sizeof(int);
            timeval tm;
            fd_set set;
            unsigned long ul = 1;
            ioctl(sockfd, FIONBIO, &ul); //設置為非阻塞模式
            bool ret = false;
            if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
            {
            tm.tv_set = TIME_OUT_TIME;
            tm.tv_uset = 0;
            FD_ZERO(&set);
            FD_SET(sockfd, &set);
            if( select(sockfd+1, NULL, &set, NULL, &tm) > 0)
            {
            getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
            if(error == 0) ret = true;
            else ret = false;
            } else ret = false;
            }
            else ret = true;
            ul = 0;
            ioctl(sockfd, FIONBIO, &ul); //設置為阻塞模式
            if(!ret)
            {
            close( sockfd );
            fprintf(stderr , "Cannot Connect the server!n");
            return;
            }
            fprintf( stderr , "Connected!n");
            //下面還可以進行發包收包操作
            ……………
            }

            以上代碼片段, 僅供參考,也是為初學者提供一些提示,主要用到的幾個函數,select, ioctl, getsockopt都可以找到相關資料,具體用法我這里就不贅述了,你只需要在linux中輕輕的敲一個man <函數名>就能夠看到它的用法。
            此外我需要說明的幾點是,雖然我們用ioctl把套接口設置為非阻塞模式,不過select本身是阻 塞的,阻塞的時間就是其超時的時間由調用select 的時候的最后一個參數timeval類型的變量指針指向的timeval結構變量來決定的,timeval結構由一個表示秒數的和一個表示微秒數 (long類型)的成員組成,一般我們設置了秒數就行了,把微妙數設為0(注:1秒等于100萬微秒)。而select函數另一個值得一提的參數就是上面 我們用到的fd_set類型的變量指針。調用之前,這個變量里面存了要用select來檢查的描述符,調用之后,針對上面的程序這里面是可寫的描述符,我 們可以用宏FD_ISSET來檢查某個描述符是否在其中。由于我這里只有一個套接口描述符,我就沒有使用FD_ISSET宏來檢查調用select之后這 個sockfd是否在set里面,其實是需要加上這個判斷的。不過我用了getsockopt來檢查,這樣才可以判斷出這個套接口是否是真的連接上了,因 為我們只是變相的用select來檢查它是否連接上了,實際上select檢查的是它是否可寫,而對于可寫,是針對以下三種條件任一條件滿足時都表示可寫 的:
            1)套接口發送緩沖區中的可用控件字節數大于等于套接口發送緩沖區低潮限度的當前值,且或者i)套接口已連接,或者ii)套接口不要求連接 (UDP方式的)
            2)連接的寫這一半關閉。
            3)有一個套接口錯誤待處理。
            這樣,我們就需要用getsockopt函數來獲取套接 口目前的一些信息來判斷是否真的是連接上了,沒有連接上的時候還能給出發生了什么錯誤,當然我程序中并沒有標出那么多狀態,只是簡單的表示可連接/不可連 接。
            下面我來談談對這個程序測試的結果。我針對3種情形做了測試:
            1. 目標機器網絡正常的情況
            可以連接到目標主機,并能成功以 阻塞方式進行發包收包作業。
            2. 目標機器網絡斷開的情況
            在等待設置的超時時間(上面的程序中為20秒)后,顯示目標主機不能連接。
            3. 程序運行前斷開目標機器網絡,超時時間內,恢復目標機器的網絡
            在恢復目標主機網絡連接之前,程序一只等待,恢復目標主機后,程序顯示連接目標主 機成功,并能成功以阻塞方式進行發包收包作業。
            以上各種情況的測試結果表明,這種設置connect超時的方法是完全可行的。我自己是把這種設置 了超時的connect封裝到了自己的類庫,用在一套監控系統中,到目前為止,運行還算正常。這種編程實現的connect超時比起修改系統參數的那種方 法的有點就在于它只用于你的程序之中而不影響系統。
            2012-12-18 11:47 | tqsheng
            性高湖久久久久久久久AAAAA| 国产成年无码久久久久毛片| 九九久久精品国产| 亚洲精品无码久久久久AV麻豆| 思思久久99热只有频精品66| 久久精品夜夜夜夜夜久久| 伊人久久精品线影院| 日韩精品久久久久久久电影| 国产欧美久久一区二区| 色欲综合久久躁天天躁| 久久99热狠狠色精品一区| 久久综合伊人77777麻豆| 99久久超碰中文字幕伊人| 性做久久久久久久久久久| 久久狠狠色狠狠色综合| 久久夜色精品国产亚洲| 国产AⅤ精品一区二区三区久久| 国内精品久久久久久久久电影网| 99久久国产免费福利| 77777亚洲午夜久久多喷| 精品国产乱码久久久久久人妻| 91久久精品国产免费直播| 久久人人爽爽爽人久久久| 久久久久久精品无码人妻| 日韩va亚洲va欧美va久久| 一本大道久久a久久精品综合| 欧美熟妇另类久久久久久不卡 | 久久精品亚洲中文字幕无码麻豆| 久久综合亚洲色HEZYO国产| 国产毛片久久久久久国产毛片 | 久久久无码一区二区三区| 无码任你躁久久久久久老妇App| 久久成人精品| 人妻无码精品久久亚瑟影视| 国产真实乱对白精彩久久| 91精品国产91久久| 99久久国产亚洲高清观看2024 | 国产精品久久亚洲不卡动漫| 精品少妇人妻av无码久久| 久久久久无码精品国产| 69久久精品无码一区二区|