首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

Linux C Socket TCP 客戶端多次發送失效:accept 循環陷阱

     編輯:程式設計疑難解答

服務端單線程 accept 循環導致的連接複用失敗分析

現象:客戶端建立連接後循環發送數據,服務端僅收到第一次請求,後續數據無響應,必須重啟服務端恢復。根因在於 服務端代碼邏輯錯誤地將「處理完一次請求」等同於「結束連接」。

問題代碼核心缺陷

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
}

方案三:I/O 多路複用—— select/poll/epoll(高併發首選)


// 使用 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);
            }
        }
    }
}

關鍵知識點總結

  • TCP 是流式協議:無消息邊界,recv 返回 >0 表示收到數據,==0 表示對端關閉(FIN),<0 表示錯誤
  • accept 僅用於建立新連接:已建立連接的數據收發應在連接套接字(newfd/cfd)上循環進行
  • 粘包/拆包處理:應用層需自定義協議(定長、分隔符、長度前綴)分割消息邊界
  • 優雅關閉:雙向關閉需調用 shutdown(fd, SHUT_WR) 發送 FIN,再循環 recv 直到返回 0

掌握 TCP 連接生命周期與 I/O 模型選型,是網絡編程從「能跑通」到「生產可用」的關鍵躍升。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved