關於struct msghdr和struct cmsghdr

關於struct msghdr和struct cmsghdr

分類: linux/unix 2011-11-16 14:55 336人閱讀 評論(0) 收藏 舉報

from http://blog.csdn.net/wsllq334/article/details/6977039


理解struct msghdr

當我第一次看到他時,他看上去似乎是一個需要創建的巨大的結構。但是不要怕。其結構定義如下:
struct msghdr {
    void         *msg_name;
    socklen_t    msg_namelen;
    struct iovec *msg_iov;
    size_t       msg_iovlen;
    void         *msg_control;
    size_t       msg_controllen;
    int          msg_flags;
};
結構成員可以分爲四組。他們是:
套接口地址成員msg_name與msg_namelen。
I/O向量引用msg_iov與msg_iovlen。
附屬數據緩衝區成員msg_control與msg_controllen。
接收信息標記位msg_flags。
在我們將這個結構分爲上面的幾類以後,結構看起來就不那樣巨大了。
成員msg_name與msg_namelen
這些成員只有當我們的套接口是一個數據報套接口時才需要。msg_name成員指向我們要發送或是接收信息的套接口地址。成員msg_namelen指明瞭這個套接口地址的長度。
當調用recvmsg時,msg_name會指向一個將要接收的地址的接收區域。當調用sendmsg時,這會指向一個數據報將要發送到的目的地址。
注意,msg_name定義爲一個(void *)數據類型。我們並不需要將我們的套接口地址轉換爲(struct sockaddr *)。
成員msg_iov與msg_iovlen
這些成員指定了我們的I/O向量數組的位置以及他包含多少項。msg_iov成員指向一個struct iovec數組。我們將會回憶起I/O向量指向我們的緩衝區。成員msg_iov指明瞭在我們的I/O向量數組中有多少元素。
成員msg_control與msg_controllen
這些成員指向了我們附屬數據緩衝區並且表明了緩衝區大小。msg_control指向附屬數據緩衝區,而msg_controllen指明瞭緩衝區大小。
成員msg_flags
當使用recvmsg時,這個成員用於接收特定的標記位(他並不用於sendmsg)。在這個位置可以接收的標記位如下表所示:
標記位        描述
MSG_EOR        當接收到記錄結尾時會設置這一位。這通常對於SOCK_SEQPACKET套接口類型十分有用。
MSG_TRUNC    這個標記位表明數據的結尾被截短,因爲接收緩衝區太小不足以接收全部的數據。
MSG_CTRUNC    這個標記位表明某些控制數據(附屬數據)被截短,因爲緩衝區太小。
MSG_OOB        這個標記位表明接收了帶外數據。
MSG_ERRQUEUE    這個標記位表明沒有接收到數據,但是返回一個擴展錯誤。
我們可以在recvmsg(2)與sendmsg(2)的man手冊頁中查看更多的信息。
附屬數據結構與宏
recvmsg與sendmsg函數允許程序發送或是接收附屬數據。然而,這些額外的信息受限於一定的格式規則。這一節將會介紹控制信息頭與程序將會用來管理這些信息的宏。
簡介struct cmsghdr結構

