• <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>
            posts - 200, comments - 8, trackbacks - 0, articles - 0

            客戶端connect()函數(shù)阻塞問題解決

            Posted on 2014-08-23 17:01 鑫龍 閱讀(1659) 評論(0)  編輯 收藏 引用 所屬分類: linux編程

            轉(zhuǎn)自:http://zhucuicui.96986.blog.163.com/blog/static/5833370220136219016445/

            建立socket后默認connect()函數(shù)為阻塞連接狀態(tài),在大多數(shù)實現(xiàn)中,connect的超時時間在75s至幾分鐘之間,想要縮短超時時間,可解決問題的兩種方法:方法一、將socket句柄設(shè)置為非阻塞狀態(tài),方法二、采用信號處理函數(shù)設(shè)置阻塞超時控制。

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

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

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

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

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

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

            方法二、定義信號處理函數(shù):

             

            1. sigset(SIGALRM, u_alarm_handler);
            2. alarm(2);
            3. code = connect(socket_fd, (struct sockaddr*)&socket_st, sizeof(struct sockaddr_in));
            4. alarm(0);
            5. sigrelse(SIGALRM);

            首先定義一個中斷信號處理函數(shù)u_alarm_handler,用于超時后的報警處理,然后定義一個2秒的定時器,執(zhí)行connect,當系統(tǒng)connect成功,則系統(tǒng)正常執(zhí)行下去;如果connect不成功阻塞在這里,則超過定義的2秒后,系統(tǒng)會產(chǎn)生一個信號,觸發(fā)執(zhí)行u_alarm_handler函數(shù), 當執(zhí)行完u_alarm_handler后,程序?qū)⒗^續(xù)從connect的下面一行執(zhí)行下去。
            其中,處理函數(shù)可以如下定義,也可以加入更多的錯誤處理。

            1. void u_alarm_handler()
            2. {
            3. }
            一本久久久久久久| 久久Av无码精品人妻系列| 999久久久国产精品| 久久久久人妻一区精品果冻| 国内精品伊人久久久影院| 69国产成人综合久久精品| 久久成人精品| .精品久久久麻豆国产精品| 久久综合视频网站| 国产成人精品久久免费动漫| 中文字幕无码久久精品青草| 久久99精品国产99久久| 久久久久亚洲精品日久生情| 国产成人精品久久一区二区三区| 蜜臀久久99精品久久久久久| 亚洲精品乱码久久久久久| 精品久久久久中文字幕一区| 久久久久人妻精品一区| 综合久久一区二区三区| 久久精品国产黑森林| 91视频国产91久久久| 无码人妻精品一区二区三区久久| 精品熟女少妇aⅴ免费久久| 久久96国产精品久久久| 久久亚洲春色中文字幕久久久 | 99久久精品国产毛片| 亚洲av日韩精品久久久久久a| 久久久久久亚洲精品不卡| 99久久成人18免费网站| 久久不射电影网| 久久精品这里热有精品| 久久国产精品一区二区| 精品人妻久久久久久888| 久久精品一本到99热免费| 人妻无码中文久久久久专区| 国内精品人妻无码久久久影院导航 | 国产精品va久久久久久久| 久久九九有精品国产23百花影院| 国产V亚洲V天堂无码久久久| 人妻少妇久久中文字幕| 99久久精品国内|