簡介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;
發佈了10 篇原創文章 · 獲贊 6 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章