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

C语言Socket远程传输时出现挂起及接收缓冲区受限问题排查

Problem Analysis & Fixes

Let’s walk through your issue and fix this step by step—this is a classic case of mixing Windows/Linux socket behaviors plus some easy-to-miss logic bugs.

Core Issue Breakdown

You’ve got two key symptoms:

  1. recv() returns far less than 4096 bytes on remote servers
  2. Socket freezes after ~1 minute of transfer, hanging the program indefinitely

First, let’s demystify the smaller recv() chunks: TCP is a stream protocol—there are no "message blocks". The number of bytes recv() returns depends entirely on how much data is available in the TCP buffer at that moment. Local testing works because same-machine communication fills the buffer quickly, so you get full 4096-byte reads. On remote networks, data gets split into MTU-sized packets (1428 bytes is a typical payload after IP/TCP headers are subtracted), which is normal behavior—you can never rely on recv() returning a fixed byte count.

The freezing issue, however, comes from critical logic errors in your code. Let’s fix them one by one:

Critical Code Bugs & Fixes

1. Broken Non-Blocking accept() Handling

You created your listening socket with SOCK_NONBLOCK, but your error handling for accept() is completely wrong:

  • In non-blocking mode, accept() returns -1 with errno = EAGAIN/EWOULDBLOCK when there are no pending connections. This is not an error—it just means you need to retry later.
  • Your current code exits the entire program immediately when this happens, making the outer while(1) loop useless.

Fix:

int newsock = accept(servSock, (struct sockaddr*) &clntAddr, &clntAddrLen);
if(newsock < 0){
    // Non-blocking mode: no connections available, wait and retry
    if (errno == EAGAIN || errno == EWOULDBLOCK) {
        usleep(1000); // Avoid CPU spamming with a short sleep
        continue;
    }
    // Only exit on real errors (e.g., invalid socket)
    fprintf(stderr, "Accept error: %s\n", strerror(errno));
    break;
}

(Don’t forget to include <errno.h> and <unistd.h> for this to work.)

2. Incorrect Non-Blocking recv() Logic

The socket returned by accept() inherits the non-blocking flag from your listening socket. Your current code treats all -1 returns from recv() as a reason to stop receiving, but non-blocking recv() returns -1 when there’s no data available (not when the connection is closed). This leads to premature termination of transfers, or in some cases, unexpected hangs.

Fix:

while(1) {
    memset(buffer, 0, sizeof(buffer));
    recvd = recv(newsock, buffer, sizeof(buffer), 0);
    if (recvd > 0) {
        printf("Received: %d\n", recvd);
        fwrite(buffer, sizeof(char), recvd, fp);
    } else if (recvd == 0) {
        // Client closed the connection normally
        printf("Client disconnected\n");
        fclose(fp);
        break;
    } else {
        // Handle temporary "no data" case for non-blocking sockets
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            usleep(1000);
            continue;
        }
        // Real error (e.g., connection reset)
        fprintf(stderr, "Recv error: %s\n", strerror(errno));
        fclose(fp);
        break;
    }
}

3. Buffer Overflow (Undefined Behavior)

Your filename handling has a dangerous overflow:

char file[17];
// ...
file[17] = '\0'; // Array only has indexes 0-16—this writes outside the buffer!

This corrupts stack memory, leading to unpredictable behavior that can vary between Windows and Linux.

Fix:

char file[17]; 
memset(file, 0, sizeof(file)); // Clear the entire buffer safely
int recvd = recv(newsock, file, sizeof(file)-1, 0); // Leave space for the null terminator
// No need for manual null terminator—memset already handled it

4. Missing File Error Checks

You don’t verify if fopen() succeeds. If the path is invalid (e.g., permission denied, missing directory), fp will be NULL, and subsequent fwrite() calls will cause crashes or hangs.

Fix:

FILE* fp = fopen(local_file_path, "wb");
if (fp == NULL) {
    fprintf(stderr, "Failed to open file %s: %s\n", local_file_path, strerror(errno));
    close(newsock);
    continue;
}

