前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >UNIX(进程间通信):17 深入理解unix域

UNIX(进程间通信):17 深入理解unix域

作者头像
用户3479834
发布2021-03-04 14:43:00
1.5K0
发布2021-03-04 14:43:00

unix域

unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通信的一种方法。

unix域是基于socket通信的一个特例,因为他的实现中使用了socket技术,但是他是基于单个主机上的进程间通信。因为在同一个主机内,所以就少了很多网络上的问题,那就减少了复杂度。unix域和传统的socket通信类型,服务器监听,客户端连接,由于在同主机,就不必要使用ip和端口的方式,浪费一个端口。unix域采用的是一个文件作为标记。大致原理如下。 1 服务器首先拿到一个socket结构体,和一个unix域相关的unix_proto_data结构体。 2 服务器bind一个文件。对于操作系统来说,就是新建一个文件,然后把文件路径信息存在unix_proto_data中。 3 listen 4 客户端通过同样的文件路径调用connect去连接服务器。这时候客户端的结构体插入服务器的连接队列,等待处理。 5 服务器调用accept摘取队列的节点,然后新建一个通信socket进行通信。 unix域通信本质还是基于内存之间的通信,客户端和服务器都维护一块内存,然后实现全双工通信,而unix域的文件路径,只不过是为了让客户端进程可以找到服务端进程。而通过connect和accept让客户端和服务器对应的结构体关联起来,后续就可以互相往对方维护的内存里写东西了。就可以实现进程间通信。

使用unix域套接字的三个好处:

  • 1.unix域套接字通常比通信两端位于同一个主机的TCP套接字快出一倍
  • 2.unix域套接字可用于在同一个主机上的不同进程之间传递描述符
  • 3.unix能够提供额外的安全检查措施,较新的实现把客户的凭证(用户ID和组ID)提供给服务器

unix域套接字中用于标识客户和服务器的协议地址是普通文件系统中的路径名。

socketpair函数

socketpair函数创建两个随后连接起来的套接字,本函数仅适用于unix域套接字。

#include <sys/socket.h>
int socketpair(int family, int type, int protocol, int sockfd[2]);/* 成功返回非0,失败返回-1 */

其中,family为AF_LOCAL, protocol为0,type可以为SOCK_STREAM或者SOCK_DGRAM。新创建的两个套接字描述符作为sockfd[0],sockfd[1]返回。

该函数与unix的pipe函数类似,返回两个彼此连接的描述符

使用字节流模式实现客户/服务器回射程序

服务器端程序:

#include <stdio.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>

#define MAXLINE 4096
#define LISTENQ 1024
#define UNIX_STREAM_PATH "/tmp/unix_stream_path"

void str_echo(int sockfd)
{
    ssize_t n;
    char buf[MAXLINE];

again:
    while ((n = read(sockfd, buf, MAXLINE)) > 0) {
        write(sockfd, buf, n);
    }

    if (n < 0 && errno == EINTR) {
        goto again;
    } else if (n < 0) {
        printf("str_echo read error: %s\n", strerror(errno));
        exit(-1);
    }
}
int main(int argc, char **argv)
{
    int ret;
    int listenfd, connfd;
    pid_t childpid;
    socklen_t clilen;
    struct sockaddr_un cliaddr, servaddr;

    listenfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (listenfd < 0) {
        printf("socket error: %s\n", strerror(errno));
        return -1;
    }

    unlink(UNIX_STREAM_PATH);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sun_family = AF_LOCAL;
    strncpy(servaddr.sun_path, UNIX_STREAM_PATH, sizeof(servaddr.sun_path) - 1);

    ret = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    if (ret) {
        printf("bind error: %s\n", strerror(errno));
        return -1;
    }

    ret = listen(listenfd, LISTENQ);
    if (ret) {
        printf("listen error: %s\n", strerror(errno));
        return -1;
    }

    while (1) {
        clilen = sizeof(cliaddr);
        if ((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) < 0) {
            if (errno == EINTR) {
                continue;
            } else {
                printf("accept error: %s\n", strerror(errno));
                return -1;
            }
        }

        if ((childpid = fork()) == 0) {
            /* child close listening socket and process connected socket */
            close(listenfd);
            str_echo(connfd);
            exit(0);
        }
        /* parent close connected socket */
        close(connfd);
    }
}

