cpp
// 3. 开始监听
if (listen(listen_sockfd, 10) < 0) {
perror("listen failed");
return 1;
}
set_non_blocking(listen_sockfd); // 将监听套接字也设为非阻塞
逐行超详细解释:监听 + 非阻塞设置
这段代码是**TCP服务器从"准备就绪"变成"正式营业"**的关键一步。
先看整体作用(大白话)
c
// 告诉操作系统:我这个服务器准备好接收客户端连接了!
// 并且最多让10个客户端排队等待连接
listen(listen_sockfd, 10);
// 把监听的"电话"设置成非阻塞模式
// 意思是:没人打电话时,我可以去干别的事,不用死等
set_non_blocking(listen_sockfd);
逐行深度拆解
第一行:if (listen(listen_sockfd, 10) < 0) {
1. listen() 函数作用
将套接字从「主动套接字」变为「被动监听套接字」
- 调用前:这个socket只是一个普通文件
- 调用后:内核开始监听端口,有客户端来连接就会通知程序
2. 第一个参数:listen_sockfd
- 就是你之前创建、设置复用、绑定好端口的监听套接字
- 告诉内核:我要监听这个端口上的连接
3. 第二个参数:10(监听队列长度 / 等待连接数)
这是高频面试题 + 核心知识点:
- 含义:内核中允许排队等待被 accept 的最大客户端连接数
- 简单理解:"候客区"容量
- 候客区满了 → 新客户端会连接失败/被拒绝
- 通常写:
5~128(不要太大也不要太小)
注意:实际内核队列大小会比你填的略大(系统会自动调整),但我们填10就代表最多让10个连接排队。
4. < 0 与错误判断
listen()成功 → 返回0listen()失败 → 返回-1- 失败原因:
- 没调用
bind()就直接监听 - 文件描述符非法
- 端口未绑定
- 没调用
第二行、第三行:perror("listen failed"); return 1;
1. perror("listen failed");
-
打印系统级错误原因
-
例子:如果没bind就listen,会打印:
listen failed: Invalid argument -
作用:快速定位bug
2. return 1;
- 程序异常退出,返回错误码1
- 服务器监听失败,无法继续运行,直接退出
重点:第四行 set_non_blocking(listen_sockfd);
这行是高性能服务器(非阻塞/IO多路复用)的关键!
1. 什么是阻塞 vs 非阻塞?
【阻塞模式(默认)】
没人来连接 → 程序卡住不动,死等
- 缺点:只能等连接,啥也干不了,无法处理高并发
【非阻塞模式】
没人来连接 → 函数立刻返回,不卡住,程序可以去做别的事
- 优点:配合
select/poll/epoll实现高并发服务器
2. 这行做了什么?
把监听套接字设置为非阻塞模式
- 以后调用
accept()时- 有连接 → 正常接收
- 没连接 → 不卡住,直接返回错误(EAGAIN/EWOULDBLOCK)
3. 为什么要把监听套接字也设为非阻塞?
因为你后面要使用:
- IO多路复用(select/poll/epoll)
- 高并发处理
- 不能让服务器在任何地方被卡住
完整原理流程图(一看就懂)
客户端发送连接请求 → 到达服务器端口
↓
内核自动接收连接(TCP三次握手内核完成!)
↓
连接放入「等待队列」(最多放10个)
↓
服务器调用 accept() → 从队列取走连接
listen() 的作用:开启这个内核队列,开始自动接收连接!
极简总结(超好记)
- listen():开启监听,让内核帮你自动处理TCP连接
- 参数10:内核等待队列长度(候客区最多10人)
- 返回值判断:监听失败就报错退出
- set_non_blocking :设置非阻塞模式,不卡住程序,支持高并发
- 核心意义 :服务器正式开始对外接收客户端连接
一句话总结
listen 让内核帮你排队等连接,非阻塞让你的程序永远不会卡死,这是高性能服务器的标准写法!