屬信息可以包括0,1,或是更多的單獨附屬數據對象。在每一個對象之前都有一個struct
cmsghdr結構。頭部之後是填充字節,然後是對象本身。最後,附屬數據對象之後,下一個cmsghdr之前也許要有更多的填充字節。在這一章,我們將
要關注的附屬數據對象是文件描述符與證書結構。
下圖顯示了一個包含附屬數據的緩衝區是如何組織的。
我們需要注意以下幾點:
cmsg_len與CMSG_LEN()宏值所顯示的長度相同。
CMSG_SPACE()宏可以計算一個附屬數據對象的所必需的空白。
msg_controllen是CMSG_SPACE()長度之後,並且爲每一個附屬數據對象進行計算。
控制信息頭部本身由下面的C結構定義:
struct cmsghdr {
    socklen_t cmsg_len;
    int       cmsg_level;
    int       cmsg_type;
/* u_char     cmsg_data[]; */
};
其成員描述如下:
成員        描述
cmsg_len    附屬數據的字節計數,這包含結構頭的尺寸。這個值是由CMSG_LEN()宏計算的。
cmsg_level    這個值表明了原始的協議級別(例如,SOL_SOCKET)。
cmsg_type    這個值表明了控制信息類型(例如,SCM_RIGHTS)。
cmsg_data    這個成員並不實際存在。他用來指明實際的額外附屬數據所在的位置。
這一章所用的例子程序只使用SOL_SOCKET的cmsg_level值。這一章我們感興趣的控制信息類型如下(cmsg_level=SOL_SOCKET):
cmsg_level        描述
SCM_RIGHTS        附屬數據對象是一個文件描述符
SCM_CREDENTIALS        附屬數據對象是一個包含證書信息的結構
簡介cmsg(3)宏
由於附屬數據結構的複雜性,Linux系統提供了一系列的C宏來簡化我們的工作。另外,這些宏可以在不同的UNIX平臺之間進行移植,並且採取了一些措施來防止將來的改變。這些宏是由cmsg(3)的man手冊頁來進行描述的,其概要如下:
#include
struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh);
struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh, struct cmsghdr *cmsg);
size_t CMSG_ALIGN(size_t length);
size_t CMSG_SPACE(size_t length);
size_t CMSG_LEN(size_t length);
void *CMSG_DATA(struct cmsghdr *cmsg);
CMSG_LEN()宏
這個宏接受我們希望放置在附屬數據緩衝區中的對象尺寸作爲輸入參數。如果我們回顧一個我們前面的介紹,我們就會發現這個宏會計算cmsghdr頭結構加上所需要的填充字符的字節長度。這個值用來設置cmsghdr對象的cmsg_len成員。
下面的例子演示瞭如果附屬數據是一個文件描述符,我們應如何來計算cmsg_len成員的值:
int fd;   /* File descriptor */
printf("cmsg_len = %d/n",CMSG_LEN(sizeof fd));
CMSG_SPACE()宏
這個宏用來計算附屬數據以及其頭部所需的總空白。儘管CMSG_LEN()宏計算了一個相似的長度,CMSG_LEN()值並不包括可能的結尾的填充字符。CMSG_SPACE()宏對於確定所需的緩衝區尺寸是十分有用的,如下面的示例代碼所示:
int fd; /* File Descriptor */
char abuf[CMSG_SPACE(sizeof fd)];
這個例子在abuf[]中聲明瞭足夠的緩衝區空間來存放頭部,填充字節以及附屬數據本身,和最後的填充字節。如果在緩衝區中有多個附屬數據對象,一定要同時添加多個CMSG_SPACE()宏調用來得到所需的總空間。
CMSG_DATA()宏
這個宏接受一個指向cmsghdr結構的指針。返回的指針值指向跟隨在頭部以及填充字節之後的附屬數據的第一個字節(如果存在)。如果指針mptr指向一個描述文件描述符的可用的附屬數據信息頭部,這個文件描述符可以用下面的代碼來得到:
struct cmsgptr *mptr;
int fd; /* File Descriptor */
. . .
fd = *(int *)CMSG_DATA(mptr);
CMSG_ALIGN()宏
這是一個Linux擴展宏,而不是Posix.1g標準的一部分。指定一個字節長度作爲輸入,這個宏會計算一個新的長度,這個新長度包括爲了維護對齊所需要的額外的填充字節。
CMSG_FIRSTHDR()宏

個宏用於返回一個指向附屬數據緩衝區內的第一個附屬對象的struct cmsghdr指針。輸入值爲是指向struct
msghdr結構的指針(不要與struct
cmsghdr相混淆)。這個宏會估計msghdr的成員msg_control與msg_controllen來確定在緩衝區中是否存在附屬對象。然
後,他會計算返回的指針。
如果不存在附屬數據對象則返回的指針值爲NULL。否則,這個指針會指向存在的第一個struct cmsghdr。這個宏用在一個for循環的開始處,來開始在附屬數據對象中遍歷。
CMSG_NXTHDR()宏
這個用於返回下一個附屬數據對象的struct cmsghdr指針。這個宏會接受兩個輸入參數:
指向struct msghdr結構的指針
指向當前struct cmsghdr的指針
如果沒有下一個附屬數據對象,這個宏就會返回NULL。
遍歷附屬數據
當接收到一個附屬數據時,我們可以使用CMSG_FIRSTHDR()與CMSG_NXTHDR()宏來在附屬數據對象中進行遍歷。下面的示例代碼顯示了for循環的通常格式以及宏的相應用法:
struct msghdr msgh;     /* Message Hdr */
struct cmsghdr *cmsg;0   /*Ptr to ancillary hdr */
int *fd_ptr;            /* Ptr to file descript.*/
int received_fd;        /* The file descriptor */
for ( cmsg=CMSG_FIRSTHDR(&msgh); cmsg!=NULL; cmsg=CMSG_NXTHDR(&msgh,cmsg) ) {
    if ( cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS ) {
        fd_ptr = (int *) CMSG_DATA(cmsg);
        received_fd = *fd_ptr;
        break;
    }
}
if ( cmsg == NULL ) {
    /* Error: No file descriptor recv'd */
}
創建附屬數據
要發送一個文件描述符的進程必須使用正確的格式化數據來創建一個附屬數據緩衝區。下面的代碼展示的通常的創建過程:
struct msghdr msg;            /* Message header */
struct cmsghdr *cmsg; /* Ptr to ancillary hdr */
int fd;              /* File descriptor to send */
char buf[CMSG_SPACE(sizeof fd)]; /* Anc. buf */
int *fd_ptr;           /* Ptr to file descriptor */
msg.msg_control = buf;
msg.msg_controllen = sizeof buf;
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof fd);
/* Initialize the payload: */
fd_ptr = (int *)CMSG_DATA(cmsg);
*fd_ptr = fd;
/*
* Sum of the length of all control
* messages in the buffer:
*/
msg.msg_controllen = cmsg->cmsg_len;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章