You need to enable JavaScript to run this app. Linux下基于addrinfo实现的TCP服务端套接字未正常监听问题求助
优惠活动
大模型
产品
解决方案
定价
更多

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释放,会造成内存泄漏。
修复方案
  1. 去掉监听套接字的非阻塞配置,或者适配非阻塞的事件处理逻辑(比如搭配epoll/select),如果只是简单的演示场景,直接用阻塞模式的监听套接字即可。
  2. 增加进程保活逻辑,accept拿到客户端连接后,不要直接退出,而是进入后续的读写循环,或者持续监听新连接。
  3. 删除无效的野指针相关代码,释放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__-_-__

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.17 11:15:04