首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Linux UDP socket 編程:sendto 與 recvfrom 最後一個參數為何一值一指針?

     編輯:程式設計綜合問答

函數原型回顧

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);   // 輸入值

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);      // 輸出指針

核心區別:輸入參數 vs 輸出參數

維度sendto (發送)recvfrom (接收)
addrlen 含義**告訴內核**目標地址結構體大小**由內核填寫**來源地址實際大小
傳遞方式值傳遞 socklen_t addrlen指針傳遞 socklen_t *addrlen
調用方責任預先設置正確大小(如 sizeof(struct sockaddr_in))分配變量並傳遞地址,調用後讀取值
內核行為只讀,用於邊界檢查寫入實際地址長度(IPv4=16, IPv6=28)

為什麼不統一?歷史與設計哲學

  1. BSD Socket 早期設計(1983):只支持 IPv4,地址長度固定 16 字節。sendto 傳值足夠,recvfrom 為了通用性(未來可能擴展)預留指針。
  2. IPv6 引入 (RFC 2553):sockaddr_in6 28 字節,sockaddr_storage 128 字節。地址長度不再固定,**接收端必須知道實際長度**以正確解析,故 recvfrom 必須回填長度。
  3. 發送端「知曉」目標長度:應用構造 dest_addr 時明確知道類型(IPv4/IPv6),直接傳 sizeof(...) 即可,無需內核回告。
  4. 接收端「未知」來源長度:數據報到達前,應用不知發送者是 IPv4 還是 IPv6,**內核必須通過指針回寫實際長度**,應用據此決定如何轉換解析。

正確調用模式

發送端

struct sockaddr_in dest;
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(9999);
inet_pton(AF_INET, "192.168.1.100", &dest.sin_addr);

sendto(sockfd, buf, len, 0, (struct sockaddr*)&dest, sizeof(dest));
// sizeof(dest) 是編譯期常量 16,直接傳值

接收端

struct sockaddr_storage src;  // 足夠大容納 IPv4/IPv6
socklen_t src_len = sizeof(src);  // 必須初始化為緩衝區大小

char buf[1024];
ssize_t n = recvfrom(sockfd, buf, sizeof(buf), 0,
                     (struct sockaddr*)&src, &src_len);
// 調用後 src_len 被更新為實際地址長度 (16 或 28)

if (src.ss_family == AF_INET) {
    struct sockaddr_in *v4 = (struct sockaddr_in*)&src;
    char ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &v4->sin_addr, ip, sizeof(ip));
    printf("來自 IPv4: %s:%d\n", ip, ntohs(v4->sin_port));
} else if (src.ss_family == AF_INET6) {
    // 處理 IPv6
}

常見錯誤

  • recvfrom(..., &src_len) 傳遞**未初始化的指針**或**指向常量的指針** → 段錯誤/內核寫入只讀內存崩潰。
  • sendto(..., sizeof(sockaddr)) 而實際結構是 sockaddr_in6 → 截斷地址導致發送失敗。
  • 忽略 recvfrom 返回的 src_len,直接強轉 sockaddr_in → IPv6 數據解析錯誤。

總結

sendto 穿值:應用告訴內核「我要發給這個地址,它佔多少字節」。
recvfrom 穿指針:內核告訴應用「數據來自這個地址,它實際佔多少字節」。
這是 **輸入參數(值)與輸出參數(指針)的本質區別**,由「發送端知長度、接收端不知長度」的非對稱性決定,並為 IPv4/IPv6 共存留足了擴展空間。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved