test-stream.c
参考:linux文件描述符传递
http://blog.csdn.net/sparkliang/article/details/5486069
http://blog.csdn.net/sparkliang/article/details/5490242
spice-server
版本:spice-0.14.0
测试工程:test-stream.c
struct RedsStream {
int socket;
SpiceWatch *watch;
RedsStreamPrivate *priv;
};
int main(int argc, char *argv[])
{
RedsStream *st[2];//结构体定义
int sv[2];
int ret, fd = -1;
char c;
//spice定义的断言
spice_return_val_if_fail(server_init() == 0, -1);
//创建socketpair:create a pair of connected sockets
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv) == -1) {
spice_error("socketpair failed %s", strerror(errno));
return -1;
}
//创建两个RedsStream 结构体
st[0] = reds_stream_new(server, sv[0]);
spice_assert(reds_stream_is_plain_unix(st[0]));
st[1] = reds_stream_new(server, sv[1]);
spice_assert(reds_stream_is_plain_unix(st[1]));
/* send stdin, for the fun of it */
ret = reds_stream_send_msgfd(st[0], 0); //关键函数:发送
spice_assert(ret == 1); //sendmsg系统调用返回值为1,为发送的一个字节
ret = sock_fd_read(sv[1], &c, 1, &fd);//关键函数:接收
spice_assert(c == '@'); //断言是否为接收的数据‘@’
spice_assert(ret == 1); //recvmsg系统调用返回值为1,位接收一个字节
spice_assert(fd != -1); //reds_stream_send_msgfd函数将文件描述符"0",也就是标准输入传给了Redstream st[1],也就是传入的fd。
close(fd); //将fd,就是标准输入关闭 本进程的。 每一个进程默认都有标准输入,标准输出, 标准错误输出
/* send invalid fd behaviour */ //此时fd等于0,参考下面的关键函数。 走另一条路径sendmsg/read 上面的收发是sendmsg/recvmsg
ret = reds_stream_send_msgfd(st[0], -1);
spice_assert(ret == 1);
ret = sock_fd_read(sv[1], &c, 1, &fd);
spice_assert(c == '@');
spice_assert(ret == 1);
spice_assert(fd == -1);
/* batch test */ //模拟发送一批。但是只发送了两次
ret = reds_stream_send_msgfd(st[0], 0);
spice_assert(ret == 1);
ret = reds_stream_send_msgfd(st[0], 0);
spice_assert(ret == 1);
ret = sock_fd_read(sv[1], &c, 1, &fd);
spice_assert(c == '@');
spice_assert(ret == 1);
spice_assert(fd != -1);
close(fd);
ret = sock_fd_read(sv[1], &c, 1, &fd);
spice_assert(c == '@');
spice_assert(ret == 1);
spice_assert(fd != -1);
close(fd);
reds_stream_free(st[0]);
reds_stream_free(st[1]);
return 0;
}
//核心关键函数
int reds_stream_send_msgfd(RedsStream *stream, int fd)
{
struct msghdr msgh = { 0, };
struct iovec iov;
int r;
const size_t fd_size = 1 * sizeof(int);
struct cmsghdr *cmsg;
// 使用联合体,保证cmsghdr和msg_control的对齐
union {
struct cmsghdr hdr;
char data[CMSG_SPACE(fd_size)];
} control;
spice_return_val_if_fail(reds_stream_is_plain_unix(stream), -1);
/* set the payload */
iov.iov_base = (char*)"@"; //发送的ascii码,一个字符
iov.iov_len = 1; //iov.iov_base的长度
msgh.msg_iovlen = 1; //只发送一个 struct iovec
msgh.msg_iov = &iov;
if (fd != -1) {//main中的第一个测试。ret = reds_stream_send_msgfd(st[0], 0);
//上面的union体与msgh结构体建立联系,为辅助缓冲区和长度
msgh.msg_control = control.data;
msgh.msg_controllen = sizeof(control.data);
/* CMSG_SPACE() might be larger than CMSG_LEN() as it can include some
* padding. We set the whole control data to 0 to avoid valgrind warnings
*/
memset(control.data, 0, sizeof(control.data));//清空数据
cmsg = CMSG_FIRSTHDR(&msgh);//从辅助缓冲区中取出一个辅助缓冲区,下面设置必要的字段值
cmsg->cmsg_len = CMSG_LEN(fd_size);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(cmsg), &fd, fd_size); //要传递的文件描述符
}
//发送数据,不成功不罢休。
do {
r = sendmsg(stream->socket, &msgh, MSG_NOSIGNAL);
} while (r < 0 && (errno == EINTR || errno == EAGAIN));
return r;
}
//关键函数,接收数据
static ssize_t
sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd)
{
ssize_t size;
if (fd) {
struct msghdr msg;
struct iovec iov;
union {//数据对齐
struct cmsghdr cmsghdr;
char control[CMSG_SPACE(sizeof (int))];
} cmsgu;
struct cmsghdr *cmsg;
//设置接收缓冲区与长度
iov.iov_base = buf;
iov.iov_len = bufsize;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
//设置辅助缓冲区与长度
msg.msg_control = cmsgu.control;
msg.msg_controllen = sizeof(cmsgu.control);
size = recvmsg(sock, &msg, 0); //接收数据
if (size < 0) {
perror ("recvmsg");
exit(1);
}
cmsg = CMSG_FIRSTHDR(&msg);//取出第一个辅助缓冲区数据指针地址
if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
if (cmsg->cmsg_level != SOL_SOCKET) {
fprintf(stderr, "invalid cmsg_level %d\n",
cmsg->cmsg_level);
exit(1);
}
if (cmsg->cmsg_type != SCM_RIGHTS) {
fprintf(stderr, "invalid cmsg_type %d\n",
cmsg->cmsg_type);
exit(1);
}
//判读有效后,复制给fd ,文件描述符传输成功。
memcpy(fd, CMSG_DATA(cmsg), sizeof(*fd));
} else
*fd = -1;
} else {
size = read(sock, buf, bufsize);
if (size < 0) {
perror("read");
exit(1);
}
}
return size;
}
[root@net01 tests]# ./test-stream
event_loop_channel_event: channel event con, type, id, event: 0, 0, 0, 3
event_loop_channel_event: channel event con, type, id, event: 0, 0, 0, 3
[root@net01 tests]# pwd
/root/spice-0.14.0/server/tests
[root@net01 tests]#