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

TCP客户端循环阻塞问题排查:UDP正常的C/S通信程序

TCP客户端循环阻塞问题的修复方案

问题背景

开发了一款支持UDP与TCP双协议的客户端-服务器程序,用于发送30KB数据包至服务器、接收回传数据并计算10次尝试的平均耗时。UDP功能运行正常,但TCP客户端的最后一个while循环出现阻塞。尝试调整发送字节数、添加sleep等待服务器响应均未解决。

问题根源

  1. 服务器侧逻辑不匹配:TCP服务器在accept客户端连接后,仅处理一次数据收发就调用close(sdconnection)关闭连接,但客户端试图在同一个长连接上完成10次请求,第二次请求时连接已断开。
  2. 客户端侧语法错误:if (ret = 0)是赋值操作而非相等判断,导致连接关闭时无法正确触发退出逻辑,recv持续阻塞。

修复方案

方案1:长连接模式(推荐)

服务器保持客户端连接,处理完10次请求后再关闭连接,与客户端的循环逻辑匹配。

修复后的服务器TCP代码片段

////////////// TCP //////////////////

myaddr.sin_port = htons(atoi(argv[2]));
sdsocketTCP = socket(AF_INET, SOCK_STREAM, 0);
if (bind(sdsocketTCP, (struct sockaddr*) &myaddr, addrlen) < 0) {
    perror("bind() nie powiodl sie (failed)\n");
    return 1;
}

if (listen(sdsocketTCP, 10) < 0) {
    perror("listen() nie powiodl sie (failed)\n");
    return 1;
}

printf("Czekam na polaczenie (I am waiting for connection)...\n");

while ((sdconnection = accept(sdsocketTCP, (struct sockaddr*) &client_addr, &client_addrlen)) >= 0)
{
    memset(buf, 0, BUFFER_SIZE);
    // 循环处理客户端的10次请求
    for (int req_cnt = 0; req_cnt < ATTEMPTS; req_cnt++) {
        received = 0;
        while (received < BUFFER_SIZE) {
            int ret = recv(sdconnection, buf + received, BUFFER_SIZE - received, 0);
            if (ret <= 0) {
                perror("recv() failed");
                close(sdconnection);
                goto end_server;
            }
            received += ret;
        }
        send(sdconnection, buf, BUFFER_SIZE, 0);
    }
    close(sdconnection);   
}
end_server:
close(sdsocketTCP);
return 0;

修复后的客户端TCP代码片段(修正判断逻辑)

////////////// TCP //////////////////

sleep(2);

endpoint.sin_port = htons(atoi(argv[3]));

if ((sdsocket2 = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket TCP() nie powiodl sie (failed)\n");
    return 1;
}

gettimeofday(&time_b, NULL);

if (connect(sdsocket2,(struct sockaddr*) &endpoint, addrlen) < 0) {
    perror("connect() nie powiodl sie");
    return 0;
}

for (i=0; i<ATTEMPTS; i++) {
    send(sdsocket2, buf, BUFFER_SIZE, 0);
    received = 0;

    while (received < BUFFER_SIZE)
    {
        printf("test %d\n", i);

        int ret = recv(sdsocket2,
                        buf+received,
                        BUFFER_SIZE-received,
                        0);
        
        if (ret <=0){
            if (ret == 0){ // 修正为相等判断
                printf("ret=0 - serwer zamknal\n");
            }
            else{
                fprintf(stderr, "recv error: %s\n", strerror(errno));
            }
         break;
        }
       received += ret; 
    }
   
}
close(sdsocket2);
gettimeofday(&time_e, NULL);

printf("Czas dla 30k-bajtowych bloków (TCP): %.6f s\n",
    (((double) (time_e.tv_sec - time_b.tv_sec) * 1000000) +
    ((double) (time_e.tv_usec - time_b.tv_usec)))
    / (1000000.0 * ATTEMPTS));
return 0;

方案2:短连接模式

客户端每次请求前重新建立连接,与UDP的无连接模式行为一致,服务器侧无需修改。

修复后的客户端TCP代码片段

////////////// TCP //////////////////

sleep(2);

endpoint.sin_port = htons(atoi(argv[3]));

gettimeofday(&time_b, NULL);

for (i=0; i<ATTEMPTS; i++) {
    // 每次请求新建TCP连接
    if ((sdsocket2 = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket TCP() nie powiodl sie (failed)\n");
        return 1;
    }

    if (connect(sdsocket2,(struct sockaddr*) &endpoint, addrlen) < 0) {
        perror("connect() nie powiodl sie");
        close(sdsocket2);
        return 0;
    }

    send(sdsocket2, buf, BUFFER_SIZE, 0);
    received = 0;

    while (received < BUFFER_SIZE)
    {
        printf("test %d\n", i);

        int ret = recv(sdsocket2,
                        buf+received,
                        BUFFER_SIZE-received,
                        0);
        
        if (ret <=0){
            if (ret == 0){ // 修正为相等判断
                printf("ret=0 - serwer zamknal\n");
            }
            else{
                fprintf(stderr, "recv error: %s\n", strerror(errno));
            }
         break;
        }
       received += ret; 
    }
    close(sdsocket2); // 每次请求完成后关闭连接
}

gettimeofday(&time_e, NULL);

printf("Czas dla 30k-bajtowych bloków (TCP): %.6f s\n",
    (((double) (time_e.tv_sec - time_b.tv_sec) * 1000000) +
    ((double) (time_e.tv_usec - time_b.tv_usec)))
    / (1000000.0 * ATTEMPTS));
return 0;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 19:25:53