1.监听队列

listen到底在干什么,创建两个监听队列,
其值在linux上的含义是,已经完成3次握手队列的大小
但在unix系统上指的是上下两个队列之和。
所以这个队列的值并不精准。共性就是给的值越大 队列就越大。
三次握手 建立连接的。

2.三次握手流程("打电话约见面")
整体流程
第 1 次握手:你打给朋友,说 "我要找你"
- 客户(你)状态:从 "没打电话"(
CLOSED)→"等朋友接"(SYN-SENT) - 发的消息:
SYN=1, seq=x- 类比:"喂,是我不?我想约你见面(
SYN=1是'请求建立连接'的信号),我这边的序号是 x(seq=x是数据的起始编号)"
- 类比:"喂,是我不?我想约你见面(
- 服务器(朋友)状态:从 "等电话"(
LISTEN)→"等你确认"(SYN-RCVD)
第 2 次握手:朋友接电话,说 "我在,你说"
- 服务器发的消息:
SYN=1, ACK=1, seq=y, ack=x+1- 类比:"我在(
ACK=1是'确认收到你的消息'),我也想跟你连(SYN=1是'同意建立连接'),我这边的序号是 y(seq=y),我收到你的序号 x 了(ack=x+1是'下一条消息请从 x+1 开始发')"
- 类比:"我在(
- 客户状态:从 "等朋友接"→"可以聊天了"(
ESTABLISHED)
第 3 次握手:你回朋友,说 "好的,开始聊"
- 客户发的消息:
ACK=1, seq=x+1, ack=y+1- 类比:"收到你的回复了(
ACK=1),我接下来发消息从 x+1 开始(seq=x+1),你发的 y 我也收到了(ack=y+1)"
- 类比:"收到你的回复了(
- 服务器状态:从 "等你确认"→"可以聊天了"(
ESTABLISHED)
核心结论
三次握手完成后,双方状态都变成ESTABLISHED,就能开始传数据了。

记口诀:"你喊我→我应你→你确认",三次沟通保证 "双方都知道'能连、想连'",避免连错 / 连丢。
3.服务器监听 + 三次握手 + accept () 建立连接

这张图是 **"服务器监听 + 三次握手 + accept () 建立连接" 的对应关系 **,结合 "奶茶店迎客" 的类比来理解:
先理清核心角色
- 左边(服务器) :对应 "奶茶店",
listen()是 "开门营业",accept()是 "接待顾客"; - 右边(客户端) :对应 "顾客",
connect()是 "上门找店"; - 中间(三次握手):是 "顾客进门的流程"。
1. listen()的作用:"开门营业,准备接客"
listen()执行后,服务器进入 **"LISTEN(监听)状态"**,相当于奶茶店 "打开门、摆好排队的栏杆";- 图里的 "未完成握手" 队列:是 "刚上门、正在和店员打招呼(进行三次握手)的顾客";
- 图里的 "已完成握手" 队列:是 "已经打完招呼、排队等接待的顾客"。
2. 三次握手和队列的关系
客户端发起connect()后,三次握手的流程会对应服务器的两个队列:
- 第 1 次握手(客户端发 SYN):服务器收到后,把这个客户端放到 "未完成握手" 队列;
- 第 2 次握手(服务器回 SYN+ACK):还在 "未完成握手" 队列里;
- 第 3 次握手(客户端回 ACK):三次握手完成,服务器把这个客户端从 "未完成" 移到 "已完成握手" 队列。
3. accept()的作用:"从排队的顾客里接一个进来"
accept()是阻塞函数 (没人排队就一直等),它会从 "已完成握手" 队列里取出一个客户端 ,并创建一个新的套接字(比如图里的c);- 这个新套接字
c,就是服务器和这个客户端单独通信的 "通道"(相当于奶茶店给这个顾客开一个 "小窗口" 点单); - 图里 "已完成握手" 队列里的红圈,就是
accept()要取的 "已经准备好的顾客"。
核心总结
listen():让服务器进入 "可接客状态",并维护 "未完成 / 已完成握手" 两个队列;- 三次握手:是客户端 "从上门到排队" 的过程,完成后进入 "已完成握手" 队列;
accept():从 "已完成队列" 里接一个客户,建立单独的通信通道。
简单说:listen()是 "开门 + 摆排队栏",三次握手是 "顾客排队",accept()是 "接排队的顾客进店"。
客户段代码:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if ( sockfd == -1 )
{
exit(1);
}
struct sockaddr_in saddr;//指定服务器的ip port
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("192.168.5.94");
int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//发起连接 三次握手
if ( res == -1 )
{
printf("connect err\n");
exit(1);
}
while( 1 )
{
printf("input\n");
char buff[128] = {0};
fgets(buff,128,stdin);
if ( strncmp(buff,"end",3) == 0 )
{
break;
}
send(sockfd,buff,strlen(buff)-1,0);
memset(buff,0,128);
recv(sockfd,buff,127,0);
printf("buff=%s\n",buff);
}
close(sockfd);
exit(0);
}

