继续学习socket编程,今天的内容会有些难以理解,一步步来分解,也就不难了,正入正题:

实际上sockpair有点像之前linux系统编程中学习的pipe匿名管道匿名管道是半双工的,只能用于亲缘关系的进程间进行通信,也就是说父子进程或兄弟进程间进行通讯,因为它是没有名称的,父子进程可以通过共享描述符的方式来进行通信,子进程继承了父进程的文件描述符,从而达到了通信的目的。而今天学习的sockpair是一个全双工的流管道,其它也一样,也只能用于父子进程或亲缘关系之间进行通讯,所以其中sv套接字对就很容易理解了,但是跟pipe有些区别,先来回顾下pipe:

其中的fd也是套接字对,一端表示读,一端表示写,而sockpair中的sv[0]、sv[1]两端分别既可以表示读端,也可以表示写端。

认识了sockpair函数原形,下面用程序来说明它的用法:

首先创业一个套接字对:

由于它也是只能用于父子进程或亲缘关系之间进行通讯,所以需要fork进程出来:

下面就来实现父子进程进行通讯:

而对于子进程而言,代码基本类似:

编译运行看结果:

从结果运行来看,通过sockpair就完成了全双工的通讯。

学习这两个函数的目的,是为了通过UNIX域协议如何传递文件描述字,关于这个函数的使用会比较复杂,需慢慢理解。

首先来查看一下man帮助:

其中第二个参数是msghdr结构体,所以有必要来研究一下这个结构体:

哇,这个结构体貌似挺复杂的,下面一一来熟悉其字段含义:

这时,需要来看另外一个函数了,该结构体在其中有介绍到:

那怎么理解该参数呢?这个需要从send函数来分析:

所以iovec结构体的字段就可以从send的这两个参数来理解:

并且,可以发现:

下面来看一个示意图:

从上面示意图中可以发现,如果用sendmsg函数,就可以发送多个缓冲区的数据了,而如果用send只能发送一个缓冲区,所以从这也可以看出sendmsg的强大。

如果说要传递文件描述字,还需要发送一些辅助的数据,这些辅助数据是一些控制信息,也就是下面这些参数:

而其中msg_control是指向一个结构体,那它长啥样呢?需要从另外一个函数的帮助文档中得知:

那具体属性的含议是啥呢?

实际上,在填充这些数据的时候,并没有这么简单,它还会按照一定的方式来进行对齐,接下来再来看另外一个示意图---辅助数据的示意图:

其中可以看到定义了一些宏,这是由于:

所以,下面来认识一下这些宏定义:

其中"size_t CMSG_SPACE(size_t length)",结合图来说明就是:

大致了解了以上这些数据结构,下面则可以开始编写代码来传递描述字了,但是代码会比较复杂,可以一步步来理解,下面开始。

实际上,就是能过以下两个函数来封装发送和接收文件描述字的功能,如下:

首先封装发送文件描述字的方法:

下面一步步来实现该函数,首先准备第二个参数:

所以,先声明一个该结构体:

接下来填充里面的各个字段,还是看图说话:

所以:

接下来指定缓冲区:

最后则要准备辅助数据了,因为我们是发送文件描述字,这也是最关键的:

所以msg_control需要指向一个辅助数据的缓冲区,其大小根据发送的文件描述符来获得,如下:

接下来,则需要准备缓冲区中cmsghdr中的数据,也就是发送文件描述字主要是靠它:

另外关于数据的填充我们不需要关心,因为都是用系统提供的宏来操作数据的,当所有的数据都准备好之后,下面则可以开始发送了:

接下来,则需要封装一个接收文件描述字的函数了,由于怎么发送文件描述字已经很明白了,所以接收也就很简单了,基本类似,这里面就不一一进行说明了:

以上发送和接收文件描述字的函数都已经封装好了,接下来利用这两个函数来实现文件描述字的真正传递实验,实验的思路是这样:如果父进程打开了一个文件描述字,再fork()时,子进程是能共享父进程的文件描述字的,也就是只要在fork()之前,打开文件描述字,子进程就能共享它;但是当fork()进程之后,如果一个子进程打开一个文件描述字,父进程是无法共享获取的,所以,这里就可以利用这个原理,来将文件描述字从子进程传递给父进程,还是用sockpair函数,具体如下:

下面编译运行看一下效果:

