TCP的服務監聽步驟(等待客戶端連接前)
TCP 服務器通過以下步驟完成從初始化到等待客戶端連接,為后續的數據傳輸(send()
/recv()
)奠定了基礎
一、創建套接字(Socket)
- 作用:套接字是網絡通信的端點,用于標識通信中的雙方(IP 地址和端口號)。服務器首先需要創建一個套接字,作為后續監聽和通信的基礎。
- 實現:應用程序通過系統調用(如
socket()
)創建套接字,指定地址族(如 IPv4 用AF_INET
)、傳輸層協議(TCP 用SOCK_STREAM
)等參數。 - 示例:在 Linux 中,
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
?會創建一個 TCP 套接字,返回套接字描述符sockfd
。
二、綁定套接字到本地地址和端口(Bind)
- 作用:將創建的套接字與服務器的本地 IP 地址和指定端口號綁定,確保客戶端能通過該地址和端口找到服務器。
- 實現:
- 應用程序定義一個包含本地 IP 地址和端口號的結構體(如
sockaddr_in
)。 - 通過系統調用(如
bind()
)將套接字與該結構體綁定。
- 應用程序定義一個包含本地 IP 地址和端口號的結構體(如
- 注意:
- 端口號通常選擇 1024 以上的非特權端口(避免與系統服務沖突)。
- 若 IP 地址設為
INADDR_ANY
(通配地址),表示服務器監聽所有可用的本地網絡接口。
- 示例:
bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
三、設置監聽狀態(Listen)
- 作用:將綁定后的套接字轉換為監聽套接字,使其能夠接收客戶端的連接請求。同時,操作系統會為該套接字維護一個連接請求隊列(未完成三次握手的客戶端請求)。
- 實現:通過系統調用(如
listen()
)設置監聽,參數包括監聽套接字和隊列的最大長度(backlog
,即最多能同時等待處理的連接請求數)。 - 注意:
backlog
的值需根據服務器性能和預期并發量設置,若隊列滿,新的連接請求會被拒絕(客戶端可能收到 “連接超時” 錯誤)。 - 示例:
listen(sockfd, 5);
(表示最多允許 5 個連接請求在隊列中等待)。
四、等待并接受客戶端連接(Accept)
- 作用:監聽套接字進入阻塞狀態(默認情況),等待客戶端的連接請求。當有客戶端發起連接時,服務器通過
accept()
系統調用接受連接,生成一個新的連接套接字用于與該客戶端通信。 - 過程:
- 客戶端通過
connect()
發起 TCP 連接請求,與服務器進行三次握手。 - 三次握手完成后,連接請求從 “未完成隊列” 移至 “已完成隊列”。
- 服務器調用
accept()
從 “已完成隊列” 中取出一個連接請求,創建新的連接套接字(與監聽套接字的 IP 和端口相同,但用于單獨的客戶端通信)。
- 客戶端通過
- 注意:
- 監聽套接字始終保持監聽狀態,用于接收新的連接請求;連接套接字則用于與特定客戶端的數據傳輸。
- 若需處理并發連接,服務器通常會通過多線程、多進程或 I/O 復用(如
select
、epoll
)來同時管理多個連接套接字。
- 示例:
int new_fd = accept(sockfd, (struct sockaddr*)&client_addr, &addr_len);
(new_fd
即為新的連接套接字)。
總結:TCP 服務監聽的完整流程
- 創建套接字(
socket()
)→ 2. 綁定地址和端口(bind()
)→ 3. 設置監聽(listen()
)→ 4. 接受連接(accept()
)。
服務端與客戶端通信(3握手4揮手)
TCP 通信遵循 "三次握手建立連接、數據傳輸、四次揮手斷開連接" 的經典模式,這種模式確保了通信的可靠性和有序性。
1. 連接建立:三次握手
TCP 是面向連接的協議,在進行實際數據傳輸前,通信雙方必須先建立連接,這個過程被形象地稱為 "三次握手"(Three-way Handshake)。
- 第一次握手:客戶端發送 SYN(同步序列編號)報文段,告知服務器客戶端的初始序列號(ISN),并請求建立連接。
- 第二次次握手:服務器收到 SYN 后,返回一個 SYN+ACK(確認)報文段,包含服務器的初始序列號,并確認收到客戶端的 SYN(ACK 值 = 客戶端 ISN+1)。
- 第三次握手:客戶端收到服務器的 SYN+ACK 后,發送一個 ACK 報文段,確認收到服務器的 SYN(ACK 值 = 服務器 ISN+1)。
三次握手完成后,TCP 連接正式建立,雙方可以開始數據傳輸。這種設計有效防止了因網絡延遲導致的 "已失效的連接請求報文段" 被服務器接收,從而避免資源浪費。
2. 數據傳輸階段
連接建立后,進入數據傳輸階段。TCP 通過以下機制保證數據傳輸的可靠性:
- 序列號與確認機制:每個數據字節都有一個序列號,接收方收到數據后會返回確認信息,告知發送方已成功接收的數據量。
- 超時重傳:發送方設置超時計時器,若在規定時間內未收到確認,則重傳數據。
- 流量控制:通過滑動窗口機制,控制發送方的發送速率,避免接收方緩沖區溢出。
- 擁塞控制:檢測網絡擁塞狀態并調整發送速率,避免網絡過載。
3. 連接終止:四次揮手
當通信結束需要斷開連接時,TCP 使用 "四次揮手"(Four-way Wavehand)過程:
- 第一次揮手:主動關閉方發送 FIN(結束)報文段,告知對方要關閉連接。
- 第二次揮手:被動關閉方收到 FIN 后,返回 ACK 確認,此時主動關閉方到被動關閉方的連接半關閉。
- 第三次揮手:被動關閉方準備好關閉連接后,也發送一個 FIN 報文段。
- 第四次揮手:主動關閉方收到 FIN 后,返回 ACK 確認,被動關閉方到主動關閉方的連接也半關閉。等待一段時間確保確認報文送達后,連接完全關閉。
四次揮手的設計是因為 TCP 連接是全雙工的,允許雙方獨立關閉各自的發送通道。
服務端的全流程
1. 調用 socket 函數創建 socket(監聽socket)
2. 調用 bind 函數 將 socket綁定到某個ip和端口的二元組上
3. 調用 listen 函數 開啟偵聽
4. 當有客戶端請求連接上來后,調用 accept 函數接受連接,產生一個新的 socket(客戶端 socket)
5. 基于新產生的 socket 調用 send 或 recv 函數開始與客戶端進行數據交流
6. 通信結束后,調用 close 函數關閉監聽?socket
1. 三次握手(建立連接)發生在第 4 步:accept()
?函數執行期間
- 當服務器調用?
listen()
?后,進入 “監聽” 狀態,內核會維護兩個隊列:- 未完成連接隊列:客戶端已發送 SYN 但三次握手未完成的請求。
- 已完成連接隊列:三次握手已完成、等待服務器處理的連接。
- 當客戶端調用?
connect()
?發起連接時,內核會自動完成三次握手:- 客戶端發送 SYN → 服務器內核接收并放入未完成隊列,返回 SYN+ACK(第二次握手)。
- 客戶端返回 ACK(第三次握手)→ 服務器內核將連接從 “未完成隊列” 移至 “已完成隊列”。
- 此時服務器調用?
accept()
?函數,只是從 “已完成隊列” 中取出一個已建立的連接,并創建新的客戶端 socket。三次握手的整個過程由內核在?listen()
?之后、accept()
?返回之前自動完成,應用程序無需干預。
2. 四次揮手(斷開連接)發生在數據傳輸結束后,由?close()
?函數觸發
- 當通信雙方(客戶端或服務器)決定結束連接時,調用?
close()
?函數會觸發內核執行四次揮手:- 主動關閉方調用?
close()
?→ 內核發送 FIN 報文(第一次揮手)。 - 被動關閉方收到 FIN 后,內核自動返回 ACK(第二次揮手),此時主動關閉方向被動關閉方的連接 “半關閉”。
- 被動關閉方處理完剩余數據后,調用?
close()
?→ 內核發送 FIN 報文(第三次揮手)。 - 主動關閉方收到 FIN 后,內核自動返回 ACK(第四次揮手),等待超時后連接完全關閉。
- 主動關閉方調用?
- 注意:通常服務器會先關閉 “客戶端 socket”(與單個客戶端的連接),最后再關閉 “監聽 socket”(停止接受新連接)。四次揮手由內核在?
close()
?調用后自動完成,應用程序只需調用?close()
?觸發這個過程。