在學習《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() 無效,自然也就沒有監聽套接字出現。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen),確認參數順序、類型轉換、長度計算無誤。Bind() 內部打印 errno 或調用 perror(),確認是否真正執行成功。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 狀態,客戶端連接恢復正常。