調用鏈:getaddrinfo() -> socket() -> connect() -> recv()
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#define SERVIP "192.168.56.112" // 示例 IP
#define SERVPORT "2349"
int main(int argc, char *argv[])
{
struct addrinfo hints, *res;
int status, sockfd, connSta, recvSta;
char buffer[1024];
char ipstr[INET_ADDRSTRLEN];
void *addr;
// 建議從命令行傳入 IP 和 Port
if (argc != 3) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; }
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // IPv4/IPv6 自適應
hints.ai_socktype = SOCK_STREAM; // TCP
status = getaddrinfo(argv[1], argv[2], &hints, &res);
if (status != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); return 2; }
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < 0) { perror("socket"); return 3; } // socket 失敗返回 -1,非 0
connSta = connect(sockfd, res->ai_addr, res->ai_addrlen);
if (connSta != 0) { perror("connect"); return 4; }
// 顯示連接信息
struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
addr = &(ipv4->sin_addr);
inet_ntop(res->ai_family, addr, ipstr, sizeof(ipstr));
printf("Linked: Host = %s, Port = %d\n", ipstr, ntohs(ipv4->sin_port));
recvSta = recv(sockfd, buffer, sizeof(buffer), 0);
if (recvSta > 0) buffer[recvSta] = '\0'; // 確保字串結束
printf("\n%s\n", buffer);
freeaddrinfo(res);
close(sockfd);
return 0;
}
調用鏈:getaddrinfo() -> socket() -> bind() -> listen() -> accept() -> send()
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#define SERVPORT "2349"
int main(int argc, char *argv[])
{
struct addrinfo hints, *res;
int status, sockfd, connFd, sendSta;
struct sockaddr_in cliAddr;
char *msg = "Hello world!";
if (argc != 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } // 原代碼邏輯保留
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // 綁定 0.0.0.0
status = getaddrinfo(NULL, SERVPORT, &hints, &res);
if (status != 0) { fprintf(stderr, "getaddrinfo fail\n"); return 2; }
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
bind(sockfd, res->ai_addr, res->ai_addrlen);
listen(sockfd, 5);
printf("======== Please Wait Client =========\n");
while (1)
{
socklen_t len = sizeof(cliAddr);
connFd = accept(sockfd, (struct sockaddr *)&cliAddr, &len);
char ipstr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(cliAddr.sin_addr), ipstr, sizeof(ipstr));
printf("client: %s\n", ipstr);
sendSta = send(connFd, msg, strlen(msg), 0);
if (sendSta == -1) perror("send");
close(connFd); // 簡單模型:發送完即關閉連接
}
close(sockfd);
return 0;
}
| 函數 | 角色 | 關鍵點 |
|---|---|---|
getaddrinfo | 地址解析 | 現代替代 gethostbyname/inet_addr,支援 IPv6、服務名解析、線程安全。記得 freeaddrinfo。 |
socket | 創建端點 | 返回文件描述符。失敗返回 -1。 |
bind | 綁定地址 | Server 必須。Client 通常隱式綁定 (connect 時自動分配臨時端口)。 |
listen | 監聽隊列 | 第二參數 backlog 為內核已完成連接隊列上限 (Linux 實際為 min(backlog, somaxconn))。 |
accept | 取出連接 | 阻塞直到新連接到達。返回**新**描述符 connFd,原 sockfd 繼續監聽。 |
connect | 發起連接 | Client 呼叫。阻塞直到三次握手完成或超時/拒絕。 |
send/recv | 數據傳輸 | TCP 流式,可能**分包/黏包**。send 返回實際發送位元組 (可能 < 請求),需循環發送;recv 同理需循環接收直到預期長度或關閉。 |
close | 關閉描述符 | 引用計數歸零時發送 FIN 包。shutdown 可單向關閉。 |
perror 輸出 errno 信息。fork()、pthread_create() 或 epoll 事件循環。SIGINT/SIGTERM,關閉監聽套接字,等待子進程/線程退出。