
首先我们分别编写 一个客户端程序和一个服务端程序服务端#include iostream #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include unistd.h #includecstring #includethread using namespace std; int main() { int sockfdsocket(PF_INET,SOCK_STREAM,0); struct sockaddr_in addr; memset(addr, 0, sizeof(addr)); addr.sin_family AF_INET; addr.sin_port htons(9696); inet_pton(PF_INET,127.0.1.1,addr.sin_addr); bind(sockfd, (struct sockaddr*)addr, sizeof(addr)); cout服务端已启动endl; if (listen(sockfd, 5) 0) { perror(listen 失败); close(sockfd); return -1; } int client_fd accept(sockfd, NULL, NULL); if(client_fd0) { cout 客户端已连接 endl; // 接收客户端消息 char buf[1024] {0}; int len recv(client_fd, buf, sizeof(buf), 0); if (len 0) { cout 收到客户端消息: buf endl; } close(client_fd); } this_thread::sleep_for(chrono::seconds(120)); cout实验结束endl; return 0; }客户端#include iostream #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include unistd.h #includecstring #includethread using namespace std; int main() { int sockfdsocket(PF_INET,SOCK_STREAM,0); if(sockfd0) return 0; struct sockaddr_in addr; memset(addr, 0, sizeof(addr)); addr.sin_family AF_INET; addr.sin_port htons(9696); inet_pton(PF_INET,127.0.1.1,addr.sin_addr); cout客户端已启动endl; int ret connect(sockfd, (struct sockaddr*)addr, sizeof(addr)); if(ret0) { cout 服务端已连接 endl; const char* msg Hello Server!; send(sockfd, msg, strlen(msg), 0); cout 已发送: msg endl; } sleep(1); close(sockfd); cout实验结束endl; return 0; }然后用tcpdump监测127.0.1.1 port 9696sudo tcpdump -i lo port 9696我们以最为广泛的情况为例及客户端发起连接得到如下结果tcpdump: verbose output suppressed, use -v[v]... for full protocol decodelistening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes19:24:38.805089 IP localhost.35468 LAPTOP-ICB9D6A5.localdomain.9696: Flags [S], seq 3868189141, win 65495, options [mss 65495,sackOK,TS val 2793727009 ecr 0,nop,wscale 7], length 019:24:38.805150 IP LAPTOP-ICB9D6A5.localdomain.9696 localhost.35468: Flags [S.], seq 3474975242, ack 3868189142, win 65483, options [mss 65495,sackOK,TS val 3205993548 ecr 2793727009,nop,wscale 7], length 019:24:38.805187 IP localhost.35468 LAPTOP-ICB9D6A5.localdomain.9696: Flags [.], ack 1, win 512, options [nop,nop,TS val 2793727009 ecr 3205993548], length 019:24:38.805414 IP localhost.35468 LAPTOP-ICB9D6A5.localdomain.9696: Flags [P.], seq 1:14, ack 1, win 512, options [nop,nop,TS val 2793727009 ecr 3205993548], length 1319:24:38.805421 IP LAPTOP-ICB9D6A5.localdomain.9696 localhost.35468: Flags [.], ack 14, win 512, options [nop,nop,TS val 3205993548 ecr 2793727009], length 019:24:38.805621 IP LAPTOP-ICB9D6A5.localdomain.9696 localhost.35468: Flags [F.], seq 1, ack 14, win 512, options [nop,nop,TS val 3205993549 ecr 2793727009], length 019:24:38.812084 IP localhost.35468 LAPTOP-ICB9D6A5.localdomain.9696: Flags [.], ack 2, win 512, options [nop,nop,TS val 2793727016 ecr 3205993549], length 019:24:39.810590 IP localhost.35468 LAPTOP-ICB9D6A5.localdomain.9696: Flags [F.], seq 14, ack 2, win 512, options [nop,nop,TS val 2793728014 ecr 3205993549], length 019:24:39.810726 IP LAPTOP-ICB9D6A5.localdomain.9696 localhost.35468: Flags [.], ack 15, win 512, options [nop,nop,TS val 3205994554 ecr 2793728014], length 0我们一条条分析19:24:38.805089 IP localhost.35468 LAPTOP-ICB9D6A5.localdomain.9696: Flags [S], seq 3868189141, win 65495, options [mss 65495,sackOK,TS val 2793727009 ecr 0,nop,wscale 7], length 0即为第一次握手 客户端发一个 SYN seq3868189141(x)从close进入SYN_SENT19:24:38.805150 IP LAPTOP-ICB9D6A5.localdomain.9696 localhost.35468: Flags [S.], seq 3474975242, ack 3868189142, win 65483, options [mss 65495,sackOK,TS val 3205993548 ecr 2793727009,nop,wscale 7], length 0即为SYNACK阶段服务端发 SYN ACK ack3868189142(x1) seq3474975242收时从close进入SYN_RCVD发时从SYN_RCVD到established19:24:38.805187 IP localhost.35468 LAPTOP-ICB9D6A5.localdomain.9696: Flags [.], ack 1, win 512, options [nop,nop,TS val 2793727009 ecr 3205993548], length 0客户端收到消息后发ack(此处ack为1是因为tcpdump自此之后都显示相对seq)收时从SYN_SENT到established稳定传输阶段19:24:38.805414 IP localhost.35468 LAPTOP-ICB9D6A5.localdomain.9696: Flags [P.], seq 1:14, ack 1, win 512, options [nop,nop,TS val 2793727009 ecr 3205993548], length 13length13,恰好是Hello Server!的长度与实际情况吻合的非常不错!断开连接阶段调用close(sockfd)后第一次挥手19:24:38.805621 IP LAPTOP-ICB9D6A5.localdomain.9696 localhost.35468: Flags [F.], seq 1, ack 14, win 512, options [nop,nop,TS val 3205993549 ecr 2793727009], length 0在我们的例子里是服务端首先发 FIN进入到FIN_WAIT_1第二次挥手19:24:38.812084 IP localhost.35468 LAPTOP-ICB9D6A5.localdomain.9696: Flags [.], ack 2, win 512, options [nop,nop,TS val 2793727016 ecr 3205993549], length 0客户端收到后发 ACK进入到close_wait此时也就是常说的半关闭状态服务端收到后进入到FIN_WAIT_2第三次挥手19:24:39.810590 IP localhost.35468 LAPTOP-ICB9D6A5.localdomain.9696: Flags [F.], seq 14, ack 2, win 512, options [nop,nop,TS val 2793728014 ecr 3205993549], length 0客户端到要断开时发 FIN进入到last_ack第四次挥手19:24:39.810726 IP LAPTOP-ICB9D6A5.localdomain.9696 localhost.35468: Flags [.], ack 15, win 512, options [nop,nop,TS val 3205994554 ecr 2793728014], length 0服务端收到时发 ack进入到time_wait客户端收到后到close在2msl后服务端到close运行截图本机条件Ubuntu 24.04.4 LTS (GNU/Linux 6.6.87.2-microsoft-standard-WSL2 x86_64)