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

多端口监听与连接接收异常:HTTP请求处理不稳定求助

Fixing Your Non-Blocking Socket HTTP/Command Server Issues

Hey Darren, let's break down why your code is behaving inconsistently and fix those problems step by step:

Core Problems in Your Current Code

  • Incorrect non-blocking read() handling: Since new_fd is set to non-blocking, read() will return -1 not just on actual errors, but also when there's no data available right now (indicated by errno = EAGAIN or EWOULDBLOCK). Your current code stops reading immediately when it hits -1, which means you often won't wait long enough to receive the full first line of the HTTP request.
  • Unchecked accept() errors: Non-blocking accept() also returns -1 when no connections are pending (same EAGAIN/EWOULDBLOCK errno), but you're not distinguishing between this normal case and actual errors (like invalid file descriptors or permission issues).
  • Potential variable state bugs: If new_fd or temp aren't properly initialized to 0 before the loop starts, you might accidentally enter their handling blocks even when no valid connection was accepted.

Corrected Code with Explanations

#include <errno.h>  // Required for errno constants
#include <string.h> // For strerror()
#include <unistd.h> // For usleep()

// Initialize these variables outside the loop to avoid uninitialized values
int new_fd = 0;
int temp = 0;
char message[100] = {0}; // Initialize message buffer to zero

while(1) {
    // Handle HTTP server connections (8080)
    errno = 0; // Reset errno before each accept
    fd_client_serv = accept(fd_serv, (struct sockaddr *) &client_serv_addr, &sin_len_serv);
    if (fd_client_serv != -1) {
        new_fd = fd_client_serv;
        fd_set_blocking(new_fd, 0);
    } else {
        // Only log errors if it's not the normal "no pending connections" case
        if (errno != EAGAIN && errno != EWOULDBLOCK) {
            fprintf(stderr, "Accept on 8080 failed: %s\n", strerror(errno));
        }
    }

    // Handle command client connections (9090)
    errno = 0;
    fd_client_comm = accept(fd_comm, (struct sockaddr *) &client_comm_addr, &sin_len_comm);
    if (fd_client_comm != -1) {
        temp = fd_client_comm;
        fd_set_blocking(temp, 0);
    } else {
        if (errno != EAGAIN && errno != EWOULDBLOCK) {
            fprintf(stderr, "Accept on 9090 failed: %s\n", strerror(errno));
        }
    }

    // Process commands first
    if (temp != 0) {
        if (server_commands(fd_comm, temp, start_time) == 1) {
            break;
        }
        temp = 0; // Reset after processing to avoid re-handling
    }

    // Process HTTP requests
    if (new_fd != 0) {
        int i = 0;
        ssize_t bytes_read;
        int done = 0;

        while (!done) {
            errno = 0;
            bytes_read = read(new_fd, &message[i], 1);
            if (bytes_read > 0) {
                if (message[i] == '\n') {
                    done = 1; // Found end of first line
                }
                i++;
                // Prevent buffer overflow!
                if (i >= sizeof(message) - 1) {
                    done = 1;
                    fprintf(stderr, "HTTP request line too long, truncating\n");
                }
            } else if (bytes_read == 0) {
                // Client closed connection early
                done = 1;
                fprintf(stderr, "Client closed connection before sending full line\n");
            } else {
                // Handle non-blocking read case
                if (errno == EAGAIN || errno == EWOULDBLOCK) {
                    // No data available right now, small delay to reduce CPU spin
                    usleep(1000); // 1ms delay
                } else {
                    // Actual read error
                    done = 1;
                    fprintf(stderr, "Read from HTTP client failed: %s\n", strerror(errno));
                }
            }
        }

        // Only print if we got some data
        if (i > 0) {
            printf("%s", message); // Use %s instead of %s\n since message already has \n
        }
        // Send valid HTTP response (original response would fail curl checks)
        write(new_fd, "HTTP/1.1 200 OK\r\nContent-Length: 14\r\n\r\nreturn message", 53);
        memset(message, 0, sizeof(message));
        close(new_fd);
        new_fd = 0; // Reset after processing
    }
}

Key Improvements

  1. Proper non-blocking error handling: We now check errno after accept() and read() to distinguish between normal "no data" cases and actual errors.
  2. Buffer overflow protection: Added a check to prevent writing past the end of the message buffer.
  3. Explicit variable reset: We reset temp and new_fd immediately after processing connections to avoid re-handling the same connection multiple times.
  4. Valid HTTP response: Updated the response to include basic HTTP headers (your original response would cause curl to throw an error as it's not compliant with HTTP specs).
  5. Reduced CPU usage: Added a small usleep() when no data is available, which prevents your loop from eating 100% CPU while waiting for data.

For better long-term scalability, you should look into using select(), poll(), or epoll() instead of spinning with usleep(). These APIs let you wait efficiently for activity on multiple file descriptors without wasting CPU cycles.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:57:39