• 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]={''};
    if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0){
    printf("socketpair error! ");
    return -1;
    }

    if((pid = fork())<0){
    printf("fork error! ");
    return -1;
    }else if(pid == 0){//child
    close(fd[0]);
    if((n=write(fd[1],wbuf,strlen(wbuf))) < 0){
    printf("child write error! ");
    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! ");
    exit(-1);
    }
    rbuf[n]=0;
    printf("son read data from father: %s ",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! ");
    exit(-1);
    }
    rbuf[n]=0;
    printf("parent read data from son :%s ",rbuf);
    if(write(fd[0],wbuf,strlen(wbuf)) < 0){
    printf("parent write error! ");
    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]={''};
    if(pipe(fd) < 0){
    //if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0){
    printf("socketpair error! ");
    return -1;
    }

    if((pid = fork())<0){
    printf("fork error! ");
    return -1;
    }else if(pid == 0){//child
    close(fd[0]);
    if((n=write(fd[1],wbuf,strlen(wbuf))) < 0){
    printf("child write error! ");
    exit(-1);
    }
    sleep(1);//子进程等待1秒
    cout<<rbuf<<endl;
    if((n=read(fd[1],rbuf,16))<0){//子进程从fd[1]读取数据
    printf("child read error! ");
    exit(-1);
    }
    rbuf[n]=0;
    printf("child read data from father: %s ",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! ");
    exit(-1);
    }
    rbuf[n]=0;
    printf("parent read data from son :%s ",rbuf);
    if(write(fd[0],wbuf,strlen(wbuf)) < 0){
    printf("parent write error! ");
    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 ");
    if (write(tip->fd, m.mtext, n) < 0)printf("write error ");
    }
    }

    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 ");
    cout<<"the msgque id of server is "<<qid[i]<<endl;
    printf("queue ID %d is %d ", i, qid[i]);
    if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fd) < 0)printf("socketpair error ");
    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 ");
    }
    for (;;) {//reading data
    if (poll(pfd, NQ, -1) < 0)printf("poll error ");
    for (i = 0; i < NQ; i++) {
    if (pfd[i].revents & POLLIN) {
    if ((n = read(pfd[i].fd, buf, sizeof(buf))) < 0)printf("read error ");
    buf[n] = 0;
    printf("queue id %d, message %s ", 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 ");
    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 ");
    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 ");
    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 ");
    printf("UNIX domain socket bound ");
    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

  • 相关阅读:
    扩散模型+文本生成
    期末总结
    查看笔记本电池损耗
    Qt数据可视化(散点图、折线图、柱状图、盒须图、饼状图、雷达图)开发实例
    24点游戏题库算法分析
    Qt项目开发实例 (含源码)
    Knowledge base for Interview
    System integration
    powerdesigner的使用
    VC++60MFC框架学习增加拖拽
  • 原文地址:https://www.cnblogs.com/wangbin/p/11436341.html
Copyright © 2020-2023  润新知