unix进程间通信方式(下)-unix域套接字(转)
在之前的博客中已经总结了其它7种进程间的通信方式。unix域套接字用于在同一台计算机上的进程间通信,虽然因特网域套接字可用于同一目的,但是unix域套接字的效率更高。unix域套接字并不进行协议处理,不需要添加或删除网络报头,无需计算校验和,不需要产生顺序号,无需发送确认报文。UNIX与套接字提供和数据报两种接口,UNIX域数据报服务是可靠的,就不会丢失消息也不会传递出错。UNIX域套接字是套接字和管道之间的混合物。为了创建一对非命名的,相互连接的UNXI域套接字,用户可以使用socketopair函数。创建一对互相连接的unix域套接字可以用socketpair函数:
1.unix未命名套接字
#include<sys/socket.h>
int socketpari(int domain, int type, int protocol, int sockfd[2]); //若成功则返回0,出错则返回-1.
《在unix环境高级编程》一书中是这样描述的:
下面我们就用一个简单的程序来验证上面的管道模型是否正确!!!
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <iostream>
#include <unistd.h>
using namespace std;
int main(void){
int fd[2];
int pid;
int n=0;
char wbuf[16] = "1234567890";
char rbuf[16]={'\0'};
if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0){
printf("socketpair error!\n");
return -1;
}
if((pid = fork())<0){
printf("fork error!\n");
return -1;
}else if(pid == 0){//child
close(fd[0]);
if((n=write(fd[1],wbuf,strlen(wbuf))) < 0){
printf("child write error!\n");
exit(-1);
}
sleep(1);//子进程等待1秒
cout<<"the data of rbuf of child before commubication is :"<<rbuf<<endl;
if((n=read(fd[1],rbuf,16))<0){//子进程从fd[1]读取数据
printf("child read error!\n");
exit(-1);
}
rbuf[n]=0;
printf("son read data from father: %s\n",rbuf);
exit(0);
}else{//parent
sleep(1);//父进程等待1秒钟
close(fd[1]);
cout<<"the data of rbuf of parent before commubication is :"<<rbuf<<endl;
if((n=read(fd[0],rbuf,16)) < 0){
printf("parent read error!\n");
exit(-1);
}
rbuf[n]=0;
printf("parent read data from son :%s\n",rbuf);
if(write(fd[0],wbuf,strlen(wbuf)) < 0){
printf("parent write error!\n");
exit(-1);
}
exit(0);
}
return 0;
}
测试结果:
由图可以看出父进程和子进程构成的通道正如图17-1所示,也就是说父进程或子进程都可以向它们的唯一端口fd[0]或fd[1]同时读写数据,而这一切都是因为unix域套接字的声明。接下来我们来验证声明一个管道,看能不能在同一段进行读写:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <iostream>
#include <unistd.h>
using namespace std;
int main(void){
int fd[2];
int pid;
int n=0;
char wbuf[16] = "1234567890";
char rbuf[16]={'\0'};
if(pipe(fd) < 0){
//if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0){
printf("socketpair error!\n");
return -1;
}
if((pid = fork())<0){
printf("fork error!\n");
return -1;
}else if(pid == 0){//child
close(fd[0]);
if((n=write(fd[1],wbuf,strlen(wbuf))) < 0){
printf("child write error!\n");
exit(-1);
}
sleep(1);//子进程等待1秒
cout<<rbuf<<endl;
if((n=read(fd[1],rbuf,16))<0){//子进程从fd[1]读取数据
printf("child read error!\n");
exit(-1);
}
rbuf[n]=0;
printf("child read data from father: %s\n",rbuf);
exit(0);
}else{//parent
sleep(1);//父进程等待1秒钟
close(fd[1]);
cout<<rbuf<<endl;
if((n=read(fd[0],rbuf,16)) < 0){
printf("parent read error!\n");
exit(-1);
}
rbuf[n]=0;
printf("parent read data from son :%s\n",rbuf);
if(write(fd[0],wbuf,strlen(wbuf)) < 0){
printf("parent write error!\n");
exit(-1);
}
exit(0);
}
return 0;
}
测试结果:
从测试结果中可以看出,对于管道来说,我们不能在管道的同一端进行读写,这样只能报错!!!!!
unix域套接字的经典应用:
我们知道消息队列存在一个问题,即不能将它们和poll或者select一起使用,这是因为它们不能关联到文件描述符,然而,套接字和文件描述符是相关联的消息到达时,可以用套接字来通知,即消息到达时我们将起写入到套接字的一端,那么我们可以用poll来轮询unix域套接字的另一端即可判断消息有没有到来,这个时候相当于多了一层中介。下面就来看看这个非常巧妙的设计:
server.c
<span style="color:#000000;">#include "apue.h"
#include <poll.h>
#include <pthread.h>
#include <sys/msg.h>
#include <sys/socket.h>
#include<iostream>
using namespace std;
#define NQ 3 /* number of queues */
#define MAXMSZ 512 /* maximum lenth */
#define KEY 0x123 /* key for first message queue */
struct threadinfo {
int qid;// the quene id
int fd;// the fd used to write
};
struct mymesg {
long mtype;
char mtext[MAXMSZ];
};
void * helper(void *arg){
int n;
struct mymesg m;
struct threadinfo *tip = (struct threadinfo *)arg;
for(;;) {
memset(&m, 0, sizeof(m));
if ((n = msgrcv(tip->qid, &m, MAXMSZ, 0, MSG_NOERROR)) < 0)printf("msgrcv error\n");
if (write(tip->fd, m.mtext, n) < 0)printf("write error\n");
}
}
int main(){
int i,n,err;
int fd[2];
int qid[NQ];
struct pollfd pfd[NQ];
struct threadinfo ti[NQ];
pthread_t tid[NQ];
char buf[MAXMSZ];
for (i = 0; i < NQ; i++) {
if ((qid[i] = msgget((KEY+i), IPC_CREAT|0666)) < 0)printf("msgget error\n");
cout<<"the msgque id of server is "<<qid[i]<<endl;
printf("queue ID %d is %d\n", i, qid[i]);
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fd) < 0)printf("socketpair error\n");
cout<<"fd[0] is: "<<fd[0]<<"fd[1] is: "<<fd[1]<<endl;
pfd[i].fd = fd[0];
pfd[i].events = POLLIN;
ti[i].qid = qid[i];
ti[i].fd = fd[1];
if ((err = pthread_create(&tid[i], NULL, helper, &ti[i])) != 0)printf("pthread_create error\n");
}
for (;;) {//reading data
if (poll(pfd, NQ, -1) < 0)printf("poll error\n");
for (i = 0; i < NQ; i++) {
if (pfd[i].revents & POLLIN) {
if ((n = read(pfd[i].fd, buf, sizeof(buf))) < 0)printf("read error\n");
buf[n] = 0;
printf("queue id %d, message %s\n", qid[i], buf);
}
}
}
exit(0);
}
</span>
client.c
<span style="color:#000000;">#include "apue.h"
#include <sys/msg.h>
#include<iostream>
using namespace std;
#define MAXMSZ 512
struct mymesg {
long mtype;
char mtext[MAXMSZ];
};
int main(int argc, char *argv[]){
key_t key;
long qid;
size_t nbytes;
struct mymesg m;
if (argc != 3) {
fprintf(stderr, "usage: sendmsg KEY message\n");
exit(1);
}
key = strtol(argv[1], NULL, 0);
if ((qid = msgget(key, 0)) < 0)printf("can't open queue key %s", argv[1]);
cout<<"the id of the queue is"<<qid<<endl;
memset(&m, 0, sizeof(m));
strncpy(m.mtext, argv[2], MAXMSZ-1);
nbytes = strlen(m.mtext);
m.mtype = 1;
if (msgsnd(qid, &m, nbytes, 0) < 0)printf("can't send message\n");
exit(0);
}</span>
测试结果:
从图中我们可以看到,1.服务器段利用poll来间接轮询来自客户端的消息;2.对于msgget,如果使用相同的key(比如ox123)那么得到的消息队列的id是一样的!!!!
2.unix命名域套接字
虽然socketpair函数能创建一对相互连接的套接字,但是每一个套接字却没有名字,无关的进程不能使用它们。如果我们绑定一个地址,这个地址就是一个路径,那么无关的进程就可以使用它们,下面是地址的格式:
<span style="color:#000000;">struct sockaddr_un {
sa_family_t sun_family; /*PF_UNIX或AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* 路径名 */
};</span>
下面我们来看以个简单的例子:
#include "apue.h"
#include <sys/socket.h>
#include <sys/un.h>
int main(){
int fd, size;
struct sockaddr_un un;
un.sun_family = AF_UNIX;
strcpy(un.sun_path, "/home/caoyan/unix/unix-domain-socket/socket");
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)printf("socket failed\n");
size = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);//the total lenght of address
if (bind(fd, (struct sockaddr *)&un, size) < 0)
printf("bind failed\n");
printf("UNIX domain socket bound\n");
exit(0);
}
其中offsetof是个宏:
#define offsetof(TYPE,MEMBER) ((int)&(TYPE *)0->MEMBER)
测试结果:
从下面的结果我们可以看出,我们的文件夹里多了一个管道文件socket,这个文件的大小为0。
————————————————
版权声明:本文为CSDN博主「caoyan_12727」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/caoyan_12727/article/details/52180075
unix进程间通信方式(下)-unix域套接字(转)的更多相关文章
- unix进程间通信方式(IPC)
unix进程间通信方式(IPC) 管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信. 命名管道(named pipe):命名管道克服了管道没有 ...
- 高级进程间通信之UNIX域套接字
UNIX域套接字用于在同一台机器上运行的进程之间的通信.虽然因特网域套接字可用于同一目的,但UNIX域套接字的效率更高.UNIX域套接字仅仅复制数据:它们并不执行协议处理,不需要添加或删除网络报头,无 ...
- Unix域套接字简介
在Linux系统中,有很多进程间通信方式,套接字(Socket)就是其中的一种.但传统的套接字的用法都是基于TCP/IP协议栈的,需要指定IP地址.如果不同主机上的两个进程进行通信,当然这样做没什么问 ...
- Unix域套接字(Unix Domain Socket)介绍【转】
本文转载自:http://blog.csdn.net/roland_sun/article/details/50266565 版权声明:本文为博主原创文章,未经博主允许不得转载. 在Linux系统中, ...
- 通过UNIX域套接字传递描述符的应用
传送文件描述符是高并发网络服务编程的一种常见实现方式.Nebula 高性能通用网络框架即采用了UNIX域套接字传递文件描述符设计和实现.本文详细说明一下传送文件描述符的应用. 1. TCP服务器程 ...
- UNIX域套接字(unix domain)
UNIX域套接字用于在同一台机器上运行的进程之间的通信. UNIX域套接字提供流和数据报两种接口. 说明:UNIX域套接字比因特网套接字效率更高.它仅赋值数据:不进行协议处理,如添加或删除网络报头.计 ...
- 《网络编程》Unix 域套接字
概述 Unix 域套接字是一种client和server在单主机上的 IPC 方法.Unix 域套接字不运行协议处理,不须要加入或删除网络报头,无需验证和,不产生顺序号,无需发送确认报文,比因特网域套 ...
- UNIX网络编程——通过UNIX域套接字传递描述符和 sendmsg/recvmsg 函数
在前面我们介绍了UNIX域套接字编程,更重要的一点是UNIX域套接字可以在同一台主机上各进程之间传递文件描述符. 下面先来看两个函数: #include <sys/types.h> #in ...
- UNIX网络编程——UNIX域套接字编程和socketpair 函数
一.UNIX Domain Socket IPC socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket.虽然网络soc ...
随机推荐
- 《设计模式之美》 <02>评判代码质量好坏的维度
如何评价代码质量的高低? 实际上,咱们平时嘴中常说的“好”和“烂”,是对代码质量的一种描述.“好”笼统地表示代码质量高,“烂”笼统地表示代码质量低.对于代码质量的描述,除了“好”“烂”这样比较简单粗暴 ...
- Django上手体验,对比Asp.Net Core框架
一.前言 最近经常听说“人生苦短,我选python”这句话,处于好奇,笔者对python相关技术和web框架做了一番研究,本篇就对python web框架代表作Django和微软主打web框架Asp. ...
- PAT Basic 1083 是否存在相等的差 (20 分)
给定 N 张卡片,正面分别写上 1.2.…….N,然后全部翻面,洗牌,在背面分别写上 1.2.…….N.将每张牌的正反两面数字相减(大减小),得到 N 个非负差值,其中是否存在相等的差? 输入格式: ...
- 01 数据库sql
1, 关于mysql,常去的地方有:https://www.yiibai.com/mysql, http://tool.oschina.net/apidocs/apidoc?api=mysql-5.1 ...
- 【Java 基础项目 - - Bank项目4】 对象构造/跨package调用
UML设计: 文件组织: (注: 在bank4中,直接调用bank3的内容, 不再重复编写代码即可!) 代码编写Bank.java: package Banking_4; import Banking ...
- vue store获取值时 Computed property "activeTag" was assigned to but it has no setter.
出现原因: element-ui中 el-tab绑定的值在切换tab时会自动修改 而activeTag是从store中获取的值,不能直接修改 要添加给它绑定上set <el-tabs cla ...
- C# 之 .net core -- 创建项目
一.新建一个Web 的 应用程序 二.选择项目的基本信息(.net coer 2.2 和带有试图控制器的程序) 这个是类似以MVC的模式,也可以用其他的,总之需要什么选什么 三. 然后既可以看到这样一 ...
- mongodb命令---创建数据库,插入文档,更新文档记录
创建数据库----基本就是使用隐式创建 例如 use 你定义的数据库名, use dingsmongo 如果你使用的是studio 3T软件,那直接选中右侧的地址栏点击右键选择Add Databas ...
- selenium+pyquery爬取淘宝商品信息
import re from selenium import webdriver from selenium.common.exceptions import TimeoutException fro ...
- springboot中使用spring security,登录url就出现403错误
参考链接:https://segmentfault.com/q/1010000012743613 有两个controller,一个是所有用户可以访问的@RequestMapping("use ...