connect()函數阻塞問題

方法一:採用select

在學習嵌入式Linux網絡編程中,不少同窗都發現了一個問題,那就是調用connect函數時,若是服務端關閉,客戶 端調用connect()函數時,發現阻塞在那裏,並且利用ctrl+c信號去中止客戶端程序時,須要等待一個較爲長的時間才能響應了,這個時間若是你們 細心會發現,每次都是75秒的時間。那麼有沒有什麼比較好的辦法,能夠以用戶能接受的一個時間響應來中止掉一個正在connect鏈接的客戶端那?好比我 們在作一個網絡控制檯的程序,用戶須要隨時能夠中止掉任何一個網絡服務鏈接,那麼對於這樣一個須要等待75秒時間才能反饋出服務狀態的程序,用戶是沒法接 受的。編程

對於如何解決這個問題,咱們能夠分析下,要想完成用戶在一個能接受的時間裏迅速反饋出服務 端已經關閉的狀態,那麼咱們的程序應該作到在一個規定的時間片內,能夠捕獲到用戶發出的控制狀態,而後處理用戶的需求。那麼要作到能夠在規定的時間片內捕 獲用戶的控制狀態,就必須禁止讓咱們的connect()函數阻塞75秒的狀況發生,也就是說,要讓connect()函數變爲非阻塞狀態才行。服務器

好了,如今解決問題的關鍵就是如何把connect變爲非阻塞狀態了,咱們知道,socket編程的操做對象是socket,而socket他又屬於系統描述符類型,那麼對於系統描述符,咱們是怎麼操做他變爲非阻塞的那?是利用fcntl()函數或者ioctl()函數。網絡

想到這裏,好像問題應該已經解決了,可是咱們調試發現,在服務端出現錯誤的時候,connect確實立刻返回,可是,若是服務端正確那,connect仍是立刻返回,這樣,咱們沒法判斷connect函數是否成功了,那這個問題又該如何解決呢?socket

咱們是否想到了一個select函數那,他具有監聽文件描述符的功能,若是咱們把以前的socket讓select監聽他是否可寫,是否是問題也就解決了。函數

好了,那麼咱們總結下整個思路:學習

1.創建socket
        2.將該socket設置爲非阻塞模式
        3.調用connect()
        4.使用select()檢查該socket描述符是否可寫
        5.根據select()返回的結果判斷connect()結果
        6.將socket設置爲阻塞模式(若是你的程序不須要用阻塞模式,這步就省了,不過通常狀況都是用阻塞模式,這樣容易管理)
spa

那麼根據上面的6個步驟,咱們寫一個簡單的模塊程序來調試看下:調試

{
                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); //設置爲阻塞模式
                //下面還能夠進行發包收包操做
                ……………
        }
方法二:定義信號處理函數
  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);
     首先定義一箇中斷信號處理函數u_alarm_handler,用於超時後的報警處理,而後定義一個2秒的定時器,執行connect,當系統 connect成功,則系統正常執行下去;若是connect不成功阻塞在這裏,則超過定義的2秒後,系統會產生一個信號,觸發執行 u_alarm_handler函數, 當執行完u_alarm_handler後,程序將繼續從connect的下面一行執行下去。
     其中,處理函數能夠以下定義,也能夠加入更多的錯誤處理。
    
  1. void u_alarm_handler()
  2. {
  3. }
相關文章
相關標籤/搜索