linux网络编程-----TCP连接及相关问题
c/s模型在建立連接時的流程如下
//服務器端 int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr; bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(8080); servaddr.sin_addr.s_addr = INADDR_ANY;bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)); listen(sockfd, 10);struct sockaddr_in addr; socklen_t len = sizeof(addr); int fd = accept(sockfd, (struct sockaddr*)&addr, &len);/* ... */close(fd); close(sockfd); //客戶端 int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr; bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(8080); inet_aton("127.0.0.1", &servaddr.sin_addr);connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr);/* ... */close(sockfd);在客戶端connect,服務器accept以及二者的close過程中,代表著TCP連接的建立和終止,也就是常說的TCP三路握手和四路揮手
三路握手
建立一個TCP連接時會發生如下情形
至此完成TCP的三路握手
四路揮手
TCP建立連接需要3個分節,而終止連接卻需要4個分節
以下以客戶端先調用close為例
1. 客戶端首先調用close函數關閉套接字,執行為主動關閉,該端的TCP發送一個FIN分節到服務器端,表示數據發送完畢,客戶端進入FIN_WAIT_1狀態
2. 接收到這個FIN的服務器端執行被動關閉。這個FIN由該端的TCP確認,發送確認分節(ACK)到客戶端,FIN的接受也作為一個文件結束符傳遞給服務器端應用進程,也就是說如果服務器端進程調用recv/read函數,會讀到相當于文件結束符的FIN,從而表示客戶端已經發送完數據,執行了close。至此服務器端進入CLOSE_WAIT狀態,意思是等待調用close
3. 客戶端接收到來自服務器端的確認分節(ACK),進入FIN_WAIT_2狀態
4. 一段時間之后(通常是recv/read到FIN后),服務器端執行close函數關閉套接字,這導致服務器端TCP也發送一個FIN到客戶端,此時服務器端進入LAST_ACK狀態
5. 客戶端接收到服務器端發送的FIN并確認這個FIN,同時發送確認(ACK)到服務器端,同時進入TIME_WAIT狀態
6. 服務器端接受到來自客戶端的確認(ACK),進入CLOSED狀態,至此TCP連接終止
相關問題
為什么是三路握手,不是二路或者四路?
服務器端accept返回的套接字占用的端口和監聽套接字監聽的端口是否相同?
相同,服務器端已建立的TCP連接套接字使用和監聽套接字相同的端口。
直觀的考慮,http服務器監聽80端口的連接,服務器不可能給成千上萬個訪問分配不同的端口,所以通過accept返回的套接字使用的也是監聽套接字的端口。
套接字由<源ip,源端口,目的ip,目的端口,協議>這個五元組唯一確定的,所以僅僅端口相同不能說明TCP連接是相同的
套接字的結構是什么樣的,為什么send時只傳入一個套接字就知道往什么地址端口發送數據?
套接字表面上使用int類型,其實內部由<源ip,源端口,目的ip,目的端口,協議>這個五元組唯一確定,
在connect返回時,內核將套接字的五元組進行賦值,記錄目的ip和目的端口,所以在之后的recv/send等io操作時只需要傳入一個套接字遍知道究竟從什么地址,端口接受數據,往什么地址,端口發送數據了。
accept同理
客戶端套接字的端口是如何確定的,為什么沒有手動綁定(使用bind函數)?
在tcp連接中,客戶端套接字的端口是由內核隨機分配的,因為客戶端不是客戶端,需要知道具體端口供其他進程連接,所以也沒必要使用bind綁定地址和端口,只需要手動connect到服務器的地址和端口即可
如何理解主動關閉的一方會進入TIME_WAIT狀態?
在TCP連接終止時,主動關閉(首先調用close)的一方會進入這個狀態,這個狀態的持續時間是最長分節生命期的兩倍,有時候稱之為2MSL,有些實現是30s,有些則是2分鐘不等。
TIME_WAIT狀態有兩個存在的理由
第一個理由可以假設四路揮手的最后一個確認分節(ACK)丟失,服務器端(仍然假設客戶端主動關閉連接)在發送FIN一段時間后仍然沒有收到來自客戶端的確認(ACK),這就導致了服務器端認為客戶端沒有接受到自己的FIN分節,從而重新發送FIN分節到客戶端。
因為客戶端此時處于TIME_WAIT狀態,仍然保留著TCP連接時的雙方信息,所以收到FIN后也重新發送確認(ACK)到服務器端,從而確保服務器端可以收到ACK正常關閉。
如果客戶端不進入TIME_WAIT狀態而直接關閉,那么當最后的ACK分節丟失,服務器端重新發送FIN給客戶端后,客戶端已經沒有了雙方連接的信息,也就是說不能識別來自服務器端的這個FIN,就會發送一個RST(另一種類型的TCP分節),服務器端便認為TCP連接出現了錯誤
第二個理由可以假設在客戶端12.106.32.254的1500端口和服務器端206.168.112.219的21端口之間有一個TCP連接被建立,客戶端執行主動關閉,而后客戶端又重新發起連接
如果客戶端關閉后不進入TIME_WAIT狀態,那么客戶端內核會分配第一個可用的端口即1500(因為剛關閉,所以可用),這就導致了兩次的TCP連接的客戶端地址和端口是一樣的,如果此時發送一段數據給服務器,服務器會誤認為這個數據是第一次的連接還沒有接受完的數據,也就是說會把本次連接誤認為是剛開始的連接
如果客戶端關閉后進入TIME_WAIT狀態,那么內核不會分配一個處于TIME_WAIT狀態的(ip,端口)給進程使用,就解決了上述的問題,又因為2msl足以讓先前的數據報文消逝,所以2msl足矣
服務器端accept完客戶端請求后可不可以關閉監聽套接字?
可以,程序可以在任何時候關閉監聽套接字,只是在關閉之后不能夠再使用accept接受客戶端請求。關閉監聽套接字后不妨礙已經建立的tcp連接。另外,四路揮手是對于tcp連接而言的,對監聽套接字使用close會立即關閉它,不需要四路揮手。
傳統iso協議和tcp/ip協議的具體內容?
iso七層模型由應用層,表示層,會話層,傳輸層,網絡層,數據鏈路層,物理層構成
tcp/ip四層模型由應用層,傳輸層,網絡層,數據鏈路層構成
- 應用層:各種應用程序協議,HTTP,FTP,TALENT等
- 傳輸層:TCP數據流傳輸,面向連接的穩定,高效,可靠的傳輸協議,支持重傳。UDP數據報傳輸,面向無連接的傳輸協議,速度快,但是不可靠,適用于實時通信
- 網絡層:ip協議
- 數據鏈路層:負責數據傳輸
socket是什么,怎么理解socket的應用?
tcp/ip協議中用戶程序直接接觸的是應用層,而對于底層的tcp協議棧,需要有一種接口供應用程序調用,socket正是這樣一種接口,供用戶程序與tcp協議棧交互,可以理解socket為tcp協議的一種抽象接口
總結
以上是生活随笔為你收集整理的linux网络编程-----TCP连接及相关问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux网络编程-----几种服务器模
- 下一篇: linux网络编程-----非阻塞con