You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言使用pthread实现Socket客户端与服务器连接异常排查

问题分析与解决方案

看起来你遇到的问题主要来自三个核心点:线程函数签名不匹配、主线程提前退出,以及潜在的时序竞争问题,我们逐个拆解解决:

1. 线程函数签名不符合pthread要求

pthread_create要求线程入口函数的签名必须是void* (*)(void*),但你的server()和client()都是返回int且无参数的函数,这会触发未定义行为,线程可能无法正常启动或执行逻辑。

你需要把这两个函数改成符合要求的签名,内部业务逻辑可以保留,最后返回NULL或者把返回值转为void*类型。

2. 主线程提前退出导致子线程被强制终止

你的main函数创建完两个线程后直接结束,这会导致整个进程终止,所有子线程会被系统强制杀死。服务器线程可能还没完成listen初始化就被终止,或者刚进入accept阻塞状态就被干掉;客户端线程也可能还没完成连接就提前退出。

必须在main函数里调用pthread_join等待两个子线程执行完毕,才能保证整个流程完整运行。

3. 时序竞争:客户端可能在服务器就绪前发起连接

客户端线程可能比服务器线程先完成初始化并调用connect,此时服务器还没完成bind和listen操作,会导致连接失败,客户端线程退出后,服务器线程就一直卡在accept状态无法退出。可以在客户端线程里加一个短暂延迟,或者用更严谨的同步机制(比如条件变量)让客户端等待服务器准备就绪。


修改后的完整代码

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>

#define PORT 8080
#define NUM_THREADS 2

// 调整线程函数签名以符合pthread要求
void* server(void* arg) {
    int server_fd, new_socket, valread;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};
    char *hello = "Hello from server";

    // 创建套接字文件描述符
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // 强制将套接字绑定到8080端口
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }

    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");
        exit(EXIT_FAILURE);
    }

    // 开始监听连接
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    printf("Server is listening on port %d...\n", PORT);

    // 阻塞等待客户端连接
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    valread = read(new_socket, buffer, 1024);
    printf("Server received: %s\n", buffer);
    send(new_socket, hello, strlen(hello), 0);
    printf("Server sent hello message\n");

    // 关闭套接字,避免资源泄漏
    close(new_socket);
    close(server_fd);
    return NULL;
}

void* client(void* arg) {
    // 短暂延迟,等待服务器完成初始化(严谨场景可改用条件变量同步)
    sleep(1);

    int sock = 0, valread;
    struct sockaddr_in serv_addr;
    char *hello = "Hello from client";
    char buffer[1024] = {0};

    // 创建客户端套接字
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n");
        return (void*)-1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // 将IP地址转为二进制格式
    if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) {
        printf("\nInvalid address/ Address not supported \n");
        return (void*)-1;
    }

    // 连接服务器
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("\nConnection Failed \n");
        return (void*)-1;
    }

    send(sock, hello, strlen(hello), 0);
    printf("Client sent hello message\n");
    valread = read(sock, buffer, 1024);
    printf("Client received: %s\n", buffer);

    // 关闭套接字
    close(sock);
    return NULL;
}

int main(){
    pthread_t threads[NUM_THREADS];
    int ret;

    printf("In main: creating thread server\n");
    ret = pthread_create(&threads[0], NULL, server, NULL);
    if (ret){
        printf("ERROR; return code from pthread_create() is %d\n", ret);
        exit(-1);
    }

    printf("In main: creating thread client\n");
    ret = pthread_create(&threads[1], NULL, client, NULL);
    if (ret){
        printf("ERROR; return code from pthread_create() is %d\n", ret);
        exit(-1);
    }

    // 等待两个子线程执行完毕
    pthread_join(threads[0], NULL);
    pthread_join(threads[1], NULL);

    printf("Main thread exiting\n");
    return 0;
}

额外说明

  • 我添加了sleep(1)让客户端等待服务器初始化,这是一个简单的时序问题解决方案。如果需要更严谨的同步,可以使用pthread_cond_t条件变量,让服务器在完成listen后主动通知客户端发起连接。
  • 代码中补充了关闭套接字的操作,避免系统资源泄漏。
  • 编译时记得链接pthread库:
    gcc -o socket_thread socket_thread.c -lpthread
    

运行后你应该能看到服务器和客户端正常收发消息的输出了。

内容的提问来源于stack exchange,提问作者Linir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 21:12:57