這兩個(gè)函數(shù)是最通用的I/O函數(shù)。實(shí)際上我們可以把所有read、readv、recv和recvfrom調(diào)用替換成recvmsg調(diào)用。類似地,各種輸出函數(shù)調(diào)用也可以替換成sendmsg調(diào)用。
#include <sys/socket.h>ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);返回:讀入或?qū)懗鲎止?jié)數(shù)——成功;-1——出錯(cuò)
這兩個(gè)函數(shù)把大部分參數(shù)封裝到一個(gè)msghdr結(jié)構(gòu)中:
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結(jié)構(gòu)符合POSIX規(guī)范。有些系統(tǒng)仍然使用本結(jié)構(gòu)源自4.2BSD的較舊版本。這個(gè)較舊的結(jié)構(gòu)沒(méi)有msg_flags成員,而且msg_control和msg_controllen成員分別被稱為msg_accrights和msg_accrightslen。這個(gè)較舊結(jié)構(gòu)唯一支持的輔助數(shù)據(jù)形式用于傳遞文件描述字(稱為訪問(wèn)權(quán)限)。
msg_name和msg_namelen這兩個(gè)成員用于套接口未連接的場(chǎng)合(譬如未連接UDP套接口)。它們類似reacvfrom和sendto的第5和第6個(gè)參數(shù):msg_name指向一個(gè)套接口地址結(jié)構(gòu),調(diào)用者在其中存放接收者(對(duì)于sendmsg調(diào)用)或發(fā)送者(對(duì)于recvmsg調(diào)用)的協(xié)議地址。如果無(wú)需指明協(xié)議地址(例如對(duì)于TCP套接口或已連接UDP套接口),msg_name應(yīng)置為空指針。msg_namelen對(duì)于sendmsg是一個(gè)值參數(shù),對(duì)于recvmsg卻是一個(gè)值-結(jié)果參數(shù)。
msg_iov和msg_iovlen這兩個(gè)成員指定輸入或輸出緩沖區(qū)數(shù)組(即iovec結(jié)構(gòu)數(shù)組),類似readv和writev的第2和第3個(gè)參數(shù)。
msg_control和msg_controllen這兩個(gè)成員指定可選的輔助數(shù)據(jù)的位置和大小。
對(duì)于recvmsg和sendmsg,我們必須區(qū)別它們的兩個(gè)標(biāo)志變量:一個(gè)是傳遞值的flags參數(shù),另一個(gè)是所傳遞msghdr結(jié)構(gòu)的msg_flags成員,它傳遞的是引用,因?yàn)閭鬟f給函數(shù)的是該結(jié)構(gòu)的地址。
只有recvmsg使用msg_flags成員。recvmsg被調(diào)用時(shí),flags參數(shù)被拷貝到msg_flags成員,并由內(nèi)核使用其值驅(qū)動(dòng)接收處理過(guò)程。內(nèi)核還依據(jù)recvmsg的結(jié)構(gòu)更新msg_flags成員的值。
sendmsg忽略msg_flags成員,因?yàn)樗苯邮褂胒lags參數(shù)驅(qū)動(dòng)發(fā)送處理過(guò)程。這一點(diǎn)意味著如果想在某個(gè)sendmsg調(diào)用中設(shè)置MSG_DONTWAIT標(biāo)志,那就把flags參數(shù)設(shè)置為該值;把msg_flags成員設(shè)置為該值不起作用。
如下圖所示,匯總了內(nèi)核為相關(guān)輸入和輸出函數(shù)檢查的flags參數(shù)值以及recvmsg可能返回的msg_flags成員值。其中沒(méi)有sendmsg msg_flags一欄,因?yàn)楸窘M合無(wú)效。
| 標(biāo)志 | 內(nèi)核檢查: send flags sendto flags sendmsg flags | 內(nèi)核檢查: recv flags recvfrom flags recvmsg flags | 內(nèi)核返回: 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 | |
這些標(biāo)志中,內(nèi)核只檢查而不返回前4個(gè)標(biāo)志;既檢查又返回下2個(gè)標(biāo)志;不檢查而只返回后5個(gè)標(biāo)志。recvmsg返回的7個(gè)標(biāo)志解釋如下:
MSG_BCAST 本標(biāo)志隨BSD/OS引入,相對(duì)較新。它的返回條件是:本數(shù)據(jù)報(bào)作為鏈路層廣播收取或者其宿ip地址是一個(gè)廣播地址。
MSG_MCAST 本標(biāo)志隨BSD/OS引入,相對(duì)較新。它的返回條件是:本數(shù)據(jù)報(bào)作為鏈路層多播收取。
MSG_TRUNC 本標(biāo)志的返回條件是:本數(shù)據(jù)報(bào)被截?cái)?;也就是說(shuō),內(nèi)核預(yù)備返回的數(shù)據(jù)超過(guò)進(jìn)程事先分配的空間(所有iov_len成員之和)。
MSG_CTRUNC 本標(biāo)志的返回條件是:本數(shù)據(jù)報(bào)的輔助數(shù)據(jù)被截?cái)?;也就是說(shuō),內(nèi)核預(yù)備返回的輔助數(shù)據(jù)超過(guò)進(jìn)程事先分配的空間(msg_controllen)。
MSG_EOR 如果返回的數(shù)據(jù)不是一個(gè)邏輯記錄的結(jié)尾所在,本標(biāo)志將清零;否則本標(biāo)志將設(shè)置。TCP不使用本標(biāo)志,因?yàn)樗且粋€(gè)字節(jié)流協(xié)議。
MSG_OOB 本標(biāo)志絕不為TCP帶外數(shù)據(jù)返回。它用于其他協(xié)議族(例如OSI協(xié)議族)。
MSG_NOTIFICATION 本標(biāo)志的返回條件是:SCTP接收端讀入的本消息是一個(gè)事件通知,而不是一個(gè)數(shù)據(jù)消息。
如下圖所示,展示了一個(gè)msghdr結(jié)構(gòu)以及它指向的各種信息。圖中假設(shè)進(jìn)程即將對(duì)一個(gè)UDP套接口調(diào)用recvmsg。

圖中給協(xié)議地址分配了16個(gè)字節(jié),給輔助數(shù)據(jù)分配了20個(gè)字節(jié)。為緩沖數(shù)據(jù)初始化了3個(gè)iovec結(jié)構(gòu)構(gòu)成的數(shù)組:第一個(gè)指定一個(gè)100字節(jié)的緩沖區(qū),第二個(gè)指定一個(gè)60字節(jié)的緩沖區(qū),第三個(gè)指定一個(gè)80字節(jié)的緩沖區(qū)。我們還假設(shè)已為這個(gè)套接口設(shè)置了IP_RECVDSTADDR套接口選項(xiàng),以接收所讀取UDP數(shù)報(bào)的宿IP地址。
我們接著假設(shè)從192.6.38.100端口2000到達(dá)一個(gè)170字節(jié)的UDP數(shù)據(jù)報(bào),它的目的地是我們的UDP套接口,宿IP地址為206.168.112.96。如下圖所示,展示了recvmsg返回時(shí)msghdr結(jié)構(gòu)中的所有信息。(圖中被修改過(guò)的字段標(biāo)了陰影)

如下圖所示為5組I/O函數(shù)之間的差異:

新聞熱點(diǎn)
疑難解答
圖片精選