|
本文是在其他博文基础上,进行总结汇总,以便能够通过该文章,就能够直接理解其原理和使用方法。
介绍使用方法之前,我们需要先了解几个知识点!
一、 联合的概念union
1、union中可以定义多个成员, union的大小由最大的成员的大小决定。
2、union成员共享同一块大小的内存, 一次只能使用其中的一个成员。
3、对某一个成员赋值,会覆盖其他成员的值(因为他们共享一块内存。
但前提是成员所占字节数相同,当成员所占字节数不同时只会覆盖相应字节上的值,
比如对char成员赋值就不会把整个int成员覆盖掉,
因为char只占一个字节,而int占四个字节
4、联合体union的存放顺序是所有成员都从低地址开始存放的。
二、 msghdr和cmsghdr结构体的关系
列出结构体,后面并给出了说明
struct msghdr {
void *msg_name; /* optional address 通道是数据报套接口才需要设置,执行要发送或者接受信息的套接口地址*/
socklen_t msg_namelen; /* size of address 套接口地址长度*/
struct iovec *msg_iov; /* scatter/gather array 实际的数据缓冲区*/
size_t msg_iovlen; /* # elements in msg_iov */
void *msg_control; /* ancillary data辅助数据, 通常指向结构体struct cmsghdr */
socklen_t msg_controllen; /* ancillary data buffer len */
int msg_flags; /* flags on received message */
};
struct cmsghdr {
socklen_t cmsg_len; /* data byte count, including header */
int cmsg_level; /* originating protocol */
int cmsg_type; /* protocol-specific type */
/* followed by unsigned char cmsg_data[]; */
};
三、辅助数据结构体的函数说明
1. 查询手册命令进行了解: man 3 cmsg
2. 函数说明
CMSG_FIRSTHDR()返回msghdr关联的辅助数据中的第一个cmsghdr的指针。
CMSG_SPACE()返回一个辅助数据所占用的字节数。这是一个常量表达式。
传入的参数length指的是一个控制信息元素(即一个cmsghdr结构体)后面数据部分的字节数,
返回的是这个控制信息的总的字节数,即包含了头部(即cmsghdr各成员)、数据部分和填充数据的总和。
CMSG_DATA()返回cmsghdr的数据部分指针。
CMSG_LEN()返回cmsghdr结构的cmsg_len成员的值,包括cmsghdr头结构加上附属数据大小,不包括最后填充数据的部分 3. 下面画个图说明各个数据之间的关系,以便加深理解

四、 客户端和服务端代码
客户端创建文件描述符,然后传递给服务端,服务端执行写操作。
1. 客户端代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <fcntl.h>
#define clog_info(...) do { \
printf("<%s %s> %s %s:%d INFO:",__DATE__, __TIME__,__FUNCTION__,__FILE__,__LINE__)&& \
printf(__VA_ARGS__)&& \
printf("\n"); \
}while(0)
#define UNIXSTR_PATH "foo.socket"
#define FILE_NAME "test"
//保证cmsghdr和msg_control对齐
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
int main(int argc, char *argv[])
{
int clifd = -1;
int ret = -1;
int filefd = -1;
char buf[100] = {0};
struct sockaddr_un servaddr;
struct msghdr msg;
struct iovec iov[1];
struct cmsghdr *pcmsg;
// 1. 创建套接字
clifd = socket(AF_UNIX, SOCK_STREAM, 0) ;
if ( clifd < 0 ) {
clog_info ( "socket failed" ) ;
return -1 ;
}
// 2. 创建文件描述符
filefd = open(FILE_NAME ,O_CREAT | O_RDWR, 0777);
if( filefd < 0 ) {
clog_info("open test failed.");
return -1;
}
bzero (&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_UNIX;
strcpy ( servaddr.sun_path, UNIXSTR_PATH);
// 3. 连接服务器端
ret = connect(clifd, (struct sockaddr*)&servaddr, sizeof(servaddr));
if(ret < 0) {
clog_info ( "connect failed." ) ;
return -1;
}
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = buf;
iov[0].iov_len = 100;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
//设置缓冲区和长度
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
//直接通过CMSG_FIRSTHDR取得附属数据
pcmsg = CMSG_FIRSTHDR(&msg);
pcmsg->cmsg_len = CMSG_LEN(sizeof(int));
pcmsg->cmsg_level = SOL_SOCKET;
pcmsg->cmsg_type = SCM_RIGHTS; // 指明发送的是描述符
*((int*)CMSG_DATA(pcmsg)) = filefd; // 把文件描述符写入辅助数据
// 4. 发送文件描述符
ret = sendmsg(clifd, &msg, 0);
clog_info ("ret = %d, filedescriptor = %d", ret, filefd);
return 0 ;
}
2. 服务端代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <fcntl.h>
#define slog_info(...) do { \
printf("<%s %s> %s %s:%d INFO:",__DATE__, __TIME__,__FUNCTION__,__FILE__,__LINE__)&& \
printf(__VA_ARGS__)&& \
printf("\n"); \
}while(0)
#define UNIXSTR_PATH "foo.socket"
union { //对齐
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
int main(int argc, char *argv[])
{
int clifd = -1, listenfd = -1;
int ret = -1;
int recvfd = -1;
char buf[100] = {0};
char *testmsg = "test msg.\n";
struct sockaddr_un servaddr, cliaddr;
struct msghdr msg;
struct iovec iov[1];
struct cmsghdr * pcmsg;
socklen_t clilen;
// 1. 创建监听套接字
listenfd = socket ( AF_UNIX , SOCK_STREAM , 0 ) ;
if(listenfd < 0) {
slog_info ( "socket failed." ) ;
return -1;
}
unlink(UNIXSTR_PATH) ;
bzero (&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_UNIX;
strcpy ( servaddr.sun_path , UNIXSTR_PATH ) ;
// 2. 绑定套接字, 并设置监听
ret = bind ( listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
if(ret < 0) {
slog_info ( "bind failed. errno = %d." , errno ) ;
close(listenfd);
return -1 ;
}
listen(listenfd, 5);
while(1) {
// 3. 接受连接
clilen = sizeof( cliaddr );
clifd = accept( listenfd, (struct sockaddr*)&cliaddr , &clilen);
if ( clifd < 0 ) {
slog_info ( "accept failed." ) ;
continue ;
}
msg.msg_name = NULL;
msg.msg_namelen = 0;
//设置数据缓冲区
iov[0].iov_base = buf;
iov[0].iov_len = 100;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
//设置辅助数据缓冲区和长度
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control) ;
// 4. 接受数据
ret = recvmsg(clifd , &msg, 0);
if( ret <= 0 ) {
slog_info ( "recvmsg failed." ) ;
return ret;
}
//5. 检查是否收到了辅助数据,以及长度
if((pcmsg = CMSG_FIRSTHDR(&msg) ) != NULL && ( pcmsg->cmsg_len == CMSG_LEN(sizeof(int)))) {
if ( pcmsg->cmsg_level != SOL_SOCKET ) {
slog_info("cmsg_leval is not SOL_SOCKET");
continue;
}
if ( pcmsg->cmsg_type != SCM_RIGHTS ) {
slog_info ( "cmsg_type is not SCM_RIGHTS" );
continue;
}
// 6. 接收到文件描述符
recvfd = *((int*)CMSG_DATA(pcmsg));
slog_info ( "recv fd = %d", recvfd );
// 7. 对文件描述符执行写操作
write ( recvfd, testmsg, strlen(testmsg) + 1);
}
}
return 0 ;
}
|