一、TCP協議
TCP(Transmission Control Protocol,傳輸控制協議)是一種面向連接的、可靠的、基于字節流的傳輸層通信協議。
1、三次握手
客戶端:
1、先發起連接,發送SYN置1,seqnum=12345(隨機值)----半連接建立,形成syn隊列
服務端:
1、收到SYN,分配tcp控制塊
2、發送ack置1,acknum=12346, syn,seqnum=34531(隨機值)
客戶端:
2、發送ack,acknum=34532 ,seqnum=12346(從之前的序列號+1) ----半連接轉為全連接,進入accept隊列
小結:
可見在TCP協議中,服務端是被動連接。
2、四次揮手
注意只有主動方和被動方:
1、主動發起斷開連接,發送FIN置1,seqnum=1234(隨機值)
2、被動方收到FIN,發送ack置1,acknum=1235(seq+1)
3、被動方發送FIN,seqnum=5678(隨機值)
4、主動方收到FIN,發送ack置1,acknum=5679(seq+1)
3、TCP的格式
首部開銷最小20字節,最大60字節
4、網絡協議棧
應用層:http,ftp,ssh
傳輸層:TCP,UDP
網絡層:IP,ICMP
5、TCP發生在哪些函數中:
通過對網絡I/O的學習,發現無論使用select,poll,還是epoll,都沒看見TCP/UDP的相關字眼,那電腦是如何判斷使用哪種協議來實現連接的?
回顧之前代碼,主要使用了socket(),bind(), listen(), accept(), connect(), send(), recv(), **close()**這幾個函數。
- 這些函數是如何實現TCP連接的?
- 這些函數編譯生成的執行文件,為什么在不同系統平臺可以正常運行?
二、POSIX API
1、什么是POSIX API?
- 是一套可移植操作系統接口標準,旨在使應用程序可以在多種UNIX和類UNIX操作系統上運行而無需修改。
- 像之前使用的socket(),bind(), listen(), accept(), connect(), send(), recv(), close(),fcntl(),select,poll,epoll等函數,都是POSIX API的一部分。
2、三次握手的過程,發送在哪些函數里面:
-
客戶端:
client:
Connet() -
server:
listen();
accept();
3、API簡介
1. socket()
int socket(int domain, int type, int protocol);
/*
*socket英文單詞的本意是插座,在網絡編程中,socket代表網絡IO的建立,實質是(fd,tcp控制塊--stream)
*功能:
*分配fd,bitmap
*tcp控制塊的分配
*domain:通信協議族,AF_INET, AF_UNIX,AF_INET6,大部分都是基于IPV4的網絡通信,通常使用AF_INET
*type:指定套接字類型,SOCK_STREAM(字節流)---TCP協議,SOCK_DGRAM(報文)---UDP協議,SOCK_RAW---原始套接字
*protocol:指定具體的協議,通常為0。前兩個參數確定了,第三個參數通常為0,由內核自動選擇合適的協議
*返回值:成功返回非負文件描述符,失敗返回-1
*/
2. bind()
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
1、綁定固定ip,port
2、還未到tcp建立過程
3、屬于設置過程
返回值:成功返回0,失敗返回-1
*/
3. listen()
int listen(int sockfd, int backlog);
/*
1、設置監聽,等待客戶端連接
2、backlog:半連接隊列的大小(內核參數,默認值5)
3、TCP開始建立
返回值:成功返回0,失敗返回-1
實質:
1、把TCP的status設置為TCP_STATUS_LISTEN
2、如果不設置,強行連接,會被拒絕
3、設置tcp全連接隊列和半連接隊列tcp->syn_queuetcp->accept_queue
*/
4. accept()
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/*
1、阻塞等待客戶端的連接請求
2、屬于建立TCP的完成后
返回值:成功返回非負文件描述符,失敗返回-1
實質:
分配fd給客戶端,并把客戶端的fd和tcp控制塊綁定EPOLLLT:水平觸發
只要有事件就緒就會一直通知EPOLLET:邊緣觸發
只有當事件發生時才會通知一次
避免不能及時響應需要額外添加while循環進行處理
while(1){fd = accept(listenfd, NULL, NULL);if(fd < 0) {break;}
}
*/
5. connect()
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
1、主動發起tcp連接
2、屬于建立TCP的過程
返回值:成功返回0,失敗返回-1
*/
6. send()
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
/*
flags:用于指定發送數據的特殊選項,如MSG_DONTWAIT,非阻塞模式,一般默認為0
1、發送數據到對方
2、屬于傳輸層
返回值:成功返回發送的字節數,失敗返回-1
實質:
將數據從用戶空間拷貝到內核緩沖區,然后通過網絡發送到對方的內核緩沖區(協議棧),異步操作
*/ssize_t write(int fd, const void *buf, size_t count);
/*
1、發送數據到對方
返回值:成功返回發送的字節數,失敗返回-1
相比于send,少了一個flags參數,其余相同,send是POSIX API,write是標準C庫函數
如果是網絡編程,推薦使用send,因為send可以指定flags參數
*/
7. recv()
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
/*
flags:用于指定接收數據的特殊選項,如MSG_WAITALL,阻塞直到所有數據都收到
1、接收數據
2、屬于傳輸層
返回值:成功返回接收的字節數,失敗返回-1
實質:
將對方內核緩沖區的數據拷貝到用戶空間緩沖區,異步操作
*/
ssize_t read(int fd, void *buf, size_t count);
/*
1、接收數據
返回值:成功返回接收的字節數,失敗返回-1
相比于recv,少了一個flags參數,其余相同,recv是POSIX API,read是標準C庫函數
如果是網絡編程,推薦使用recv,因為recv可以指定flags參數
*/
8. close()
int close(int fd);
/*
1、關閉fd,釋放bitmap位圖資源
2、如果是TCP連接,會發送fin包給對方,等待對方的ack包
3、如果是UDP連接,直接釋放資源
返回值:成功返回0,失敗返回-1
實質:
回收fd,釋放bitmap位圖資源
如果是tcp連接,發送fin包給對方,等待對方的ack包
如果是udp連接,直接釋放資源
*/int shutdown(int sockfd, int howto);
/*
howto:用于指定關閉哪一部分連接,SHUT_RD(關閉讀),SHUT_WR(關閉寫),SHUT_RDWR(同時關閉讀寫)
1、關閉fd,釋放bitmap位圖資源
2、如果是TCP連接,可以指定只關閉讀或者寫
返回值:成功返回0,失敗返回-1
實質:
回收fd,釋放bitmap位圖資源
如果是tcp連接,可以選擇只關閉讀或者寫
不推薦使用,關閉連接就關閉了,使用shutdown,還只關閉一部分連接,容易引起混亂
*/
三、總結
1、在TCP傳輸過程中,服務端屬于被動連接,客戶端屬于主動發起連接。
2、對于不同的系統平臺,使用了POSIX API,可以實現跨平臺網絡編程。
四、拓展問題
1、tcp連接的生命周期從什么時候開始?
tcp連接的生命周期是從三次握手開始,到四次揮手結束。即客戶端調用connect()開始,到服務端調用close()結束。
2、第三次握手數據包,如何從半連接隊列查找匹配的節點
源端口,目的端口,源ip,目的ip
3、syn泛洪攻擊
- 定義:
syn泛洪攻擊是一種常見的網絡攻擊手段,其目的是通過發送大量的TCP連接請求(SYN包),耗盡服務器的資源,從而阻止合法的用戶建立正常的TCP連接。 - 解決方案:
早期系統版本,通過listen(fd, backlog)第二個參數,控制半連接隊列的大小
然后是將半連接隊列的大小設置為syn+accept隊列總長度,放未分配fd的tcb的數量
現在大部分系統是將半連接隊列的大小設置為accept隊列長度
4、為什么建立連接需要三次握手,而斷開連接需要四次揮手?
- 三次握手:
為了實現數據的可靠性傳輸;TCP通信協議雙方,都必須維護一個序列號,以標識發送出去的數據包,哪些是已經被對方收到。實質上是通信雙方相互告知序列號的起始值,并確認對方已經收到了這個序列號。如果是兩次握手,至多只有連接發起方的序列號能被確認,而另一方的序列號無法被確認。 - 四次揮手:
為了保證數據完整性,TCP協議需要通過四次揮手來正確關閉連接。主動方發送FIN包后,被動方需要先回復ACK確認收到,然后再發送自己的FIN包給主動方,最后等待主動方的ACK確認收到后才真正斷開連接。這樣可以確保雙方都能夠正確地關閉連接,避免數據丟失
5、accept發生在三次握手的哪一步
發生在第三次握手之后,TCP建立完成之后。
6、TCP是如何保證順序的?
通過序列號、確認應答、重傳機制、滑動窗口和擁塞控制等多種機制來保證數據的順序性。
7、ack沒有收到,先收到fin怎么辦?
繼續發ack
8、雙方調用close會發生什么?
結論:服務端會出現大量的TIME_WAIT狀態
- ?雙方發送FIN報文,都進入FIN_WAIT_1狀態
- ?雙方收到FIN并發送ACK,都進入closing狀態
- ?雙方收到ACK并進入TIME_WAIT狀態,等待2MSL(最大段生存時間)以確保對方收到ACK。
- ?連接完全關閉,2MSL超時后,雙方都進入CLOSED狀態,連接完全關閉。