Linux下基于addrinfo实现的TCP服务端套接字未正常监听问题求助
问题原因分析
- 核心逻辑错误:程序在执行完
accept调用后就直接终止了,进程退出后所有套接字资源都会被系统回收,自然无法保持监听状态。你的tcp_listen_sock函数只要拿到accept的返回值就会立刻返回,main函数拿到返回值后直接结束进程,全程没有保持进程运行的逻辑。 - 非阻塞套接字使用错误:你给监听套接字设置了
O_NONBLOCK非阻塞标识,这种场景下如果调用accept时没有待处理的客户端连接,accept会立刻返回-1,你的错误处理函数_check会直接终止进程。你看到的输出能走到now listening on tcp,大概率是刚好调用accept时有客户端发起了连接,属于巧合情况。 - 野指针未定义行为:代码中定义了
struct sockaddr *addrin;但没有分配内存就直接调用memcpy写入数据,属于典型的野指针访问,会引发不可预知的问题,且该变量后续没有被使用,属于无效代码。 - 资源泄漏:调用
getaddrinfo获取的地址链表没有调用freeaddrinfo释放,会造成内存泄漏。
修复方案
- 去掉监听套接字的非阻塞配置,或者适配非阻塞的事件处理逻辑(比如搭配epoll/select),如果只是简单的演示场景,直接用阻塞模式的监听套接字即可。
- 增加进程保活逻辑,accept拿到客户端连接后,不要直接退出,而是进入后续的读写循环,或者持续监听新连接。
- 删除无效的野指针相关代码,释放
getaddrinfo分配的资源。
修复后示例代码
#include <stdio.h> #include <sys/socket.h> #include <fcntl.h> #include <string.h> #include <unistd.h> #include <netdb.h> #include <net/if.h> #include <errno.h> #include <stdlib.h> #include <arpa/inet.h> static int _check(int retval, const char *msg) { if(retval == -1) { fprintf(stderr, "%s: %s\n", msg, strerror(errno)); exit(EXIT_FAILURE); } return retval; } int tcp_listen_sock(int listen_connection) { int sock; struct addrinfo hints, *result; memset(&hints ,0 , sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_family = AF_INET; // 固定IPv4,避免兼容问题 const char *host = "0.0.0.0"; _check(getaddrinfo(host, "5678", &hints, &result), "getaddrinfo"); _check((sock = socket(AF_INET, SOCK_STREAM, 0)), "socket"); // 端口复用配置,避免重启时端口占用问题 int opt = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "enp0s3"); _check(setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)), "setsockopt bind to device"); _check(bind(sock, result->ai_addr, result->ai_addrlen), "tcp bind"); freeaddrinfo(result); // 释放getaddrinfo分配的资源 _check(listen(sock, listen_connection), "listen"); printf("now listening on 0.0.0.0:5678!\n"); // 阻塞等待客户端连接 struct sockaddr_storage client_addr; socklen_t client_len = sizeof(client_addr); int client_sock = accept(sock, (struct sockaddr*)&client_addr, &client_len); _check(client_sock, "accept"); char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &((struct sockaddr_in*)&client_addr)->sin_addr, client_ip, INET_ADDRSTRLEN); printf("accepted client connection from %s:%d\n", client_ip, ntohs(((struct sockaddr_in*)&client_addr)->sin_port)); return client_sock; } int main(int argc, char **argv) { printf("Starting program\n"); int client_sock = tcp_listen_sock(5); // 这里添加后续的读写逻辑,保持进程运行 // 示例:循环等待读取客户端数据 char buf[1024]; ssize_t n; while((n = read(client_sock, buf, sizeof(buf)-1)) > 0) { buf[n] = '\0'; printf("received %zd bytes: %s\n", n, buf); // 回写数据 write(client_sock, buf, n); } close(client_sock); printf("ending program\n"); return 0; }
验证方法
修复后运行程序,新开终端执行ss -tunlp | grep 5678就能看到对应的监听套接字,用nc 127.0.0.1 5678就能发起连接测试收发逻辑。
内容的提问来源于stack exchange,提问作者Jarvis__-_-__
相关产品推荐
相关产品推荐

