簡體   English   中英

套接字,TCP狀態和寫入系統調用

[英]Sockets, TCP states and the write systemcall

我一直在使用一個簡單的服務器,它每30秒向一個客戶端發送一個心跳包,然后通過心跳回復包確認心跳。 當我通過發送SIGKILL,SIGSEGV來粗暴地終止服務器時,客戶端通過select()和read()系統調用很容易發現它。 然后我開始想知道當你在客戶端寫入其心跳回復數據包之前這樣做會發生什么,所以我在客戶端代碼中放置了20秒的睡眠並且同時殺死了服務器但發現客戶端寫入仍然成功。 緊接着嘗試第二次寫入會觸發預期的SIGPIPE信號並寫入返回的EPIPE。 據我所知,這是正常行為,但出於好奇,我打印出了客戶端tcp狀態。 結果是:

  1. TCP_ESTABLISHED - 在發送服務器SIGKILL之前。
  2. TCP_CLOSE_WAIT - 在第一個客戶端寫入之前的服務器端SIGKILL之后。
  3. TCP_CLOSE - 在第一次和第二次寫入嘗試之后。

所以我的問題是:

  1. 為什么第一次寫入不會引發SIGPIPE並返回EPIPE?
  2. 我可以得出結論,如果第一次寫入后TCP狀態是TCP_CLOSE,那么與服務器的連接是否已關閉,或者我是否必須再次重新發送數據以確定?

正如我現在所理解的那樣,正在發生的事情的圖表:

                       server                               client

          [ESTABLISHED]  |                                     | [ESTABLISHED] 
 SIGKILL or close () --> |                                     |  
          [FIN_WAIT_1]   |------------FIN M------------------->| [CLOSE_WAIT] 
                         |                                     |            ---\
          [FIN_WAIT_2]   |<-----------ACK M+1------------------|               |  
                         |                                     |               |   a read performed after a
          [TIME_WAIT]    |<-----------FIN N--------------------| [LAST_ACK?]   |-- serverside SIGKILL returns 0
                         |                                     |               |   but write succeeds
                         |------------ACK N+1----------------->| [CLOSE]       |
                         |                                     |            ---/
                         |                                     | 
                         |                                     |            ---\
                         |                                     | [CLOSE]       |   After the first write returns
                         |                                     |               |   the TCP/IP state is CLOSED 
                         |                                     | [CLOSE]       |   but even so only the a second 
                         |                                     |               |   returns EPIPE and raises SIGPIPE.
                         |                                     | [CLOSE]       |   
                         |                                     |               v 

為什么第一次寫入不會引發SIGPIPE並返回EPIPE?

TCP是異步的。 您的只寫將數據復制到套接字緩沖區並返回。 TCP堆棧在后台接管並用於發送該數據。 換句話說,當send/sendmsg/write返回時,並不意味着數據尚未發送。

當服務器被殺死時,內核會在你的套接字上close ,發送未完成的數據,然后是FIN ,這會將你的客戶端套接字置於TCP_CLOSE_WAIT狀態。 它是一個半開連接狀態,客戶端仍可以發送數據,前提是服務器需要它。

您的客戶端發送更多數據,但服務器操作系統使用RST響應,因為沒有處理傳入數據的進程。 這會將您的客戶端套接字置於TCP_CLOSE

我可以得出結論,如果第一次寫入后TCP狀態是TCP_CLOSE,那么與服務器的連接是否已關閉,或者我是否必須再次重新發送數據以確定?

TCP_CLOSE是最終的TCP狀態。 不確定您要問的是什么,但如果您需要確保其他對等方接收並處理了您的數據,則需要發回一些應用程序級別的消息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM