天天看點

66-套接字逾時(alarm)

我們知道,使用 read 或 recvfrom 函數從 socket 上讀取資料時,可能會導緻阻塞。特别是 UDP 伺服器,稍有不甚,資料報就丢失就會直接導緻程式假死(阻塞在 recvfrom)。

還有 connect 函數,如果去連接配接一個網絡上不存在的主機,需要等待很長時間,可能長達 2 分鐘。如果我們能控制它在指定的時間内連接配接不上,就直接報錯,行不行呢?

很可惜,上面的問題 linux 并未直接提供帶逾時的函數幫我們解決,是以我們需要一些奇技淫巧來完成上面的功能。

1. 如何設定逾時?

常用的有 3 種方法來設定逾時:

  • 1) 使用 alarm 函數,一旦到期就産生 SIGALRM 信号,打斷正在執行的系統調用。
  • 2) 使用 select 函數。
  • 3) 使用套接字選項 SO_RCVTIMEO 和 SO_SNDTIMEO.

關于這幾個方案,有幾點要說明:

  • 三個技術都可用于輸入和輸出(read 和 write)
  • 對于阻塞的 connect 隻能使用方案一,對于非阻塞的 connect,隻能使用 select.
  • 方案三對 connect 不适用。
  • 方案一和二适用于任何描述符,而方案三僅适用于套接字描述符。

2. 使用 alarm 設定逾時

它的僞代碼如下,這裡以 connect 為例:

void sighandler(int sig) {
  // 信号處理函數,什麼也不用做
  return;
}

int connect_timeo(/*...*/, int nsec) {
   // 注冊信号
   registSignal(SIGALRM, sighandler, &oldhandler);
   // 開啟一個 nsec 秒的 alarm
   alarm(nsec);
   ret = connect(/*...*/);
   // 如果被信号打斷,則表示逾時
   if (ret < 0 && errno == EINTR) {
     errno = ETIMEDOUT;
   }
   // 删除 alarm
   alarm(0);
   registSignal(SIGALRM, oldhandler);
   return      

3. 實驗代碼

代碼托管在 gitos 上,請使用下面的指令擷取:

git clone https://git.oschina.net/ivan_allen/unp.git      

如果你已經 clone 過這個代碼了,請使用 ​

​git pull​

​​ 更新一下。本節程式所使用的程式路徑是 ​

​unp/program/advcio/conn_timeo/conn_timeo.cc​

​​,另一個使用 alarm 為 recvfrom 設定逾時的例子是 ​

​unp/program/advcio/read_timeo/read_timeo_alarm​

​.

4. 實驗結果

ip 位址 192.168.166.4 在網絡上并不存在,直接連接配接它就會導緻 connect 函數阻塞,一直在發送 SYN 請求。

  • 不使用逾時的 connect
66-套接字逾時(alarm)

圖1 不使用逾時的 connect

可以看到,預設的 connect 函數逾時值約為 127 秒。

  • 使用逾時的 connect

通過指令行 ​

​--timeo​

​ 選項傳遞逾時值給 connec_timeo 函數。

66-套接字逾時(alarm)

圖2 使用逾時的 connect

5. 總結

  • 掌握使用 alarm 編寫逾時函數的方法

繼續閱讀