數據報套接字 (SOCK_DGRAM, UDP) **無連接**。發送前無需 connect(),但每次發送必須指定**目標地址**;接收時可獲取**來源地址**。
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *msg, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
sockfd:socket(AF_INET, SOCK_DGRAM, 0) 創建的描述符。msg:發送數據緩衝區。len:數據長度。flags:通常 0 (可用 MSG_DONTROUTE 等)。dest_addr:目標地址結構 (sockaddr_in 包含 IP+Port)。addrlen:sizeof(struct sockaddr_in)。返回:實際發送位元組數 (UDP 通常全發或全不發,異常返回 -1)。
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
src_addr:輸出參數,發送方地址。addrlen:輸入輸出,調用前設緩衝區大小,返回實際地址結構大小。返回:接收位元組數,-1 表錯誤。
sendto 一次發送 = recvfrom 一次接收 (不超過緩衝區)。不會像 TCP 產生黏包/分包。connect() 僅綁定默認目標地址,允許用 send/recv 替代 sendto/recvfrom,並啟用內核端口不可達錯誤過濾 (ICMP Port Unreachable)。
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in servaddr = { .sin_family=AF_INET, .sin_port=htons(9999), .sin_addr.s_addr=INADDR_ANY };
bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
struct sockaddr_in cliaddr;
socklen_t len = sizeof(cliaddr);
char buf[1024];
while (1) {
int n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&cliaddr, &len);
if (n > 0) { buf[n]=0; printf("From %s:%d -> %s\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buf); }
}
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in servaddr = { .sin_family=AF_INET, .sin_port=htons(9999) };
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
sendto(sockfd, "Hello UDP", 9, 0, (struct sockaddr*)&servaddr, sizeof(servaddr));
sendto/recvfrom 是 UDP 編程核心 API。掌握「每包帶地址」、「邊界保持」、「不可靠」三大特性,是構建 DNS、NTP、流媒體、遊戲同步等高性能協議的基礎。