應該沒有人會質疑,現在是乙個網路時代了。應該不少程式設計師在程式設計中需要考慮多機、區域網、廣域網的各種問題。所以網路知識也是避免不了學習的。而且筆者一直覺得tcp/ip網路知識在乙個程式設計師知識體系中必需占有一席之地的。
在tcp協議中rst表示復位,用來異常的關閉連線,在tcp的設計中它是不可或缺的。傳送rst包關閉連線時,不必等緩衝區的包都發出去,直接就丟棄快取區的包傳送rst包。而接收端收到rst包後,也不必傳送ack包來確認。
其實在網路程式設計過程中,各種rst錯誤其實是比較難排查和找到原因的。下面我列出幾種會出現rst的情況。
伺服器程式埠未開啟而客戶端來連線。這種情況是最為常見和好理解的一種了。去telnet乙個未開啟的tcp的埠可能會出現這種錯誤。這個和作業系統的實現有關。在某些情況下,作業系統也會完全不理會這些發到未開啟埠請求。
比如在下面這種情況下,主機241向主機114傳送乙個syn請求,表示想要連線主機114的40000埠,但是主機114上根本沒有開啟40000這個埠,於是就向主機241傳送了乙個rst。這種情況很常見。特別是伺服器程式core dump之後重啟之前連續出現rst的情況會經常發生。
當然在某些作業系統的主機上,未必是這樣的表現。比如向一台windows7的主機傳送乙個連線不存在的埠的請求,這台主機就不會回應。
曾經遇到過這樣乙個情況:乙個客戶端連線伺服器,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拒絕進一步傳送資料。
關於tcp,我想我們在教科書裡都讀到過一句話,'tcp是一種可靠的連線'。 而這可靠有這樣一種含義,那就是作業系統接收到的來自tcp連線中的每乙個位元組,我都會讓應用程式接收到。如果應用程式不接收怎麼辦?你猜對了,rst。
看兩段程式:
int main(int argc, char** argv)
bzero(&listen_addr,sizeof(listen_addr));
listen_addr.sin_family = af_inet;
listen_addr.sin_addr.s_addr = htonl(inaddr_any);
listen_addr.sin_port = htons(serv_port);
bind(listen_fd,(struct sockaddr *)&listen_addr, len);
listen(listen_fd, wait_count);
while(1)
if(fork() == 0)
close(real_fd);
}
return 0;
}
這一段是server的最簡單的**。邏輯很簡單,監聽乙個tcp埠然後當有客戶端來連線的時候fork乙個子程序來處理。注意看的是這一段fork裡面的處理:
char pccontent[4096];
read(real_fd,pccontent,4096);
close(real_fd);
每次只是讀socket的前4096個位元組,然後就關閉掉連線。
然後再看一下client的**:
int main(int argc, char** argv)
bzero(&s_addr, sizeof(s_addr));
s_addr.sin_family = af_inet;
inet_pton(af_inet,ser_ip,&s_addr.sin_addr);
s_addr.sin_port = htons(ser_port);
if(connect(send_sk,(struct sockaddr*)&s_addr,len) == -1)
char pccontent[5000]=;
write(send_sk,pccontent,5000);
sleep(1);
close(send_sk);
}
這段**更簡單,就是開啟乙個socket然後連線乙個伺服器並傳送5000個位元組。剛才我們看伺服器的**,每次只接收4096個位元組,那麼就是說客戶端傳送的剩下的4個位元組服務端的應用程式沒有接收到,伺服器端的socket就被關閉掉,這種情況下會發生什麼狀況呢,還是抓包看一看。
前三行就是tcp的3次握手,從第四行開始看,客戶端的49660埠向伺服器的9877埠傳送了5000個位元組的資料,然後伺服器端傳送了乙個ack進行了確認,緊接著伺服器向客戶端傳送了乙個rst斷開了連線。和我們的預期一致。
如果某個socket已經關閉,但依然收到資料也會產生rst。
**如下:
客戶端:
int main(int argc, char** argv)
bzero(&s_addr, sizeof(s_addr));
s_addr.sin_family = af_inet;
inet_pton(af_inet,ser_ip,&s_addr.sin_addr);
s_addr.sin_port = htons(ser_port);
if(connect(send_sk,(struct sockaddr*)&s_addr,len) == -1)
char pccontent[4096]=;
write(send_sk,pccontent,4096);
sleep(1);
write(send_sk,pccontent,4096);
close(send_sk);
}
服務端:
int main(int argc, char** argv)
bzero(&listen_addr,sizeof(listen_addr));
listen_addr.sin_family = af_inet;
listen_addr.sin_addr.s_addr = htonl(inaddr_any);
listen_addr.sin_port = htons(serv_port);
bind(listen_fd,(struct sockaddr *)&listen_addr, len);
listen(listen_fd, wait_count);
while(1)
if(fork() == 0)
close(real_fd);
}
return 0;
}
?
客戶端在服務端已經關閉掉socket之後,仍然在傳送資料。這時服務端會產生rst。
總結,本文講了幾種tcp連線中出現rst的情況。實際上肯定還有無數種的rst發生,我以後會慢慢收集把更多的例子加入這篇文章。
1 從tcp協議的原理來談談rst攻擊
2 tcp客戶-伺服器程式例子
幾種TCP連線中出現RST的情況
在tcp協議中rst表示復位,用來異常的關閉連線,在tcp的設計中它是不可或缺的。傳送rst包關閉連線時,不必等緩衝區的包都發出去,直接就丟棄快取區的包傳送rst包。而接收端收到rst包後,也不必傳送ack包來確認。其實在網路程式設計過程中,各種rst錯誤其實是比較難排查和找到原因的。下面列出幾種會...
幾種TCP連線中出現RST的情況
應該沒有人會質疑,現在是乙個網路時代了。應該不少程式設計師在程式設計中需要考慮多機 區域網 廣域網的各種問題。所以網路知識也是避免不了學習的。而且筆者一直覺得tcp ip網路知識在乙個程式設計師知識體系中必需占有一席之地的。在tcp協議中rst表示復位,用來異常的關閉連線,在tcp的設計中它是不可或...
tcp出現rst的情況
正常情況tcp四層握手關閉連線,rst基本都是異常情況,整理如下 1.gfw 2.對方埠未開啟,發生在連線建立 如果對方sync backlog滿了的話,sync簡單被丟棄,表現為超時,而不會rst 3.close socket 時recv buffer 不為空 例如,客戶端發了兩個請求,伺服器只從...