簡單 TCP 伺服器,客戶端連線後印出 IP:Port。首次連線顯示:
127.0.0.1(如亂碼 IP)1023(非預期)加上 bzero(&cin, sizeof(cin)) 後,首次變為 0.0.0.0:0,仍錯誤。
accept 最後一個參數 socklen_t *addrlen 是值-結果參數:
sizeof(struct sockaddr_in)),告知核心可寫入多少位元組。未初始化 導致核心讀取到隨機/零值,認為緩衝區太小,不寫入地址,殘留堆疊垃圾資料。
struct sockaddr_in cin;
socklen_t len;
while (1) {
len = sizeof(cin); // 每次 accept 前必須重新賦值
int fc = accept(fs, (struct sockaddr *)&cin, &len);
if (fc < 0) { perror("accept"); continue; }
char ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &cin.sin_addr, ip, sizeof(ip));
printf("Client: %s:%d\n", ip, ntohs(cin.sin_port));
// ...
}
accept 前必須重設 len = sizeof(addr),因核心可能修改其值。NULL:雖然部分文件說可傳 NULL(不需地址),但某些核心版本會回傳錯誤(如本案 fc == -1)。bzero 不能替代 len 初始化:bzero 清空地址結構體,但 len 仍為未定義值。socklen_t 而非 int,確保型別正確(POSIX 標準)。#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 8000
#define MAX_LINE 100
void to_lower(char *s) {
for (; *s; s++)
if (*s >= 'A' && *s <= 'Z') *s = *s - 'A' + 'a';
}
int main(void)
{
struct sockaddr_in sin, cin;
int fs, fc;
socklen_t len;
char buf[MAX_LINE], ip[INET_ADDRSTRLEN];
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(PORT);
fs = socket(AF_INET, SOCK_STREAM, 0);
bind(fs, (struct sockaddr *)&sin, sizeof(sin));
listen(fs, 10);
int reuse = 1;
setsockopt(fs, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
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; }
inet_ntop(AF_INET, &cin.sin_addr, ip, sizeof(ip));
printf("Client connected: %s:%d\n", ip, ntohs(cin.sin_port));
int n = read(fc, buf, MAX_LINE);
if (n > 0) {
buf[n] = '\0';
printf("Received: %s\n", buf);
to_lower(buf);
write(fc, buf, n);
}
close(fc);
}
close(fs);
return 0;
}
accept(fd, NULL, NULL) 僅取得連線 fd。man 2 accept 註明:addrlen 不可為 NULL 若 addr 非 NULL;若兩者皆 NULL 則不回傳地址。len,兼容性最好。