1、什么是網絡超時檢測
我們都知道阻塞IO是進程如果有數據到來時,就會繼續執行,如果沒有數據到來時,就會無限制的阻塞下去,直到數據到來,比如:我在等一個人給我匯報一件事情,如果他一直沒有做完,我就要一直等下去,這樣就太浪費時間了,而如果我只給他10分鐘時間,如果他還是沒有完成,我就認為他事情沒有完成,然后繼續做我自己的事情,這就是超時檢測,而不是一直死等,那么如果是用于在網絡通信過程中進行超時檢測的話,就稱為網絡超時檢測
2、網絡超時檢測的必要性
1)避免進程在沒有數據時無限制的阻塞
2)當設定的時間到時,進程從原操作返回繼續執行
3、設置網絡超時檢測的方法
在網絡通信中,要做到超時檢測有三種方法:
1)設置socket的屬性SO_RCVTIMEO
2)用select檢測socket是否ready
3)設置定時器(timer),捕捉SIGALRM信號
接下來,我們分別去看怎么實現:
1)設置socket的屬性SO_RCVTIMEO
使用setsockopt設置socket的屬性為SO_RCVTIMEO,即接收超時
注意:如果是tcp服務器的話,會有兩個套接字,一個是監聽套接字一個是連接套接字,都可以進行設置
設置好了以后,定義結構體變量,設置超時時間,如下圖:
2)用select檢測socket是否ready
int select(int n, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds, struct timeval *timeout);這是select函數的原型,后一個參數,就是用來設置超時的,如果后一個參數為NULL的話,也就是沒有超時檢測,那么select監控的文件描述符都沒有準備好,select就會一直阻塞,直到有準備好的文件描述符,如要需要超時檢測,后一個參數就是要設置的超時時間,一旦超時,此時select就會返回0,表示沒有準備好的文件描述符,代碼可參考下圖:
但是要注意:我們在運行的時候發現select只有在第一次調用的時候,會阻塞等待,直 到超時,而后面”select timeout...”會不斷打印,根本就不會等待,這是因為在第一次調用select函數的時候,就將tv.tv_sec的值改為0了
3)設置定時器(timer),捕捉SIGALRM信號
這種方式是想通過信號的方式,中斷正在阻塞的系統調用的執行,也就是,首先在系統調用之前(比如recv函數)設置定時器,當時間到了以后,中斷recv函數,然后再繼續執行recv下面的操作,而不是像之前的如果沒有數據可讀,recv會一直阻塞
但要注意:在安裝信號的時候,不能使用signal函數,因為signal默認會繼續執行系統調用,也就是說當時間到了以后,又繼續執行recv函數了,還是會繼續阻塞,沒有達到我們想要的效果,所以這個時候我們就要使用比它功能更強大的sigaction了,如下圖:
下圖當沒有數據可讀時的運行結果:
另外注意:上面的代碼中SA_RESTART指的是重新執行被信號中斷的系統調用,如果沒有取反的話,就和signal的處理結果是一樣的了