客户端:

#include <stdio.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/select.h>

#define MAXLINE 4096
#define UNIX_STREAM_PATH "/tmp/unix_stream_path"

int max(int a, int b)
{
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

void str_cli(FILE *fp, int sockfd)
{
    int maxfdp1, stdineof;
    fd_set rset;
    char buf[MAXLINE];
    int n;

    stdineof = 0;
    FD_ZERO(&rset);

    while (1) {
        if (stdineof == 0) {
            FD_SET(fileno(fp), &rset);
        }
        FD_SET(sockfd, &rset);
        maxfdp1 = max(fileno(fp), sockfd) + 1;

        select(maxfdp1, &rset, NULL, NULL, NULL);

        if (FD_ISSET(sockfd, &rset)) {
            if ((n = read(sockfd, buf, MAXLINE)) == 0) {
                if (stdineof == 1) {
                    return;
                } else {
                    printf("str_cli: server terminated prenatruely\n");
                    exit(-1);
                }
            }
            write(fileno(stdout), buf, n);
        }

        if (FD_ISSET(fileno(fp), &rset)) {
            if ((n = read(fileno(fp), buf, MAXLINE)) == 0) {
                stdineof = 1;
                shutdown(sockfd, SHUT_WR);
                FD_CLR(fileno(fp), &rset);
                continue;
            }
            write(sockfd, buf, n);
        }
    }
}

int main(int argc, char **argv)
{
    int ret, sockfd;
    struct sockaddr_un servaddr;

    sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sun_family = AF_LOCAL;
    strncpy(servaddr.sun_path, UNIX_STREAM_PATH, sizeof(servaddr.sun_path));
    connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    str_cli(stdin, sockfd);

    return 0;
}
 
 
使用数据报模式实现客户/服务器回射程序

服务器端:

#include <stdio.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>

#define MAXLINE 4096
#define UNIX_DGRAM_PATH "/tmp/unix_dgram_path"

void dg_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen)
{
    int n;
    socklen_t len;
    char mesg[MAXLINE];

    while(1) {
        len = clilen;
        n = recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);

        sendto(sockfd, mesg, n, 0, pcliaddr, len);
    }
}

int main(int argc, char **argv)
{ 
    int ret, sockfd;
    struct sockaddr_un cliaddr, servaddr;

    sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        printf("socket error: %s\n", strerror(errno));
        return -1;
    }

    unlink(UNIX_DGRAM_PATH);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sun_family = AF_LOCAL;
    strncpy(servaddr.sun_path, UNIX_DGRAM_PATH, sizeof(servaddr.sun_path) - 1);

    ret = bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    if (ret) {
        printf("bind error: %s\n", strerror(errno));
        return -1;
    }

    dg_echo(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));

} 

客户端:

#include <stdio.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>

#define MAXLINE 4096
#define UNIX_DGRAM_PATH "/tmp/unix_dgram_path"

void dg_cli(FILE *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen)
{
    int n;
    char sendline[MAXLINE], recvline[MAXLINE + 1];

    while (fgets(sendline, MAXLINE, fp) != NULL) {
        sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);

        n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);

        recvline[n] = 0;
        fputs(recvline, stdout);
    }
}

int main(int argc, char **argv)
{ 
    int ret, sockfd;
    struct sockaddr_un cliaddr, servaddr;

    sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        printf("socket error: %s\n", strerror(errno));
        return -1;
    }

    bzero(&cliaddr, sizeof(cliaddr));
    cliaddr.sun_family = AF_LOCAL;
    strncpy(cliaddr.sun_path, tmpnam(NULL), sizeof(cliaddr.sun_path) - 1);

    ret = bind(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
    if (ret) {
        printf("bind error: %s\n", strerror(errno));
        return -1;
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sun_family = AF_LOCAL;
    strncpy(servaddr.sun_path, UNIX_DGRAM_PATH, sizeof(servaddr.sun_path) - 1);

    dg_cli(stdin, sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    return 0;
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-02-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 游戏开发司机 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • unix域
    • socketpair函数
      • 使用字节流模式实现客户/服务器回射程序
        • 使用数据报模式实现客户/服务器回射程序
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档