当前位置: 首页 > ops >正文

listen() 函数详解

在这里插入图片描述

listen() 函数详解

listen() 是 TCP 服务器编程中的核心函数,用于将套接字置于被动监听状态,准备接受客户端连接请求。它在 bind() 之后、accept() 之前调用。

函数原型

#include <sys/socket.h>int listen(int sockfd, int backlog);

参数说明:

  • sockfd
    已绑定的套接字文件描述符(通过 socket() 创建并已调用 bind()
  • backlog
    等待连接队列的最大长度(内核为该套接字排队的最大连接数)

返回值:

  • 成功:返回 0
  • 失败:返回 -1 并设置 errno

工作原理图解

客户端连接请求           内核维护的队列│▼
┌───────────────┐       ┌──────────────────┐
│   SYN_RCVD    │◀──────│  未完成队列       │◀── 新连接请求(SYN)
│ (半连接状态)   │       │ (SYN_RCVD状态)    │     backlog 限制
└───────────────┘       └──────────────────┘│                        ││ 完成三次握手            │▼                        ▼
┌───────────────┐       ┌──────────────────┐
│  ESTABLISHED  │◀──────│   已完成队列      │◀── `accept()` 取走的连接
│ (全连接状态)   │       │ (ESTABLISHED状态)│
└───────────────┘       └──────────────────┘
  1. 未完成队列 (SYN队列)

    • 存储收到 SYN 但未完成三次握手的连接
    • 大小由系统参数决定(非 backlog 控制)
  2. 已完成队列 (Accept队列)

    • 存储已完成三次握手的连接
    • 最大长度 = min(backlog, net.core.somaxconn)

使用案例:TCP 服务器

#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>#define PORT 8080
#define BACKLOG 5  // 等待队列长度int main() {// 1. 创建套接字int server_fd = socket(AF_INET, SOCK_STREAM, 0);if (server_fd < 0) {perror("socket failed");return 1;}// 2. 绑定地址struct sockaddr_in address;memset(&address, 0, sizeof(address));address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(PORT);if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {perror("bind failed");close(server_fd);return 1;}// 3. 开始监听if (listen(server_fd, BACKLOG) < 0) {perror("listen failed");close(server_fd);return 1;}printf("Server listening on port %d\n", PORT);// 4. 接受连接int addrlen = sizeof(address);while(1) {int new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen);if (new_socket < 0) {perror("accept failed");continue;}printf("New connection accepted\n");// 处理客户端请求...char *response = "HTTP/1.1 200 OK\nContent-Type: text/plain\n\nHello from server!";send(new_socket, response, strlen(response), 0);close(new_socket); // 关闭客户端套接字}close(server_fd);return 0;
}

关键注意事项

1. backlog 参数的最佳实践

  • 现代系统:实际队列长度 = min(backlog, net.core.somaxconn)
  • 查看系统限制:
    cat /proc/sys/net/core/somaxconn  # 通常默认128
    
  • 推荐值
    • 轻负载:5-10
    • 高并发:1000+(需同步调整系统参数)
    • 生产环境:根据压力测试调整

2. 错误处理

常见错误码:

  • EADDRINUSE:端口已被占用(检查是否重用套接字)
  • EBADF:无效套接字描述符
  • ENOTSOCK:文件描述符不是套接字
  • EOPNOTSUPP:套接字类型不支持监听

3. 完整连接建立流程

ClientServerKernelsocket()bind()listen(fd, backlog)创建SYN队列和Accept队列SYN (连接请求)不通知应用层accept() 阻塞等待SYN+ACKACK (完成握手)accept() 返回新套接字ClientServerKernel

高级主题

1. SYN Flood 攻击防护

当未完成队列溢出时:

// 检查半连接状态
cat /proc/net/stat/syn_recv

防护措施

  • 启用内核参数 net.ipv4.tcp_syncookies = 1
  • 减少 net.ipv4.tcp_synack_retries(默认5)

2. 非阻塞模式下的 listen()

使用 fcntl() 设置非阻塞:

fcntl(server_fd, F_SETFL, O_NONBLOCK);

此时 accept() 立即返回:

  • 有连接:返回有效描述符
  • 无连接:返回 -1 且 errno = EAGAIN/EWOULDBLOCK

3. 多线程/多进程模型

while(1) {int client_fd = accept(...);if (fork() == 0) {  // 子进程处理close(server_fd); handle_client(client_fd);exit(0);}close(client_fd);  // 父进程关闭客户端fd
}

常见问题解答

Q:backlog 设置为 0 会怎样?
A:行为取决于系统,Linux 中会允许至少1个连接(实际值 = max(backlog, 1)

Q:客户端在 accept() 前完成连接会怎样?
A:连接存入完成队列,等待 accept() 取出,不会丢失

Q:如何监控队列状态?

# 查看Accept队列溢出
netstat -s | grep "listen queue"# 查看SYN队列溢出
netstat -s | grep "SYNs to LISTEN"

Q:为什么需要 listen() 而 UDP 不需要?
A:TCP 是面向连接的协议,需要维护连接状态;UDP 是无连接的。

💡 最佳实践listen() 是TCP服务器启动的最后一步准备,合理的 backlog 设置对高并发系统至关重要,需结合系统参数优化。加粗样式

http://www.xdnf.cn/news/16390.html

相关文章:

  • Petalinux驱动开发
  • 多智能体系统设计:协作、竞争与涌现行为
  • 零基础学习性能测试第六章:性能难点-Jmeter实现海量用户压测
  • 【奔跑吧!Linux 内核(第二版)】第5章:内核模块
  • 关于“PromptPilot” 之2 -目标系统:Prompt构造器
  • Linux c网络专栏第三章DPDK
  • Rust与Java DynamoDB、MySQL CRM、tokio-pg、SVM、Custors实战指南
  • UV: 下一代 Python 包管理工具
  • Unity 实时 CPU 使用率监控
  • 前缀和-560.和为k的子数组-力扣(LeetCode)
  • XFile 系统架构设计文档
  • iOS安全和逆向系列教程 第20篇:Objective-C运行时机制深度解析与Hook技术
  • 七、搭建springCloudAlibaba2021.1版本分布式微服务-skywalking9.0链路追踪
  • 前端基础班学习路线
  • GPGPU基本概念
  • PiscCode实现从图像到字符艺术
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现PCB上二维码检测识别(C#代码UI界面版)
  • 北大区块链技术与应用 笔记
  • 虚拟机ubuntu20.04共享安装文件夹
  • AI驱动的金融推理:Fin-R1模型如何重塑行业决策逻辑
  • docker搭建部署 onlyoffice 实现前端集成在线解析文档解决方案
  • elasticsearch 倒排索引原理详解
  • LeetCode 923.多重三数之和
  • 面试150 数字范围按位与
  • 第六章 JavaScript 互操(3)JS调用.NET
  • Ubuntu服务器安装与运维手册——操作纯享版
  • 算法竞赛阶段二-数据结构(37)数据结构动态链表list
  • CLAP文本-音频基础模型: LEARNING AUDIO CONCEPTS FROM NATURAL LANGUAGE SUPERVISION
  • 机器学习的算法有哪些?
  • Jmeter的元件使用介绍:(八)断言器详解