UNIX網絡編程讀書筆記:recvmsg和sendmsg函數


這兩個函數是最通用的I/O函數。實際上我們可以把所有read、readv、recv和recvfrom調用替換成recvmsg調用。類似地,各種輸出函數調用也可以替換成sendmsg調用。

#include <sys/socket.h>
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);
返回:讀入或寫出字節數——成功;-1——出錯

這兩個函數把大部分參數封裝到一個msghdr結構中:

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() */
};

這里給出的msghdr結構符合POSIX規范。有些系統仍然使用本結構源自4.2BSD的較舊版本。這個較舊的結構沒有msg_flags成員,而且msg_control和msg_controllen成員分別被稱為msg_accrights和msg_accrightslen。這個較舊結構唯一支持的輔助數據形式用於傳遞文件描述字(稱為訪問權限)。

msg_name和msg_namelen這兩個成員用於套接口未連接的場合(譬如未連接UDP套接口)。它們類似reacvfrom和sendto的第5和第6個參數:msg_name指向一個套接口地址結構,調用者在其中存放接收者(對於sendmsg調用)或發送者(對於recvmsg調用)的協議地址。如果無需指明協議地址(例如對於TCP套接口或已連接UDP套接口),msg_name應置為空指針。msg_namelen對於sendmsg是一個值參數,對於recvmsg卻是一個值-結果參數。

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

msg_control和msg_controllen這兩個成員指定可選的輔助數據的位置和大小。

對於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成員設置為該值不起作用。

如下圖所示,匯總了內核為相關輸入和輸出函數檢查的flags參數值以及recvmsg可能返回的msg_flags成員值。其中沒有sendmsg msg_flags一欄,因為本組合無效。

標志 內核檢查:
send flags
sendto flags
sendmsg flags
內核檢查:
recv flags
recvfrom flags
recvmsg flags
內核返回:
recvmsg msg_flags
MSG_DONTROUTE
MSG_DONTWAIT
MSG_PEEK
MSG_WAITALL
         太陽
         太陽


          太陽
          太陽
          太陽
 
MSG_EOR
MSG_OOB
         太陽
         太陽

          太陽
              太陽
              太陽
MSG_BCAST
MSG_MCAST
MSG_TRUNC
MSG_CTRUNC
MSG_NOTIFICATION
                  太陽
              太陽
              太陽
              太陽
              太陽

這些標志中,內核只檢查而不返回前4個標志;既檢查又返回下2個標志;不檢查而只返回后5個標志。recvmsg返回的7個標志解釋如下:

MSG_BCAST    本標志隨BSD/OS引入,相對較新。它的返回條件是:本數據報作為鏈路層廣播收取或者其宿IP地址是一個廣播地址。

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。

image

圖中給協議地址分配了16個字節,給輔助數據分配了20個字節。為緩沖數據初始化了3個iovec結構構成的數組:第一個指定一個100字節的緩沖區,第二個指定一個60字節的緩沖區,第三個指定一個80字節的緩沖區。我們還假設已為這個套接口設置了IP_RECVDSTADDR套接口選項,以接收所讀取UDP數報的宿IP地址。

我們接着假設從192.6.38.100端口2000到達一個170字節的UDP數據報,它的目的地是我們的UDP套接口,宿IP地址為206.168.112.96。如下圖所示,展示了recvmsg返回時msghdr結構中的所有信息。(圖中被修改過的字段標了陰影)

image

紅心如下圖所示為5組I/O函數之間的差異:

image


免責聲明!

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



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