在Linux的网络编程体系中,listen是一个用于套接字操作的核心函数,它的主要作用是让一个已经绑定了地址和端口的套接字进入监听状态,准备接收来自客户端的连接请求,是服务端程序建立网络通信的必要步骤之一。

listen的基本定义
listen函数属于Berkeley套接字接口的一部分,在Linux系统中通过<sys/socket.h>头文件提供声明。它的核心功能是将一个主动类型的套接字(默认创建的套接字是主动的,可用于发起连接)转换为被动套接字,让该套接字能够监听指定端口上的连接请求,等待客户端的connect调用。
只有面向连接的套接字类型(比如SOCK_STREAM,对应TCP协议)才需要调用listen函数,无连接的套接字(比如SOCK_DGRAM,对应UDP协议)不需要监听流程,因此不会使用这个函数。
listen的函数原型与参数
listen的函数原型如下:
#include <sys/socket.h> int listen(int sockfd, int backlog);
两个参数的含义分别是:
- sockfd:已经通过socket函数创建,并且已经调用bind函数绑定了本地地址和端口的套接字描述符。
- backlog:定义内核为该套接字维护的挂起连接队列的最大长度,这个参数不是限制同时建立的连接总数,而是限制已经完成TCP三次握手但还没有被服务端accept函数取走的连接的最大数量。
函数返回值为0表示执行成功,返回-1表示执行失败,此时可以通过errno获取具体的错误原因,比如EBADF表示sockfd不是有效的描述符,EOPNOTSUPP表示该套接字类型不支持listen操作。
listen的使用流程示例
一个典型的TCP服务端使用listen的完整流程如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 8080
#define BACKLOG 10
int main() {
int server_fd;
struct sockaddr_in server_addr;
// 1. 创建套接字
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
perror("socket创建失败");
exit(EXIT_FAILURE);
}
// 2. 绑定地址和端口
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("bind绑定失败");
close(server_fd);
exit(EXIT_FAILURE);
}
// 3. 调用listen进入监听状态
if (listen(server_fd, BACKLOG) < 0) {
perror("listen监听失败");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("服务端已在端口%d进入监听状态,等待客户端连接...n", PORT);
// 后续可以通过accept接收客户端连接
// ...
close(server_fd);
return 0;
}
上面的代码中,在创建套接字并绑定端口之后,调用listen函数让服务端套接字进入监听状态,此时客户端就可以通过该端口向服务端发起连接请求了。
关于backlog参数的常见疑问
很多用户会疑惑backlog的具体作用,这里需要明确:当客户端发起TCP连接时,会先完成三次握手,握手完成后的连接会被放入内核的已完成连接队列,listen的backlog参数就是这个队列的最大长度。如果队列已经满了,新的已完成握手连接可能会被内核丢弃,导致客户端连接失败。
不同Linux内核版本对backlog的处理略有差异,早期的实现中backlog同时限制半连接队列和已完成连接队列,现在的内核中backlog主要限制已完成连接队列,半连接队列的长度由系统参数/proc/sys/net/ipv4/tcp_max_syn_backlog控制。
listen相关的常见错误
使用listen时常见的错误场景包括:
- 对未绑定地址的套接字调用listen,会返回错误,因为内核不知道需要监听哪个端口。
- 对UDP类型的套接字调用listen,会返回EOPNOTSUPP错误,因为UDP是无连接协议,不需要监听流程。
- backlog参数设置为负数,会被内核自动调整为0,此时只允许一个挂起连接,实际使用中不建议设置过小。
如果遇到listen调用失败的情况,可以通过perror函数打印错误信息,快速定位问题原因。