另外,文件描述字的传递,只能通过UNIX域协议的套接字,当前是利用了sockpair函数来实现了父子进程文件描述字的传递,而如果要实现不相关的两个进程之间传递,就不能用socketpair了,就得用上一节中介绍的UNIX域套接字来进行传递,而普通的TCP套接字是不能传递文件描述字的,这个是需要明白了。

最后贴出代码:

send_fd.c:

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() void send_fd(int sock_fd, int send_fd)
{
struct msghdr msg;
struct iovec vec;
struct cmsghdr *p_cmsg; char sendchar = ;
vec.iov_base = &sendchar;
vec.iov_len = sizeof(sendchar); msg.msg_name = NULL;
msg.msg_namelen = ;
msg.msg_iov = &vec;
msg.msg_iovlen = ; char cmsgbuf[CMSG_SPACE(sizeof(send_fd))]; msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf); p_cmsg = CMSG_FIRSTHDR(&msg);
p_cmsg->cmsg_level = SOL_SOCKET;
p_cmsg->cmsg_type = SCM_RIGHTS;
p_cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd));
int *p_fds;
p_fds = (int*)CMSG_DATA(p_cmsg);
*p_fds = send_fd; int ret;
ret = sendmsg(sock_fd, &msg, );
if (ret != )
ERR_EXIT("sendmsg");
} int recv_fd(const int sock_fd)
{
int ret;
struct msghdr msg;
char recvchar;
struct iovec vec;
int recv_fd;
char cmsgbuf[CMSG_SPACE(sizeof(recv_fd))];
struct cmsghdr *p_cmsg;
int *p_fd;
vec.iov_base = &recvchar;
vec.iov_len = sizeof(recvchar);
msg.msg_name = NULL;
msg.msg_namelen = ;
msg.msg_iov = &vec;
msg.msg_iovlen = ;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
msg.msg_flags = ; p_fd = (int*)CMSG_DATA(CMSG_FIRSTHDR(&msg));
*p_fd = -;
ret = recvmsg(sock_fd, &msg, );
if (ret != )
ERR_EXIT("recvmsg"); p_cmsg = CMSG_FIRSTHDR(&msg);
if (p_cmsg == NULL)
ERR_EXIT("no passed fd"); p_fd = (int*)CMSG_DATA(p_cmsg);
recv_fd = *p_fd;
if (recv_fd == -)
ERR_EXIT("no passed fd"); return recv_fd;
} int main(void)
{
int sockfds[]; if (socketpair(PF_UNIX, SOCK_STREAM, , sockfds) < )
ERR_EXIT("socketpair"); pid_t pid;
pid = fork();
if (pid == -)
ERR_EXIT("fork"); if (pid > )
{
close(sockfds[]);
int fd = recv_fd(sockfds[]);
char buf[] = {};
read(fd, buf, sizeof(buf));
printf("buf=%s\n", buf);
}
else if (pid == )
{
close(sockfds[]);
int fd;
fd = open("test.txt", O_RDONLY);
if (fd == -);
send_fd(sockfds[], fd);
}
return ;
}

今天学的东西有点复杂,主要是得搞清楚其结构体的填充,对照着示意图来其实也不难,需要好好消化,下节再见~~

