Socket编程HTTP场景下连接自动关闭与recv()返回0问题求助
Hey there! As someone who’s also fumbled through early socket programming projects, let’s break down the issues you’re facing and walk through actionable fixes. Your problems with POST requests triggering unexpected socket closures and delayed GET requests showing recv() returning 0 are tied to several common oversights in handling HTTP and socket operations.
Key Issues & Fix Directions
1. Incomplete Request Reading
Right now, you only call recv() once to read the entire HTTP request. But HTTP requests—especially POST ones with larger form bodies—often arrive in multiple chunks. A single recv() might only capture part of the request, leaving your code to process incomplete data, which leads to unpredictable behavior like socket closures.
Fix Tip:
Loop recv() until you’ve read the full request:
- First, read until you hit the
\r\n\r\nsequence that marks the end of the HTTP headers. - Parse the
Content-Lengthheader to know how much data to read for the POST body (if present). - Continue reading until you’ve received the total specified length of the request body.
2. Unchecked Socket Function Errors
You’re not validating if send() or recv() calls succeed. For example:
recv()returning 0 means the client already closed the connection—trying to send data after this will cause network errors.send()can returnSOCKET_ERROR(Windows) or-1(Unix) for issues like broken connections, which your code currently ignores.
Fix Tip:
Add error checking for every socket operation:
// Example for recv() int bytes_recv = recv(c1, req, sizeof(req)-1, 0); if (bytes_recv <= 0) { if (bytes_recv == 0) { printf("Client closed the connection.\n"); } else { fprintf(stderr, "recv() failed: %d\n", GETSOCKETERRNO()); } CLOSESOCKET(c1); continue; } req[bytes_recv] = '\0'; // Ensure null-termination for string functions // Example for send() int bytes_sent = send(c1, buffer, strlen(buffer), 0); if (bytes_sent == SOCKET_ERROR) { fprintf(stderr, "send() failed: %d\n", GETSOCKETERRNO()); CLOSESOCKET(c1); continue; }
3. Missing Null Termination for Received Data
Your code uses string functions like strstr() on the req buffer, but recv() doesn’t automatically add a null terminator. If the buffer fills up completely, strstr() will read beyond the buffer’s bounds, causing undefined behavior (like crashes or socket closures).
Fix Tip:
Always reserve one byte in your buffer for the null terminator, then add it immediately after receiving data—like the example above shows with req[bytes_recv] = '\0'.
4. Incorrect POST Parameter Extraction
POST request parameters are stored in the request body, not the initial request line. Your code tries to parse POST data from the first recv() buffer (which only holds headers/request line), so you’re not actually accessing the form data. This leads to incomplete processing and socket issues.
Fix Tip:
After reading the headers, extract the Content-Length value, then read that many bytes into a separate buffer for the request body. Then parse the body (e.g., split username=foo&pswd=bar into key-value pairs).
5. Unhandled File Opening Errors
In your ReadFrom() function, you don’t check if fopen() succeeds. If the requested file doesn’t exist, fp will be NULL, and subsequent calls to fseek()/ftell() will crash your program, causing the socket to close abruptly.
Fix Tip:
Add a check for NULL after opening the file, and return a 404 error if it fails:
FILE *fp = fopen(filename, "rb"); if (!fp) { sprintf(buffer, "HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n<h1>404 Page Not Found</h1>"); send(c1, buffer, strlen(buffer), 0); CLOSESOCKET(c1); return -1; }
6. Inconsistent Socket Closure
Some code paths close the client socket (CLOSESOCKET(c1)), but others don’t. This leads to socket resource leaks, which over time can cause your server to stop accepting new connections or behave unpredictably.
Fix Tip:
Ensure that every client connection is closed after processing, regardless of success or failure. You can use a goto to jump to a cleanup section, or wrap your connection handling in a block that always closes the socket at the end.
Your Code for Reference
#if defined(_WIN32) #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0600 #endif #include <winsock2.h> #include <ws2tcpip.h> #include<io.h> #include <process.h> #pragma comment(lib, "ws2_32.lib") #else #include <sys/types.h> #include <sys/socket.h> #include<sys/stat.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> #include <errno.h> #endif #if defined(_WIN32) #define ISVALIDSOCKET(s) ((s) != INVALID_SOCKET) #define CLOSESOCKET(s) closesocket(s) #define GETSOCKETERRNO() (WSAGetLastError()) #else #define ISVALIDSOCKET(s) ((s) >= 0) #define CLOSESOCKET(s) close(s) #define SOCKET int #define GETSOCKETERRNO() (errno) #endif #if defined(_WIN32) #else #define mkdir(s) mkdir(s,0666) #endif #include <stdio.h> #include <stdlib.h> #include <string.h> int ReadFrom(char uri[], char buffer[],SOCKET c1){ sprintf(buffer, "HTTP/1.1 200 OK\r\n"); send(c1, buffer, strlen(buffer), 0); sprintf(buffer, "Connection: close \r\n"); send(c1, buffer, strlen(buffer), 0); sprintf(buffer, "Content-Type: text/html\r\n"); send(c1, buffer, strlen(buffer), 0); printf("\ntillhere23\n"); sprintf(buffer, "\r\n"); printf("\ntillhere23\n"); send(c1, buffer, strlen(buffer), 0); char filename[30]="./public/"; strcat(filename,uri); printf("\ntillhere23\n"); strcat(filename,".html"); FILE *fp=fopen(filename,"rb"); printf("\ntillhere23 %s %d %c \n",filename,fp,fp); fseek(fp, 0L, SEEK_END); printf("\ntillhere23 %s\n",filename); size_t cl = ftell(fp); printf("\ntillhere23 %s\n",filename); rewind(fp); printf("\ntillhere23 %s\n",filename); printf("%s helo :: \n",buffer); int r = fread(buffer, 1, 1024, fp); while (r) { send(c1, buffer, r, 0); r = fread(buffer, 1, 1024, fp); } fclose(fp); } int main(){ #if defined(_WIN32) WSADATA d; if (WSAStartup(MAKEWORD(2, 2), &d)) { fprintf(stderr, "Failed to initialize.\n"); return 1; } #endif printf("Configuring local address...\n"); struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; struct addrinfo *bind_address; getaddrinfo("127.0.0.1","8080", &hints, &bind_address); printf("Creating socket...\n"); SOCKET socket_listen; socket_listen = socket(bind_address->ai_family, bind_address->ai_socktype, bind_address->ai_protocol); if (!ISVALIDSOCKET(socket_listen)) { fprintf(stderr, "socket() failed. (%d)\n", GETSOCKETERRNO()); exit(1); } printf("Binding socket to local address...\n"); if (bind(socket_listen, bind_address->ai_addr, bind_address->ai_addrlen)) { fprintf(stderr, "bind() failed. (%d)\n", GETSOCKETERRNO()); exit(1); } freeaddrinfo(bind_address); SOCKET Server=socket_listen; SOCKET c1=-1; struct sockaddr_storage client_address; socklen_t client_len = sizeof(client_address); while(1){ printf("Listening...\n"); if (listen(socket_listen, 10) < 0) { fprintf(stderr, "listen() failed. (%d)\n", GETSOCKETERRNO()); exit(1); } c1 = accept(socket_listen, (struct sockaddr*) &client_address, &client_len); printf("CAME BACK %d\n",c1); printf("CAME BACK2 :: %d\n",c1); char buffer[1024]; char req[1024]; printf("receiving starts now::\n"); printf("STATUS OF RECV: %d",recv(c1,req,1024,0)); printf("\nRCV:\n%s\n",req); /*for(int i=0;i<strlen(req);i++){ printf("\ni::%d ,char: %c, num:%d\n",i,req[i],req[i]); } printf("\n ABOVE CONTAINS ALLREQuests IN ASCII\n");*/ if(req[0]=='G' &&req[1]=='E'&& req[2]=='T') { printf("\nEntered GET\n"); if(strstr(req,"username")==NULL){ if(strstr(req,"/Login.html HTTP/1.1")!=NULL) { printf("\nenetered login\n"); ReadFrom("Login",buffer,c1); CLOSESOCKET(c1); } else if(strstr(req,"/Logout.html HTTP/1.1")!=NULL) { CLOSESOCKET(c1); CLOSESOCKET(Server); printf("closed"); exit(0); } else if(strstr(req,"/Signup.html HTTP/1.1")!=NULL) { printf("\nenetered signup\n"); ReadFrom("Signup",buffer,c1); CLOSESOCKET(c1); printf("WHY CLOSE"); } else { printf("\nenetered index\n"); ReadFrom("index",buffer,c1); } } else { printf("INSTEAD \n"); if(strstr(req,"/Move.html HTTP/1.1")!=NULL) { printf("\nenetered Move\n"); MainRead(0,buffer,c1,1,USERNAMEnum); } else if(strstr(req,"/Read.html?Category=")!=NULL) { char Category[20]; char Patid[20]; wordify(req,"Category=","?",Category); wordify(req,"Patid=","?",Patid); ReadFrom("Read",buffer,c1); } else if(strstr(req,"/Write.html HTTP/1.1")!=NULL) { printf("\nenetered write\n"); char Category[20]; char Patid[20]; wordify(req,"Category=","?",Category); wordify(req,"Patid=","?",Patid); ReadFrom("Read",buffer,c1); } else if(strstr(req,"/New.html HTTP/1.1")!=NULL) { printf("\nenetered New\n"); MainRead(0,buffer,c1,4,USERNAMEnum); } else if(strstr(req,"/Logout.html HTTP/1.1")!=NULL) { CLOSESOCKET(c1); CLOSESOCKET(Server); printf("closed"); exit(0); } else{ MainRead(0,buffer,c1,0,USERNAMEnum); } } } else if(req[0]=='P' && req[1]=='O' && req[2]=='S' && req[3]=='T') { printf("came here to POST:\n"); if(strstr(req,"/table1.html HTTP/1.1")!=NULL) { printf("came here to TABLE\n"); printf("GOT::TABLE\n"); wordify(req,"Sno=","&",User.doctor_name); StoI(User.doctor_name,&User.serialno); wordify(req,"username=","&",User.username); wordify(req,"&pswd=","\b",User.password); if(Login(User)==0){ //Code for error } else{ ItoS(USERNAMEnum,Login(User)); } MainRead(0,buffer,c1,0,USERNAMEnum); printf("LOL"); }else if (strstr(req,"/Signup.html HTTP/1.1")!=NULL){ printf("\nenetered SIgnup in POST\n"); wordify(req,"Sno1=","&",User.doctor_name); StoI(User.doctor_name,&User.serialno); wordify(req,"&Name=","&",User.doctor_name); wordify(req,"&Phoneno=","&",User.username); StoI(User.username,&User.Phoneno); wordify(req,"&Hospitalname=","&",User.hostipal_name); wordify(req,"&State1=","&",User.state); wordify(req,"&username1=","&",User.username); wordify(req,"&pswd1=","\b",User.password); printf("THE NUMBER:: %u ul \n",User.serialno); Signup(User); ReadFrom("Login",buffer,c1); printf("Signup success\n"); CLOSESOCKET(c1); } } /* else{ CLOSESOCKET(c1); CLOSESOCKET(Server); printf("closed"); exit(0); }*/ } return 1; }
内容的提问来源于stack exchange,提问作者karthik raja anandan

