socket編程:recvmsg 和 sendmsg 函數


背景

復習 socket 編程的時候發現了以前沒有留意到的 2個函數:recvmsgsendmsg

ref : Linux編程之recvmsg和sendmsg函數

知識

先來看看函數原型:

#include <sys/types.h>
#include <sys/socket.h>

...
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

...
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);


struct msghdr {
    void          *msg_name;        // protocol address
    socklen_t      msg_namelen;     // size of protocol address
    struct iovec  *msg_iov;         // scatter/gather array
    int            msg_iovlen;      // elements in msg_iov
    void          *msg_control;     // ancillary data (cmsghdr struct)
    socklen_t      msg_controllen;  // length of ancillary data
    int            msg_flags;       // flags returned by recvmsg()
};

msg_name 和 msg_namelen

這兩個成員用於套接字未連接的場合(如未連接 UDP 套接字)。它們類似 recvfrom 和 sendto 的第五個和第六個參數:

  • msg_name 指向一個套接字地址結構,調用者在其中存放接收者(對於 sendmsg 調用)或發送者(對於recvmsg調用)的協議地址。如果無需指明協議地址(如對於 TCP 套接字或已連接 UDP 套接字),msg_name 應置為空指針。
  • msg_namelen 對於 sendmsg 是一個值參數,對於 recvmsg 卻是一個值-結果參數。

msg_iov 和 msg_iovlen

這兩個成員指定輸入或輸出緩沖區數組(即iovec結構數組),類似 readv 或 writev 的第二個和第三個參數。

msg_control 和 msg_controllen

這兩個成員指定可選的輔助數據的位置和大小。msg_controllen 對於 recvmsg 是一個值-結果參數。

flags

對於 recvmsg 和 sendmsg,必須區別它們的兩個標志變量:

  • 一個是傳遞值的 flags 參數;
  • 另一個是所傳遞 msghdr 結構的 msg_flags 成員,它傳遞的是引用,因為傳遞給函數的是該結構的地址。

只有 recvmsg 使用 msg_flags 成員。recvmsg 被調用時,flags 參數被復制到 msg_flags 成員,並由內核使用其值驅動接收處理過程。內核還依據 recvmsg 的結果更新 msg_flags 成員的值。

sendmsg 則忽略 msg_flags 成員,因為它直接使用 flags 參數驅動發送處理過程。這一點意味着如果想在某個 sendmsg 調用中設置 MSG_DONTWAIT 標志,那就把 flags 參數設置為該值,把 msg_flags 成員設置為該值不起作用。

recvmsg 返回的 7 個標志如下:

  • MSG_BCAST:本標志隨 BSD/OS 引入,相對較新。它的返回條件是本數據包作為鏈路層廣播收取或者其目的 IP 地址是一個廣播地址。與 IP_RECVD-STADDR 套接字選項相比,本標志是用於判定一個 UPD 數據包是否發往某個廣播地址的更好方法。
  • MSG_MCAST:本標志隨 BSD/OS 引入,相對較新。它的返回條件是本數據報作為鏈路層多播收取。
  • MSG_TRUNC:本標志的返回條件是本數據報被截斷,也就是說,內核預備返回的數據超過進程事先分配的空間(所有 iov_len 成員之和)。
  • MSG_CTRUNC:本標志的返回條件是本數據報的輔助數據被截斷,也就是說,內核預備返回的輔助數據超過進程事先分配的空間(msg_controllen)。
  • MSG_EOR:本標志的返回條件是返回數據結束一個邏輯記錄。TCP 不使用本標志,因為它是一個字節流協議。
  • MSG_OOB:本標志絕不為 TCP 帶外數據返回。它用於其他協議族(如 OSI 協議族)。
  • MSG_NOTIFICATION:本標志由 SCTP 接收者返回,指示讀入的消息是一個事先通知,而不是數據消息。

圖解

下圖展示了一個 msghdr 結構以及它指向的各種信息。圖中假設進程即將對一個 UDP 套接字調用 recvmsg:
img
圖中給協議地址分配了 16 個字節,給輔助數據分配了 20 個字節。為緩沖數據初始化了一個由 3 個 iovec 結構構成的數組:第一個指定一個 100 字節的緩沖區,第二個指定一個 60 字節的緩沖區,第三個指定一個 80 字節的緩沖區。假設已為這個套接字設置了 IP_RECVDSTADDR 套接字選項,以接收所讀取 UDP 數據包的目的 IP 地址。

假設從 198.38.100:2000 到達一個 170 字節的 UDP 數據報,它的目的地是我們的 UDP 套接字,目的 IP 地址為 206.168.112.96.下圖展示了 recvmsg 返回時 msghdr 結構中的所有信息。
img
圖中被 recvmsg 修改過的字段標上了陰影。從第一幅圖到第二幅圖的變動包括以下幾點:

  • 由 msg_name 成員指向的緩沖區被填以一個網際網套接字地址結構,其中有所收到數據報的源 IP 地址和源 UPD 端口號。
  • msg_namelen 成員(一個值-結果參數)被更新為存放在 msg_name 所指緩沖區中的數據量。本成員並無變化,因為 recvmsg 調用前和返回后其值均為 16.
  • 所收取數據報的前 100 個字節數據存放在第一個緩沖區,中 60 字節數據存放在第二個緩沖區,后 10 字節數據存放在第三個緩沖區。最后那個緩沖區的后 70 字節沒有改動。recvmsg 函數的返回值(即 170)就是該數據報的大小。
  • 由 msg_control 成員指向的緩沖區被填以一個 cmsghdr 結構。該 cmsghdr 結構中,cmsg_len 成員值為 16,cmsg_level 成員值為 IPPROTO_IP,cmsg_type 成員值為 IP_RECVDSTADDR,隨后 4 個字節存放所收到 UDP 數據報的目的 IP 地址。這個 20 字節緩沖區的后 4 個字節沒有改動。
  • msg_controllen 成員被更新為所存放輔助數據的實際數據量。本成員也是一個值-結果參數,recvmsg 返回時其結果為 16。
  • msg_flags 成員同樣被 recvmsg 更新,不過沒有標志返回給進程。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM