5種TCP連線中出現RST的情況。連線復位Reset a connection.
應該沒有人會質疑,現在是一個網路時代了。應該不少程式設計師在程式設計中需要考慮多機、區域網、廣域網的各種問題。所以網路知識也是避免不了學習的。而且筆者一直覺得TCP/IP網路知識在一個程式設計師知識體系中必需佔有一席之地的。
在TCP協議中RST表示復位,用來異常的關閉連線,在TCP的設計中它是不可或缺的。傳送RST包關閉連線時,不必等緩衝區的包都發出去,直接就丟棄快取區的包傳送RST包。而接收端收到RST包後,也不必傳送ACK包來確認。
其實在網路程式設計過程中,各種RST錯誤其實是比較難排查和找到原因的。下面我列出幾種會出現RST的情況。
1 埠未開啟
伺服器程式埠未開啟而客戶端來連線。這種情況是最為常見和好理解的一種了。去telnet一個未開啟的TCP的埠可能會出現這種錯誤。這個和作業系統的實現有關。在某些情況下,作業系統也會完全不理會這些發到未開啟埠請求。
比如在下面這種情況下,主機241向主機114傳送一個SYN請求,表示想要連線主機114的40000埠,但是主機114上根本沒有開啟40000這個埠,於是就向主機241傳送了一個RST。這種情況很常見。特別是伺服器程式core dump之後重啟之前連續出現RST的情況會經常發生。
當然在某些作業系統的主機上,未必是這樣的表現。比如向一臺WINDOWS7的主機發送一個連線不存在的埠的請求,這臺主機就不會迴應。
2 請求超時
曾經遇到過這樣一個情況:一個客戶端連線伺服器,connect返回-1並且error=EINPROGRESS。 直接telnet發現網路連線沒有問題。ping沒有出現丟包。用抓包工具檢視,客戶端是在收到伺服器發出的SYN之後就莫名其妙的傳送了RST。
比如像下面這樣:
有89、27兩臺主機。主機89向主機27傳送了一個SYN,表示希望連線8888埠,主機27迴應了主機89一個SYN表示可以連線。但是主機27卻很不友好,莫名其妙的傳送了一個RST表示我不想連線你了。
後來經過排查發現,在主機89上的程式在建立了socket之後,用setsockopt的SO_RCVTIMEO選項設定了recv的超時時間為100ms。而我們看上面的抓包結果表示,從主機89發出SYN到接收SYN的時間多達110ms。(從15:01:27.799961到15:01:27.961886, 小數點之後的單位是微秒)。因此主機89上的程式認為接收超時,所以傳送了RST拒絕進一步傳送資料。
3 提前關閉
關於TCP,我想我們在教科書裡都讀到過一句話,'TCP是一種可靠的連線'。 而這可靠有這樣一種含義,那就是作業系統接收到的來自TCP連線中的每一個位元組,我都會讓應用程式接收到。如果應用程式不接收怎麼辦?你猜對了,RST。
看兩段程式:
01 |
//server.c |
02 |
03 |
int main( int argc, char **
argv) |
04 |
{ |
05 |
int listen_fd,
real_fd; |
06 |
struct sockaddr_in
listen_addr, client_addr; |
07 |
socklen_t
len = sizeof ( struct sockaddr_in); |
08 |
listen_fd
= socket(AF_INET, SOCK_STREAM, 0); |
09 |
if (listen_fd
== -1) |
10 |
{ |
11 |
perror ( "socket
failed " ); |