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

TCP 網絡編程:netstat -a 無法檢測到監聽套接字的排查實戰

     編輯:程式設計綜合問答

問題現象

在學習《Unix 網絡編程》第 5 章 TCP 服務器示例(tcpserv01.c)時,按書中說法後台啟動服務器 ./tcpserv01 &,理應看到 9877 端口處於 LISTEN 狀態,但 netstat -a 卻查不到該套接字。隨後運行客戶端 tcpcli01.c 調用 connect() 即報錯,推測服務端代碼有誤。

服務端核心代碼片段

#include "unp.h"

int main(int argc, char **argv) {
    int listenfd, connfd;
    pid_t childpid;
    socklen_t clilen;
    struct sockaddr_in cliaddr, servaddr;

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);  // SERV_PORT 定義為 9877

    Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
    Listen(listenfd, LISTENQ);

    for ( ; ; ) {
        clilen = sizeof(cliaddr);
        connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);

        if ( (childpid = Fork()) == 0) {  // 子進程
            Close(listenfd);
            str_echo(connfd);
            exit(0);
        }
        Close(connfd);  // 父進程關閉已連接套接字
    }
}

根因分析與解決

經逐步調試發現,問題出在自定義包裹函數 Bind() 的實現上。書中使用的 unp.h 封裝了系統調用並進行錯誤檢查,若 Bind() 內部調用 bind() 時參數傳遞錯誤(如地址結構體大小計算錯誤、強制類型轉換不當),會導致綁定失敗但未報錯,進而使 listen() 無效,自然也就沒有監聽套接字出現。

排查關鍵點

  1. 檢查包裹函數實現:對照系統調用原型 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen),確認參數順序、類型轉換、長度計算無誤。
  2. 添加錯誤輸出:在 Bind() 內部打印 errno 或調用 perror(),確認是否真正執行成功。
  3. 驗證端口佔用:用 netstat -an | findstr 9877(Windows)或 netstat -an | grep 9877(Linux)精確查找。

修正後的關鍵代碼

// 正確的 Bind 包裝函數示例
void Bind(int fd, const struct sockaddr *sa, socklen_t salen) {
    if (bind(fd, sa, salen) < 0)
        err_sys("bind error");  // 必須包含錯誤處理
}

修正 Bind() 後重新編譯運行,netstat -a 即可看到 9877 端口進入 LISTEN 狀態,客戶端連接恢復正常。

經驗總結

  • 封裝函數雖簡化調用,但內部錯誤極難察覺,調試時務必「打開黑盒」檢查實現。
  • 網絡編程中「靜默失敗」最隱蔽,養成檢查所有系統調用返回值的習慣至關重要。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved