現象:客戶端建立連接後循環發送數據,服務端僅收到第一次請求,後續數據無響應,必須重啟服務端恢復。根因在於 服務端代碼邏輯錯誤地將「處理完一次請求」等同於「結束連接」。
while (1) {
// 1. 阻塞等待新連接
newfd = accept(sockfd, &client_addr, &len);
// 2. 接收一次數據
ret = recv(newfd, recvbuf, RECVBUF_SIZE, 0);
// 3. 發送回應
send(newfd, resp_data, ...);
// 4. ❌ 關鍵錯誤:循環繼續,下一次 accept 會阻塞等待「新連接」
// 但客戶端並未斷開重連,而是在同一連接上繼續發送!
// 導致服務端永遠收不到同一連接的後續數據
}
while (1) {
newfd = accept(sockfd, ...);
// 在同一連接上循環收發,直到客戶端關閉
while (1) {
ret = recv(newfd, recvbuf, ...);
if (ret <= 0) break; // 0=對端關閉, <0=錯誤
// 處理業務邏輯
send(newfd, response, ...);
}
close(newfd); // 釋放當前連接,接受下一個
}
while (1) {
newfd = accept(sockfd, ...);
if (fork() == 0) { // 子進程處理
close(sockfd); // 關閉監聽套接字
while (1) {
ret = recv(newfd, ...);
if (ret <= 0) break;
send(newfd, ...);
}
close(newfd);
exit(0);
}
close(newfd); // 父進程關閉連接套接字,繼續 accept
}
// 使用 epoll 邊緣觸發模式
int epfd = epoll_create1(0);
epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &(struct epoll_event){EPOLLIN, {sockfd}});
while (1) {
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < n; i++) {
if (events[i].data.fd == sockfd) {
// 新連接
int cfd = accept(sockfd, ...);
epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &(struct epoll_event){EPOLLIN | EPOLLET, {cfd}});
} else {
// 數據可讀
int cfd = events[i].data.fd;
while ((ret = recv(cfd, buf, sizeof(buf), 0)) > 0) {
send(cfd, buf, ret, 0);
}
if (ret == 0) { // 對端關閉
epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL);
close(cfd);
}
}
}
}
recv 返回 >0 表示收到數據,==0 表示對端關閉(FIN),<0 表示錯誤newfd/cfd)上循環進行shutdown(fd, SHUT_WR) 發送 FIN,再循環 recv 直到返回 0掌握 TCP 連接生命周期與 I/O 模型選型,是網絡編程從「能跑通」到「生產可用」的關鍵躍升。