前言
sendmsg和recvmsg函数是一对相对下层的套接字发送、接受函数。
通过这对函数,我们能够设置或者取得数据包的一些额外的控制信息。这些信息中比較经常使用的就是本文要介绍的发送、接受接口信息。通过这对函数,我们能够指定一个数据包发送的时候使用的接口(网卡)或者获得一个接受到数据包的来源接口。
sendmsg函数使用
sendmsg(fd, &mhdr, 0)函数中最重要的就是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; };
在我们发送一个数据包之前。要首先把要发送的数据以及相关控制信息填写到这个结构体中。
这个结构体中包括的是数据以及控制信息的指针。因此在发送之前我们还须要另外为数据和控制信息分配空间。
数据就不用说了,直接char buf[MAX_MSG_SIZE]就可以。难点在于控制信息。
首先要定义一个msghdr 结构体。这个结构体用来存储全部的控制信息和发送的数据包:
struct msghdr mhdr;
接着定义控制信息结构体cmsghdr :
struct cmsghdr *cmsg;
我们要首先通过CMSG_SPACE 宏计算出控制信息cmsghdr结构体所需的空间大小。接着为控制信息结构体指针分配内存。cmsghdr的大小跟你要填写的控制消息有关。我们这里要填写的是发送接口控制信息,这个控制信息还须要一个结构体struct
in6_pktinfo结构体来承载,所以这里cmsghdr的大小则跟in6_pktinfo的大小有关:
struct in6_pktinfo pk;//定义接口控制数据结构 pk.ini_index=1;//指定接口(这里的1是接口索引號) cmsglen = CMSG_SPACE (sizeof(pk));//计算cmsghdr结构在mhdr中所需的大小 mhdr.msg_control=(void *)malloc(cmsglen);//为cmsghdr分配内存,并在mhdr中填写控制信息的指针 mhdr.msg_controllen=cmsglen; cmsg = CMSG_FIRSTHDR (&mhdr);//我们利用CMSG_FIRSTHDR找到控制信息在mhdr结构体中的真正地址 //利用CMSG_LEN算出控制信息cmsghdr结构中真正的控制数据的长度 cmsg->cmsg_len = CMSG_LEN(sizeof(*pk)); cmsg->cmsg_level = IPPROTO_IPV6;//填写cmsghdr控制信息结构体 cmsg->cmsg_type = IPV6_PKTINFO; //CMSG_DATA(cmsg) 能够找到真正的控制信息在cmsghdr结构体中的位置,之后我们就能够利用memcpy把控制结构体的数据复制到cmsg中: memcpy(CMSG_DATA(cmsg), pk, sizeof(*pk));
以上是发送的过程。接收过程跟发送过程类似。
最好需要注意的是。要发送以及接收这些控制信息,我们必需要设置套接字选项:
int val = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)) < 0) return -1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVDSTOPTS, &val, sizeof(val)) < 0) return -1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVRTHDR, &val, sizeof(val)) < 0) return -1;