1、TCP三次握手
TCP三次握手
(TCP three-way handshake)是TCP協(xié)議建立可靠連接
的過程,確??蛻舳撕头?wù)器之間可以進(jìn)行可靠的通信。下面是TCP三次握手的詳細(xì)過程:
假設(shè)客戶端為A,服務(wù)器為B。
SYN---> ACK + SYN --->ACK
(1) 第一次握手
第一次握手(SYN=1,seq=500)
A向B發(fā)送一個(gè)帶有SYN標(biāo)志位的數(shù)據(jù)包,表示A請(qǐng)求建立連接。SYN標(biāo)志位為1表示這是一個(gè)連接請(qǐng)求數(shù)據(jù)包,500是A隨機(jī)選擇的初始序列號(hào)。
(2) 第二次握手
第二次握手(SYN=1,ACK=1,ack=500+1,seq=800):
B接收到A發(fā)送的連接請(qǐng)求后,會(huì)向A回復(fù)一個(gè)數(shù)據(jù)包。該數(shù)據(jù)包中,SYN和ACK標(biāo)志位都被設(shè)置為1。ACK=1表示B確認(rèn)收到了A的連接請(qǐng)求,ack字段的值為A的初始序列號(hào)加1,表明B期望下一個(gè)收到的序列號(hào)是A初始序列號(hào)加1。seq字段800是B隨機(jī)選擇的初始序列號(hào)。
(3) 第三次握手
第三次握手(ACK=1,ack=800+1):
A收到B的回復(fù)后,檢查ACK標(biāo)志位是否為1,以及ack字段的值是否為B的初始序列號(hào)加1。如果正確,A會(huì)向B發(fā)送一個(gè)確認(rèn)數(shù)據(jù)包。在該數(shù)據(jù)包中,ACK標(biāo)志位被設(shè)置為1,表示A確認(rèn)收到了B的回復(fù)。ack字段的值是B的初始序列號(hào)加1,表明A期望下一個(gè)收到的序列號(hào)是B初始序列號(hào)加1。
完成這三次握手后,TCP連接就建立成功,A和B之間可以開始傳輸數(shù)據(jù)。連接的狀態(tài)變?yōu)橐呀?ESTABLISHED)。
三次握手是操作系統(tǒng)內(nèi)核(Kernel)的TCP協(xié)議棧負(fù)責(zé)處理。用戶層的表現(xiàn):服務(wù)器端是accept(),客戶端是connect(),其這兩個(gè)函數(shù)成功執(zhí)行并返回了。
2、TCP四次揮手
TCP四次揮手
(TCP four-way handshake)是TCP連接的關(guān)閉過程,用于在客戶端和服務(wù)器之間終止一個(gè)已建立的連接。與TCP三次握手不同,四次揮手需要進(jìn)行四個(gè)步驟來關(guān)閉連接,以確保數(shù)據(jù)傳輸?shù)耐暾院涂煽啃浴?/p>
FIN--->ACK FIN--->ACk
(1) 一次揮手
客戶端向服務(wù)器發(fā)送連接釋放請(qǐng)求(FIN)
的數(shù)據(jù)包。
客戶端希望關(guān)閉連接,因此發(fā)送一個(gè)帶有FIN標(biāo)志位的數(shù)據(jù)包,F(xiàn)IN=1表示連接釋放請(qǐng)求。設(shè)置序列號(hào)為seq=501。
(2) 二次揮手
服務(wù)器接收到客戶端的連接釋放請(qǐng)求后,回復(fù)確認(rèn)連接釋放(ACK)
的數(shù)據(jù)包。
服務(wù)器收到客戶端的FIN后,發(fā)送一個(gè)帶有ACK標(biāo)志位的數(shù)據(jù)包,ACK=1,ack 502表示確認(rèn)收到客戶端的連接釋放請(qǐng)求。
(3) 三次揮手
服務(wù)器向客戶端發(fā)送連接釋放請(qǐng)求(FIN)的數(shù)據(jù)包。
服務(wù)器希望關(guān)閉連接,因此發(fā)送一個(gè)帶有FIN標(biāo)志位的數(shù)據(jù)包,F(xiàn)IN=1表示連接釋放請(qǐng)求。設(shè)置序列號(hào)為seq=701。
(4) 四次揮手
客戶端接收到服務(wù)器的連接釋放請(qǐng)求后,回復(fù)確認(rèn)連接釋放(ACK)的數(shù)據(jù)包。
客戶端收到服務(wù)器的FIN后,發(fā)送一個(gè)帶有ACK標(biāo)志位(這個(gè)不是數(shù)據(jù),是控制報(bào)文),ACK=1,ack 702表示確認(rèn)收到服務(wù)器的連接釋放請(qǐng)求。
在發(fā)送完ACK后,客戶端等待一段時(shí)間,確保服務(wù)器收到了ACK,然后完全關(guān)閉連接。
3、TCP滑動(dòng)窗口
TCP滑動(dòng)窗口是TCP協(xié)議中的一個(gè)重要概念,用于實(shí)現(xiàn)流量控制和可靠性傳輸。滑動(dòng)窗口機(jī)制允許發(fā)送方和接收方在數(shù)據(jù)傳輸過程中動(dòng)態(tài)調(diào)整可發(fā)送和可接收的數(shù)據(jù)量,從而適應(yīng)不同的網(wǎng)絡(luò)條件和接收方的處理能力。每次通信時(shí),接收方利用win(4096)告知發(fā)送方緩沖區(qū)剩余大小。
MSS(Maximum Segment Size)是指TCP數(shù)據(jù)包中的最大有效載荷大小,它表示在TCP協(xié)議中一次性發(fā)送的最大數(shù)據(jù)量(即數(shù)據(jù)包中的有效數(shù)據(jù)部分,不包括TCP頭部和IP頭部)。
在TCP連接建立時(shí),通過TCP三次握手的過程中,雙方會(huì)交換彼此的MSS值,然后根據(jù)兩端通信的網(wǎng)絡(luò)鏈路的MTU大小進(jìn)行協(xié)商,確定實(shí)際使用的MSS。
MSS = 1500 - 20 (TCP頭部) - 20 (IP頭部) = 1460 字節(jié)
這意味著在該TCP連接中,一次可以發(fā)送的最大有效數(shù)據(jù)量為1460字節(jié),超過這個(gè)大小的數(shù)據(jù)將被拆分成多個(gè)TCP數(shù)據(jù)包進(jìn)行傳輸。
MSS的設(shè)置對(duì)于TCP性能和網(wǎng)絡(luò)吞吐量很重要。合理設(shè)置MSS可以避免網(wǎng)絡(luò)分段和數(shù)據(jù)重組,提高數(shù)據(jù)傳輸效率,特別是在一些高延遲、低帶寬的網(wǎng)絡(luò)環(huán)境中。
4、TCP狀態(tài)時(shí)序圖
使用命令查看狀態(tài)
netstat -aptn | grep 端口號(hào) #查看tcp端口
netstat -apn | grep 端口號(hào) #查看tcp、udp端口
1
、主動(dòng)發(fā)起連接請(qǐng)求端
CLOSE----發(fā)送 SYN—SYN_SEND—接收 ACK、SYN—發(fā)送 ACK-ESTABLISHED(數(shù)據(jù)通信態(tài))
2
、主動(dòng)關(guān)閉連接請(qǐng)求端
ESTABLISHED(數(shù)據(jù)通信態(tài))—發(fā)送 FIN—FIN_WAIT_1 --接收 ACK --FIN_WAIT_2(半關(guān)閉)—接收對(duì)端發(fā)送 FIN—FIN_WAIT_2(半關(guān)閉)—回發(fā)ACK–TIME_WAIT(只有主動(dòng)關(guān)閉連接方,會(huì)經(jīng)歷該狀態(tài))—等2MSL時(shí)長(zhǎng)—CLOSE
3
、被動(dòng)接收連接請(qǐng)求端
CLOSE—LISTEN—接收 SYN—LISTEN—發(fā)送 ACK、SYN—SYN_RCVD—接收ACK—ESTABLISHED(數(shù)據(jù)通信態(tài))
4
、被動(dòng)關(guān)閉連接請(qǐng)求端
ESTABLISHED(數(shù)據(jù)通信態(tài))—接收 FIN —ESTABLISHED(數(shù)據(jù)通信態(tài))— 發(fā)送ACK — CLOSE_WAIT(說明對(duì)端【主動(dòng)關(guān)閉連接端】處于FIN_WAIT_2(半關(guān)閉)狀態(tài)
—發(fā)送FIN —LAST_ACK—接收ACK—CLOSE重點(diǎn)
:ESTABLISHED(數(shù)據(jù)通信態(tài))、FIN_WAIT_2、CLOSE_WAIT、TIME_WAIT(2MSL).
先啟動(dòng)服務(wù)器,只有LISTEN
狀態(tài)。
啟動(dòng)客戶端,此時(shí)三次握手建立完成,進(jìn)入ESTABLISHED
(數(shù)據(jù)通信態(tài))。
嘗試關(guān)閉一個(gè)客戶端,此時(shí)該客戶端進(jìn)入TIME_WAIT
狀態(tài)。
服務(wù)器先主動(dòng)關(guān)閉,服務(wù)器進(jìn)入FIN_WAIT_2(半關(guān)閉)
狀態(tài)??蛻舳诉M(jìn)入CLOSE_WAIT
狀態(tài)。
此時(shí)迅速關(guān)閉客戶端,客戶端處于TIME_WAIT
狀態(tài)。
提示:#include "wrap.h"
錯(cuò)誤處理函數(shù),已經(jīng)封裝
錯(cuò)誤處理函數(shù)文章來源:http://www.zghlxwxcb.cn/news/detail-855208.html
5、多進(jìn)程并發(fā)服務(wù)器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <pthread.h>
#include <signal.h>
#include <sys/wait.h>
#include "wrap.h" //錯(cuò)誤處理函數(shù),已經(jīng)封裝
//https://blog.csdn.net/qq_45009309/article/details/131813756?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171204506416800184170823%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=171204506416800184170823&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-131813756-null-null.nonecase&utm_term=%E9%94%99%E8%AF%AF&spm=1018.2226.3001.4450
#define SRV_PORT 9999
void catch_child(int signum) //回調(diào)函數(shù) 內(nèi)核操作 產(chǎn)生信號(hào)后進(jìn)來
{
while(waitpid(0, NULL, WNOHANG) > 0); //非阻塞回收子進(jìn)程
//循環(huán)回收是因?yàn)榭赡墚a(chǎn)生多個(gè)子進(jìn)程死亡
return ;
}
int main(int argc, char* argv[])
{
int lfd, cfd;
pid_t pid;
int ret;
char buf[BUFSIZ];
struct sockaddr_in srv_addr, clt_addr;
socklen_t clt_addr_len;
//memset(&srv_addr, 0, sizeof(srv_addr)); //地址結(jié)構(gòu)清零
bzero(&srv_addr, sizeof(srv_addr));
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons(SRV_PORT);
srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
lfd = Socket(AF_INET, SOCK_STREAM, 0); //創(chuàng)建套接字 返回用于監(jiān)聽的文件描述符
Bind(lfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)); //綁定服務(wù)器IP+地址
Listen(lfd, 128); //設(shè)置監(jiān)聽上線
clt_addr_len = sizeof(clt_addr);
while (1) {
cfd = Accept(lfd, (struct sockaddr*)&clt_addr, &clt_addr_len); //返回用于雙方通信的文件描述符
pid = fork(); //創(chuàng)建子進(jìn)程
if (pid < 0) {
perr_exit("fork error");
}
else if (pid == 0) {
close(lfd);
break;
}
else { //父進(jìn)程使用信號(hào)捕捉回收子進(jìn)程
struct sigaction act;
act.sa_handler = catch_child;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
ret = sigaction(SIGCHLD, &act, NULL);
if (ret != 0) {
perr_exit("sigaction error");
}
close(cfd);
continue;
}
}
if (pid == 0) { //子進(jìn)程實(shí)現(xiàn)讀寫功能
for (;;) {
ret = Read(cfd, buf, sizeof(buf));
for (int i = 0; i < ret; i++) {
buf[i] = toupper(buf[i]);
}
write(cfd, buf, ret);
write(STDOUT_FILENO, buf, ret); //實(shí)現(xiàn)大小寫轉(zhuǎn)換功能
if (ret == 0) {
close(cfd);
exit(1);
}
}
}
return 0;
}
文章來源地址http://www.zghlxwxcb.cn/news/detail-855208.html
6、多線程并發(fā)服務(wù)器
//頭文件同上
#define MAXLINE 8192
#define SERV_PORT 8000
struct s_info { //定義一個(gè)結(jié)構(gòu)體,將地址結(jié)構(gòu)跟cfd捆綁
struct sockaddr_in cliaddr;
int connfd;
};
void* do_work(void* arg) //子線程主調(diào)函數(shù)
{
int n, i;
struct s_info* ts = (struct s_info*)arg;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN]; //16
while (1) {
n = Read(ts->connfd, buf, MAXLINE);
if (n == 0) {
printf("the client %d closed...\n", ts->connfd);
break;
}
printf("received from %s at PORT %d\n", //打印客戶端IP+端口
inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)),
ntohs((*ts).cliaddr.sin_port));
for (i = 0; i < n; i++)
buf[i] = toupper(buf[i]);
Write(STDOUT_FILENO, buf, n);
Write(ts->connfd, buf, n); //回寫到客戶端
}
Close(ts->connfd);
return (void*)0;
}
int main(int argc, char* argv[])
{
struct sockaddr_in servaddr, cliaddr;
socklen_t cliaddr_len;
int listenfd, connfd;
pthread_t tid;
struct s_info ts[256]; //創(chuàng)建結(jié)構(gòu)體數(shù)組
int i = 0;
listenfd = Socket(AF_INET, SOCK_STREAM, 0); //創(chuàng)建一個(gè)socket,得到lfd
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT); //指定端口號(hào)
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //指定本地任意IP
Bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)); //綁定
Listen(listenfd, 128);
printf("Accepting client connect...\n");
while (1) {
cliaddr_len = sizeof(cliaddr);
connfd = Accept(listenfd, (struct sockaddr*)&cliaddr, &cliaddr_len); //阻塞監(jiān)聽客戶端鏈接請(qǐng)求
ts[i].cliaddr = cliaddr;
ts[i].connfd = connfd;
pthread_create(&tid, NULL, do_work, (void*)&ts[i]);
pthread_detach(tid); //子線程分離,防止僵尸線程產(chǎn)生
i++;
}
return 0;
}
到了這里,關(guān)于Linux網(wǎng)絡(luò)編程二(TCP圖解三次握手及四次揮手、TCP滑動(dòng)窗口、MSS、TCP狀態(tài)轉(zhuǎn)換、多進(jìn)程/多線程服務(wù)器實(shí)現(xiàn))的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!