5. Redundant Listening Socket Creation

Your outer while(1) loop recreates the listening socket every time, which can cause bind() failures due to TIME_WAIT port locks. You only need to create, bind, and listen once.

Fixed Full Code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define STANDARD_PORT "1234"
#define BACKLOG 5

int main() { 
    int servSock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); 
    if(servSock < 0){ 
        fprintf(stderr, "Socket error: %s\n", strerror(errno)); 
        return 1; 
    } 

    // Allow port reuse to avoid TIME_WAIT bind failures
    int opt = 1;
    if (setsockopt(servSock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
        fprintf(stderr, "Setsockopt error: %s\n", strerror(errno));
        close(servSock);
        return 1;
    }

    struct sockaddr_in servAddr; 
    memset(&servAddr, 0, sizeof(servAddr)); 
    servAddr.sin_family = AF_INET; 
    servAddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    servAddr.sin_port = htons(atoi(STANDARD_PORT)); 

    if(bind(servSock, (struct sockaddr*) &servAddr, sizeof(servAddr)) < 0){ 
        fprintf(stderr, "Bind error: %s\n", strerror(errno)); 
        close(servSock); 
        return 1; 
    } 

    if(listen(servSock, BACKLOG) < 0){ 
        fprintf(stderr, "Listen error: %s\n", strerror(errno)); 
        close(servSock); 
        return 1; 
    } 

    printf("Listening on socket for incoming connections.\n"); 

    while(1) { 
        struct sockaddr_in clntAddr; 
        socklen_t clntAddrLen = sizeof(clntAddr); 
        int newsock = accept(servSock, (struct sockaddr*) &clntAddr, &clntAddrLen); 

        if(newsock < 0){ 
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                usleep(1000);
                continue;
            }
            fprintf(stderr, "Accept connection error: %s\n", strerror(errno));
            break;
        } 

        char clntName[INET_ADDRSTRLEN]; 
        if (inet_ntop(AF_INET, &clntAddr.sin_addr.s_addr, clntName, sizeof(clntName)) != NULL) 
            printf("Handling client %s:%d\n", clntName, ntohs(clntAddr.sin_port)); 

        char file[17]; 
        memset(file, 0, sizeof(file)); 
        int recvd = recv(newsock, file, sizeof(file)-1, 0); 

        if (recvd <= 0) {
            fprintf(stderr, "Failed to receive filename\n");
            close(newsock);
            continue;
        }

        char local_file_path[200]; 
        memset(local_file_path, 0, sizeof(local_file_path)); 
        strcat(local_file_path, "/home/"); 
        strcat(local_file_path, file); 
        printf("Saving to: %s\n", local_file_path); 

        FILE* fp = fopen(local_file_path, "wb"); 
        if (fp == NULL) {
            fprintf(stderr, "Failed to open file: %s\n", strerror(errno));
            close(newsock);
            continue;
        }

        char buffer[4096]; 
        while(1) { 
            memset(buffer, 0, sizeof(buffer)); 
            recvd = recv(newsock, buffer, sizeof(buffer), 0); 

            if (recvd > 0) {
                printf("Received: %d\n", recvd);
                fwrite(buffer, sizeof(char), recvd, fp);
            } else if (recvd == 0) {
                printf("Client closed connection\n");
                fclose(fp);
                break;
            } else {
                if (errno == EAGAIN || errno == EWOULDBLOCK) {
                    usleep(1000);
                    continue;
                }
                fprintf(stderr, "Recv error: %s\n", strerror(errno));
                fclose(fp);
                break;
            }
        } 
        close(newsock); 
    } 
    close(servSock); 
    return 0; 
}

Extra Recommendations

  • If you don’t need non-blocking behavior, remove the SOCK_NONBLOCK flag entirely—this simplifies the code and avoids all the non-blocking error handling.
  • For large file transfers, consider using sendfile() for zero-copy performance gains (though this is an optimization, not a fix).
  • Add more detailed logging (e.g., errno values) to help diagnose future issues faster.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 14:17:39