APUE编程:134---进程间通信(命名域套接字:struct sockaddr_un)
一、域套接字地址(struct sockaddr_un)
- 域套接字是在同一计算机的不同进程中进行通信,但是不采用IP地址和端口进行通信。内核提供了此结构体,用于为域套接字绑定文件名来绑定地址,从而实现不同域套接字之间的通信
#include <sys/un.h>//Linux和Solaris中:
struct sockaddr_un
{sa_family_t sun_family; /*AF_UNIX*/char sun_path[108]; /*pathname*/
}//FreeBSD和Max OS X中
struct sockaddr_un
{unsigned char sun_len; /*sockaddr length*/sa_family_t sun_family; /*AF_UNIX*/char sun_path[104]; /*pathname*/
}
sun_len:
- sockaddr_un套接字地址的长度,不是每个系统都实现该成员
sun_family:
- AF_UNIX或者AF_LOCAL
- 备注:POSIX域协议重新命名为“本地IPC”,以消除对于Unix操作系统的依赖。历史性常值AF_UNIX变为AF_LOCAL。尽管如此,我们依然使用“Unix域”这个称谓
sun_path:
- 给出一个路径文件名,带代表域套接字地址,必须以空字符结尾(为了标识域套接字的地址,我们采用文件系统中的路径名作为域套接字的地址,这与网络套接字不同)
- 这些路径名不是普通的文件:除非把它们和域套接字关联起来,否则无法直接读写这些文件
- 数组的大小:BSD早起版本定义sun_path的大小为108字节。POSIX没有定义sun_path的大小,而且明确警示应用程序不应该假设一个特定长度。所以应用程序在运行时要使用sizeof得到本结构的长度,再验证一个路径名是否适合存放到sun_path中(数组长度很可能在92到108之间)
- 如果sun_path是一个空字符串:那么绑定此结构体的域套接字地址等价于IPV4的INADDR_ANY常值或者IPV6的IN6ADDR_ANY_INIT常值
- 如果不确定给出哪一个文件名,可以使用tmpnam函数给出一个临时文件名称,使用案例见文章:https://blog.csdn.net/qq_41453285/article/details/101753011
SUN_LEN宏:
- 功能:参数为一个sockaddr_un结构的指针,此宏返回此结构的长度,其中包括路径名中非空字节数
offsetof宏:
- 该宏可以以字节为单位,返回一个结构中指定成员在结构体中的偏移量,我们可以用该宏来判断sun_path在域套接字地址中的大小,使用案例见文章:https://blog.csdn.net/qq_41453285/article/details/90742619
S_IFSOCK类型文件
- 当我们将一个地址(sockaddr_un)绑定到一个域套接字上之后,系统会根据sockaddr_un结构体中的sun_path创建一个S_IFSOCK类型的文件
- 该文件仅用于向客户进程告示套接字名字。文件无法打开,也不能由应用程序用于通信
- 注意事项:当我们绑定同一地址时,如果sun_path给出的文件已经存在,那么bind就会失败。当关闭套接字时,并不自动删除该文件,所以要确保在应用程序退出前,对该文件执行解除链接操作,或者在绑定地址之前确定该文件不存在
二、使用网络套接字API的一些异同点
域套接字也使用网络套接字的API,但是使用起来与网络套接字之间的通信还是存在一定的异同点:
- ①域套接字地址中的路径名默认访问权限为0777,并按照当前系统的umaks值进行修正
- ②域套接字关联的路径名应该是一个绝对路径名,千万不能使用相对路径名
- ③域套接字两端进行通信时,它们的套接字类型(字节流或数据报)也必须一致
- ④调用connect连接一个域套接字设计的权限测试等同于调用open以只写方式访问相应的路径名
- ⑤域字节流类似TCP套接字:它们都为进程提供一个无记录边界的字节流连接口
- ⑥域数据报套接字类似于UDP套接字:它们都提供一个保留记录边界的不可靠的数据报服务
- ⑦如果对于某个域套接字的connect调用发现这个监听套接字的队列已满,调用就立即返回一个ECONNREFUSED错误。这一点不同于TCP:如果TCP监听套接字的队列已满,TCP监听就忽略新到达的SYN,而TCP连接发起端将数次发送SYN进行重试
- ⑧在一个未绑定的域套接字上发送数据报不会自动给这个套接字绑定一个路径名。这一点不同于UDP:如果在一个未绑定的UDP套接字上发送UDP数据报,系统会给这个套接字绑定一个临时端口
- ⑨与⑧类似,对于某个于域数据报套接字的connect调用不会自动给域套接字绑定一个路径名,这一点不同于TCP和UDP
三、命名套接字的绑定(bind)
- 绑定时,要注意绑定的地址的文件必须是系统中不存在的文件,且使用绝对路径
- 绑定的地址中的文件的访问权限最好是0777,然后按照当前umask进行修正
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <errno.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <sys/types.h>int main(int argc, char **argv)
{int sockfd;socklen_t len;struct sockaddr_un addr1, addr2;if (argc != 2)perror("usage: unixbind <pathname>");if((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0))==-1){perror("socket");exit(EXIT_FAILURE);}unlink(argv[1]);bzero(&addr1, sizeof(addr1));addr1.sun_family = AF_LOCAL;strncpy(addr1.sun_path, argv[1], sizeof(addr1.sun_path)-1);if(bind(sockfd, (struct sockaddr *) &addr1, SUN_LEN(&addr1))==-1){perror("bind");exit(EXIT_FAILURE);}len = sizeof(addr2);if(getsockname(sockfd, (struct sockaddr *) &addr2, &len)==-1){perror("getsockname");exit(EXIT_FAILURE);}printf("bound name = %s, returned len = %d\n", addr2.sun_path, len);exit(0);
}
unlink函数的使用
- bind时,如果套接字地址中指定的sun_path已经存在,那么bind就会失败,所以在bind之前我们调用unlink删除这个路径名,以防它已经存在。如果文件本身就不存在,那么unlink返回一个我们可以忽略的错误
strncpy函数的使用
- 命令行参数是我们输入的要绑定的域套接字地址的路径名,我们使用strncpy将命令行参数拷贝进域套接字地址的sun_path成员中
- 又因为sun_path成员必须以空字符结尾,我们在strncpy之前用bzero函数将域套接字地址初始化为0,并然后strncpy函数的最后一个参数大小为sun_path数组的大小减去1,所以可以肯定该路径名将以空字符结尾
SUN_LEN宏的使用
- 在bind的时候,我们使用SUN_LEN宏获取域套接字地址的大小
getsockname函数的使用
- 我们为了试验,在bind成功之后我们调用getsockname函数将绑定的路径名显示出来
演示结果:
- returned len返回13:sun_family占2个字节,路径名占11个字节(扣除结尾的空字符)
- 我们使用printf输出文件名,因为sun_path是以空字符结尾的
- 我们可以连续两次运行该程序,而不bind失败,是因为在bind之前我们使用unlink删除了路径名
- ls -l查看:该文件是一个socket类型的文件(s),并且其访问权限根据umask的结果进行了修改
- ls -lF查看:F参数的作用是在文件的微末加上该文件的类型描述符,后面添加了一个“=”号,说明该文件是一个socket文件,ls命令具体信息见文章:https://blog.csdn.net/qq_41453285/article/details/85028945
四、演示案例
- 客户端和服务端启动时传递一个文件名,作为服务端域套接字地址的sun_path成员
- 客户端与服务端进行数据的交互
//server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>void echo_with_client(int clientFd);int main(int argc,char *argv[])
{int serverFd,clientFd;pid_t pid;struct sockaddr_un serverAddr;if((serverFd=socket(AF_UNIX,SOCK_STREAM,0))==-1){perror("scoket");exit(EXIT_FAILURE);}bzero(&serverAddr,sizeof(serverAddr));serverAddr.sun_family=AF_UNIX;bcopy(argv[1],serverAddr.sun_path,strlen(argv[1]));if(bind(serverFd,(struct sockaddr*)&serverAddr,sizeof(serverAddr))==-1){perror("bind");exit(EXIT_FAILURE);}if(listen(serverFd,10)==-1){perror("listen");exit(EXIT_FAILURE);}printf("accept......\n");for(;;){if((clientFd=accept(serverFd,NULL,NULL))==-1){continue;}if((pid=fork())==0){echo_with_client(clientFd);exit(EXIT_SUCCESS);}else if(pid<0){perror("fork");close(clientFd);continue;}close(clientFd);}return 0;
}void echo_with_client(int clientFd)
{for(;;){int len=0;char buff[1024];bzero(buff,sizeof(buff));if(read(clientFd,buff,sizeof(buff))==-1){perror("read");exit(EXIT_FAILURE);return;}printf("recv:%s\n",buff);printf("Enter:");fflush(stdout);bzero(buff,sizeof(buff));len=read(0,buff,sizeof(buff)-1);if(len>0){if(write(clientFd,buff,strlen(buff))==-1){perror("write");exit(EXIT_FAILURE);return;}}else{perror("read");exit(EXIT_FAILURE);return;}}
}
//client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>void echo_with_client(int clientFd);int main(int argc,char *argv[])
{int clientFd;struct sockaddr_un serverAddr;if((clientFd=socket(AF_UNIX,SOCK_STREAM,0))==-1){perror("scoket");exit(EXIT_FAILURE);}bzero(&serverAddr,sizeof(serverAddr));serverAddr.sun_family=AF_UNIX;bcopy(argv[1],serverAddr.sun_path,strlen(argv[1]));if(connect(clientFd,(struct sockaddr*)&serverAddr,sizeof(serverAddr))==-1){perror("connect");exit(EXIT_FAILURE);}for(;;){int len=0;char buff[1024];bzero(buff,sizeof(buff));printf("Enter:");fflush(stdout);len=read(0,buff,sizeof(buff)-1);if(len>0){if(write(clientFd,buff,strlen(buff))==-1){perror("write");exit(EXIT_FAILURE);return;}}else{perror("read");exit(EXIT_FAILURE);return;}bzero(buff,sizeof(buff));if(read(clientFd,buff,sizeof(buff))==-1){perror("read");exit(EXIT_FAILURE);return;}printf("recv:%s\n",buff);}return 0;
}
APUE编程:134---进程间通信(命名域套接字:struct sockaddr_un)相关推荐
- 域服务器广播消息,广播,组播和UNIX域套接字
1.广播 1.特点 一对多 仅能使用UDP 2.概念 发送方只有一个接收方则称单播 如果同时发给局域网中的所有主机,成为广播 只有用户数据包(使用UDP协议)套接字才能广播 广播地址 1.以192.1 ...
- UNIX域套接字编程和socketpair 函数
一.UNIX Domain Socket IPC socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket.虽然网络soc ...
- unix 域套接字实现进程间通信
目录 1.认识域套接字 2.unix域套接字相关API及地址结构介绍 (1) 创建unix域套接字 (2) 填充地址结构 sockaddr_un 3.unix域套接字实现进程间通信( ...
- 【socket】 unix域套接字(socketpair )通信|socketpair和pipe的区别|进程间通信-Unix domain socket
目录 unix域套接字(socketpair )通信|socketpair和pipe的区别 socketpair机制 描述 原理 socketpair和pipe的区别 进程间通信-Unix domai ...
- 网络编程_5(超时检测+UNIX域套接字+抓包工具+包头分析)
一二章请点击:网络编程_1(网络基础+跨主机传输) 三四章请点击:网络编程_2(网络属性+UDP(UDP模型+广播组播)) 第五章请点击:网络编程_3(TCP) 第六章请点击:网络编程_4(IO模型) ...
- 学习Unix域套接字总结
开门见山,哲学三问!Unix域套接字是什么?为什么会存在Unix域套接字?如何用Unix域套接字? Unix域套接字是什么,为什么会有Unix用于套接字? Linux系统中不同进程进行通信的手段很多, ...
- linux 服务器间通信,Linux 下的进程间通信:套接字和信号 | Linux 中国
原标题:Linux 下的进程间通信:套接字和信号 | Linux 中国 学习在 Linux 中进程是如何与其他进程进行同步的. -- Marty Kalin 本篇是 Linux 下(IPC)系列的第三 ...
- Windows系统编程之进程间通信
Windows系统编程之进程间通信 标 题: Windows系统编程之进程间通信 作 者: 北极星2003 时 间: 2006-05-25,14:18:36 链 接: http://bbs.pediy ...
- 《Unix网络编程卷1:套接字联网API》读书笔记
第一部分:简介和TCP/IP 第1章:简介 第2章:传输层:TCP.UDP和SCTP TCP:传输控制协议,复杂.可靠.面向连接协议 UDP:用户数据报协议,简单.不可靠.无连接协议 SCTP:流控制 ...
最新文章
- php for循环in的用法,JavaScript中for in循环是如何使用的?需要注意些什么?
- 自定义类型: 结构体,枚举,联合
- 三十岁以前不必在乎的29件事
- Angular消息通知组件ngx-notification
- 音视频开发(3)---ffmpeg
- python---用python实现冒泡排序
- 分布式锁没那么难,手把手教你实现 Redis 分布锁!|保姆级教程
- Spring 事务模型
- SaaS-HRM(2)数据库设计与前端框架(企业管理前后台)
- abaqus2018+intel fortran2019+vs2015安装全记录
- 微信小程序快捷键和windows快捷键
- 2022年最新最全uniapp入门学习,零基础入门uniapp到实战项目,unicloud数据后台快速打造uniapp小程序项目
- 经典音频MUTE电路分析
- html5css3菜鸟教程,HTML5+CSS3实现拖放(Drag and Drop)示例
- 对宇宙起源的一个现代猜想-重生而非诞生
- 超材料常用的仿真软件CST COMSOL HFSS指导实际操作
- 2022年虚拟电厂行业研究报
- 中国线上超市行业营销态势与投资盈利预测报告(2022-2027)
- 100句正能量的句子经典语句
- 最简单的8421码计算方法