4.网络编程中的套接字(Socket)通信流程
核心是服务端处理客户端连接的基本逻辑,下面分步骤讲清楚:
明确图里的关键概念
- sockfd(监听套接字) :服务端启动后创建的 "监听用套接字",作用是绑定端口、监听客户端的连接请求,不负责和客户端直接通信。
- c1/c2(连接套接字) :当客户端发起连接时,服务端通过
accept()函数生成的 "连接用套接字",每个连接套接字对应一个客户端,后续和这个客户端的读写都用它。 - client:发起连接的客户端程序。
5.TCP四次挥手

可以把 TCP 的 "四次挥手" 想象成两人打电话挂电话的过程,更通俗:
假设你(客户端)和朋友(服务器)正在打电话聊天,现在你想挂电话(主动关闭):
第 1 次 "挥手":你说 "我没啥要说的了"
你(客户端)先开口:"我这边没别的话了哈~"(对应发FIN报文),然后你拿着电话等朋友回应(进入FIN-WAIT-1状态)。
第 2 次 "挥手":朋友说 "收到,我再收尾一下"
朋友(服务器)听到后,先回你:"好的,我知道你没话说了,我把剩下的事说完哈"(对应发ACK确认报文),此时朋友开始整理自己没说完的内容(服务器进入CLOSE-WAIT状态);你听到朋友的回应,就安静等他收尾(你进入FIN-WAIT-2状态)。
第 3 次 "挥手":朋友说 "我也说完了,挂吧"
朋友把自己的话全说完后,跟你说:"我这边也没话说了,咱们挂吧"(对应发FIN+ACK报文),然后朋友拿着电话等你最后确认(服务器进入LAST-ACK状态)。
第 4 次 "挥手":你说 "收到,我等会儿再挂"
你听到朋友说 "挂吧",回他:"收到,那我等几秒再挂哈~"(对应发ACK确认报文),然后你会等一小会儿(2MSL,防止朋友没听到你的回应又喊你);朋友听到你的回应,直接挂了电话(服务器进入CLOSED状态);等你等够时间,也挂了电话(客户端进入CLOSED状态)。
简单说:四次挥手就是 "我说完了→知道了→我也说完了→收到了,等会儿挂",确保双方都没话说了再彻底挂掉,避免有人话没说完就被打断。

把左边看成主动挂电话的人(客户端) ,右边看成被动挂电话的人(服务器):
-
第 1 步(左→右):FIN seq=x左边(主动方)先喊:"我没话说了(close)",对应发 "结束请求"(FIN),x 是他最后一句话的 "序号"。
-
第 2 步(右→左):ACK x+1右边(被动方)回:"收到你的话了",用 "ACK x+1" 表示 "我听到 x 这句话了,等你下一句(但你已经没话了)"。
-
第 3 步(右→左):FIN seq=y右边(被动方)把自己的话说完后,也喊:"我也没话说了(close)",发 "结束请求"(FIN),y 是他最后一句话的 "序号"。
-
第 4 步(左→右):ACK y+1左边(主动方)回:"收到你的话了",用 "ACK y+1" 表示 "我听到 y 这句话了"。
简单讲:这张图就是 **"主动方说'我说完了'→被动方确认→被动方说'我也说完了'→主动方确认"**,完成 "互相确认没话讲,然后挂掉" 的过程。