继续学习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. 2019年Java面试题基础系列228道,题目汇总,可以先看会多少

    Java面试题(一) 1.面向对象的特征有哪些方面? 2.访问修饰符 public,private,protected,以及不写(默认)时的区别? 3.String 是最基本的数据类型吗? 4.flo ...

  2. javascript bom操作

    BOM BOM介绍 全称 Browser Object Mode 浏览器对象模式 操作浏览器的API接口.比如浏览器自动滚动 Windows对象的顶层部分是BOM的顶层(核心)对象,所有的对象都是通过 ...

  3. Eclipse 常用配置和基本调试

    常用配置 1.显示行号:window->Preferences->General->Editors->Text Editors , 勾选show line numbers 2. ...

  4. htm5手机端实现拖动图片

    htm5手机端实现拖动图片 <pre> <!doctype html><html><head> <title>Mobile Cookbook ...

  5. AJAX-前后端交互的艺术

    AJAX-前后端交互的艺术 为什么要用AJAX? 当我们通过提交表单向服务器提交内容,或者进行一些其他操作,均涉及到了与浏览器之间的交互,传统的方式与AJAX方式的处理方法是不同的 传统方式:用户触发 ...

  6. [转帖]nginx基础整理

    nginx基础整理 https://www.cnblogs.com/guigujun/p/6588545.html 目录结构如下: Nginx基础知识 Nginx HTTP服务器的特色及优点 Ngin ...

  7. crontab每小时运行一次

    先给出crontab的语法格式 对于网上很多给出的每小时定时任务写法,可以说绝大多数都是错误的!比如对于下面的这种写法: 00 * * * * #每隔一小时执行一次 00 */1 * * * #与上面 ...

  8. python 之 面向对象基础(继承与派生,经典类与新式类)

    7.2 继承与派生 7.21继承 1.什么是继承? 继承是一种新建类的的方式,在python中支持一个子类继承多个父类.新建的类称为子类或者派生类,父类又可以称为基类或者超类,子类会”遗传“父类的属性 ...

  9. Scala 面向对象编程之Trait

    将trait作为接口使用 // Scala中的Triat是一种特殊的概念 // 首先我们可以将Trait作为接口来使用,此时的Triat就与Java中的接口非常类似 // 在triat中可以定义抽象方 ...

  10. bash 和 powershell 常用命令集锦

    Linux Shell # 1. 后台运行命令 nohup python xxx.py & # 查找替换 ## 只在目录中所有的 .py 和 .dart 文件中递归搜索字符"main ...