多端口监听与连接接收异常: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: Sincenew_fdis set to non-blocking,read()will return-1not just on actual errors, but also when there's no data available right now (indicated byerrno = EAGAINorEWOULDBLOCK). 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-blockingaccept()also returns-1when no connections are pending (sameEAGAIN/EWOULDBLOCKerrno), 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_fdortemparen'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
- Proper non-blocking error handling: We now check
errnoafteraccept()andread()to distinguish between normal "no data" cases and actual errors. - Buffer overflow protection: Added a check to prevent writing past the end of the
messagebuffer. - Explicit variable reset: We reset
tempandnew_fdimmediately after processing connections to avoid re-handling the same connection multiple times. - 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).
- 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
相关产品推荐
相关产品推荐