linux网络编程之socket编程(十六)的更多相关文章

  1. linux网络编程之socket编程(十五)

    今天继续学习socket编程,这次主要是学习UNIX域协议相关的知识,下面开始: [有个大概的认识,它是来干嘛的] ①.UNIX域套接字与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍. ...

  2. linux网络编程之socket编程(十二)

    今天继续学习socket编程,期待的APEC会议终于在京召开了,听说昨晚鸟巢那灯火通明,遍地礼花,有点08年奥运会的架势,有种冲动想去瞅见一下习大大的真容,"伟大的祖国,我爱你~~~&quo ...

  3. linux网络编程之socket编程(十)

    今天继续socket编程的学习,最近晚上睡觉都没有发热,没有暖气的日子还是种煎熬,快乐的十一也已经走来,幸福有暖气的日子也快啦,好了,回到正题~ ①close终止了数据传送的两个方向. ②shutdo ...

  4. linux网络编程之socket编程(十四)

    经过令国鸡冻的APEC会之后,北京的冬天终于不冷了,有暖气的日子就是倍儿爽呀~~洗完热水澡,舒服的躺在床上欢乐地敲打着键盘,是件多么幸福的事呀,好了,抒发情感后,正题继续. 上节中已经初步学习了UDP ...

  5. linux网络编程之socket编程(六)

    经过一个国庆长假,又有一段时间没有写博文了,今天继续对linux网络编程进行学习,如今的北京又全面进入雾霾天气了,让我突然想到了一句名句:“真爱生活,珍惜生命”,好了,言归正传. 回顾一下我们之间实现 ...

  6. linux网络编程之socket编程(四)

    经过两周的等待,终于可以回归我正常的学习之旅了,表哥来北京了在我这暂住,晚上回家了基本在和他聊天,周末带他在北京城到处乱转,几乎剥夺了我自由学习的时间了,不过,亲人之情还是很难得的,工作学习并不是生活 ...

  7. linux网络编程之socket编程(一)

    今天开始,继续来学习linux编程,这次主要是研究下linux下的网络编程,而网络编程中最基本的需从socket编程开始,下面正式开始学习: 什么是socket: 在学习套接口之前,先要回顾一下Tcp ...

  8. linux网络编程之socket编程(八)

    学习socket编程继续,今天要学习的内容如下: 先来简单介绍一下这五种模型分别是哪些,偏理论,有个大致的印象就成,做个对比,因为最终只会研究一个I/O模型,也是经常会用到的, 阻塞I/O: 先用一个 ...

  9. linux网络编程之socket编程(七)

    今天继续学习socket编程,北京在持续几天的雾霾天之后久违的太阳终于出来了,心情也特别特别的好,于是乎,在这美好的夜晚,该干点啥事吧,那当然就是继续坚持我的程序学习喽,闲话不多说,进入正题: 通过这 ...

随机推荐

  1. 基于TreeSoft实现异构数据同步

    一.为了解决数据同步汇聚,数据分发,数据转换,数据维护等需求,TreeSoft将复杂的网状的同步链路变成了星型数据链路.     TreeSoft作为中间传输载体负责连接各种数据源,为各种异构数据库之 ...

  2. 14、OpenCV实现图像的空间滤波——图像锐化及边缘检测

    1.图像锐化理论基础 1.锐化的概念 图像锐化的目的是使模糊的图像变得清晰起来,主要用于增强图像的灰度跳变部分,这一点与图像平滑对灰度跳变的抑制正好相反.而且从算子可以看出来,平滑是基于对图像领域的加 ...

  3. 高级UI-事件传递

    事件传递在Android中有着举足轻重的作用,那么事件的传递在Android中又是怎么样实现的呢,在这里我们将进一步探讨Android的事件传递机制 从一个例子入手 首先是一个简单的onTouch和o ...

  4. 走进热修复框架AndFix的世界

    关于阿里的开源热修复框架AndFix引起了广泛共鸣,受到了很多人的青睐.那今天就跟随我的步伐来详细了解一下AndFix的详细信息和使用方法.1.什么是AndFix? AndFix是阿里巴巴出的一个专门 ...

  5. linu SSH 不在同一网段的两台机器如何通过ssh通信,SSH限制特定网段登陆的方法

    1. linu SSH 不在同一网段的两台机器如何通过ssh通信 https://blog.csdn.net/lhf19891003/article/details/39895763 https:// ...

  6. Spring+SpringMVC+Mybatis(SSM)框架集成搭建

    Spring+SpringMVC+Mybatis框架集成搭建教程 一.背景 最近有很多同学由于没有过SSM(Spring+SpringMvc+Mybatis , 以下简称SSM)框架的搭建的经历,所以 ...

  7. 创建线程的三种方式(Thread、Runnable、Callable)

    方式一:继承Thread类实现多线程: 1. 在Java中负责实现线程功能的类是java.lang.Thread 类. 2. 可以通过创建 Thread的实例来创建新的线程. 3. 每个线程都是通过某 ...

  8. WSL部署记录

    1. 管理员打开PowerShell,开启WSL功能: Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Sub ...

  9. 彻底关闭networkmanager

    chkconfig NetworkManager offsystemctl stop NetworkManagersystemctl disable NetworkManager

  10. JAVA堆,栈的区别,用AarrayList、LinkedList自定义栈

    大家都知道java模拟机在运行时要开辟空间所以它有特定的五个内存划分: 1.寄存器:    2.本地方法区:    3.方法区:    4.栈内存:    5.堆内存: 但是我们今天来注重讲一下栈和堆 ...