簡單 TCP 服務器,客戶端連接後即退出。首次 accept() 返回的客戶端 IP 顯示非 127.0.0.1 (如亂碼 IP),端口顯示 1023 或 0。加上 bzero(&cin, sizeof(cin)) 後變為 0.0.0.0:0。
accept() 最後一個參數 socklen_t *addrlen 是 **值-結果參數** (Value-Result Argument):
sizeof(struct sockaddr_in),告知內核緩衝區大小。未初始化 時,棧上隨機值被當作緩衝區長度傳給內核,導致內核寫入越界或截斷,返回垃圾數據。
[c]
struct sockaddr_in cin;
socklen_t len = sizeof(cin); // 必須在 accept 前賦值!
int fc = accept(fs, (struct sockaddr*)&cin, &len);
if (fc < 0) { perror("accept"); return -1; }
char ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &cin.sin_addr, ip, sizeof(ip));
printf("client ip:%s, port:%d\n", ip, ntohs(cin.sin_port));
NULL 傳遞的測試[c]
fc = accept(fs, (struct sockaddr*)&cin, NULL); // 部分舊系統允許,現代 Linux 返回 -1 並置 errno=EINVAL
測試發現 fc == -1,**強烈建議永不傳 NULL**,始終提供合法 socklen_t*。
[c]
#include
#include
#include
#include
#include
#include
#define MAX_LINE 100
#define PORT 8000
void to_lower(char *p) {
for (; *p; p++) if (*p >= 'A' && *p <= 'Z') *p = *p - 'A' + 'a';
}
int main() {
struct sockaddr_in sin, cin;
int fs, fc;
socklen_t len;
char buf[MAX_LINE];
char ip[INET_ADDRSTRLEN];
int n;
fs = socket(AF_INET, SOCK_STREAM, 0);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(PORT);
bind(fs, (struct sockaddr*)&sin, sizeof(sin));
listen(fs, 10);
printf("Server listening on port %d\n", PORT);
while (1) {
len = sizeof(cin); // 關鍵:每次循環重新賦值
fc = accept(fs, (struct sockaddr*)&cin, &len);
if (fc < 0) { perror("accept"); continue; }
n = read(fc, buf, MAX_LINE);
inet_ntop(AF_INET, &cin.sin_addr, ip, sizeof(ip));
printf("Client %s:%d -> %.*s\n", ip, ntohs(cin.sin_port), n, buf);
to_lower(buf);
write(fc, buf, n);
close(fc);
}
close(fs);
return 0;
}
socklen_t len = sizeof(struct sockaddr_in); 放在 while 循環內,每次 accept 前重置。inet_ntop 替代過時 inet_ntoa (執行緒不安全、靜態緩衝區)。accept 返回值,EAGAIN/EWOULDBLOCK (非阻塞)、ECONNABORTED (連接中斷) 需區分處理。SO_REUSEADDR、非阻塞、信號處理、優雅關閉。