AF_UNIX 本地通信编程示例

AF_UNIX 本地通信编程示例

1. Linux进程通信机制

linux本地进程间通讯,大概有如下几种方式,socket本地域套接字是其中的一种。
在这里插入图片描述

基于socket的框架上发展出一种IPC机制,就是UNIX Domain Socket。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC 更有效率 :

  • 不需要经过网络协议栈
  • 不需要打包拆包、计算校验和、维护序号和应答等,可靠性更强

只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。UNIX Domain Socket也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。

1. AF_INET域socket通信过程

在这里插入图片描述
典型的TCP/IP四层模型的通信过程。

发送方、接收方依赖IP:Port来标识,即将本地的socket绑定到对应的IP端口上,发送数据时,指定对方的IP端口,经过Internet,可以根据此IP端口最终找到接收方;接收数据时,可以从数据包中获取到发送方的IP端口。

发送方通过系统调用send()将原始数据发送到操作系统内核缓冲区中。内核缓冲区从上到下依次经过TCP层、IP层、链路层的编码,分别添加对应的头部信息,经过网卡将一个数据包发送到网络中。经过网络路由到接收方的网卡。网卡通过系统中断将数据包通知到接收方的操作系统,再沿着发送方编码的反方向进行解码,即依次经过链路层、IP层、TCP层去除头部、检查校验等,最终将原始数据上报到接收方进程。

2. AF_UNIX域socket通信过程

典型的本地IPC,类似于管道,依赖路径名标识发送方和接收方。即发送数据时,指定接收方绑定的路径名,操作系统根据该路径名可以直接找到对应的接收方,并将原始数据直接拷贝到接收方的内核缓冲区中,并上报给接收方进程进行处理。同样的接收方可以从收到的数据包中获取到发送方的路径名,并通过此路径名向其发送数据。
在这里插入图片描述

3. 相同点

操作系统提供的接口socket(),bind(),connect(),accept(),send(),recv(),以及用来对其进行多路复用事件检测的select(),poll(),epoll()都是完全相同的。收发数据的过程中,上层应用感知不到底层的差别。

4. 不同点

1 建立socket传递的地址域,及bind()的地址结构稍有区别:

socket() 分别传递不同的域AF_INET和AF_UNIX

bind()的地址结构分别为sockaddr_in(制定IP端口)和sockaddr_un(指定路径名)

2 AF_INET需经过多个协议层的编解码,消耗系统cpu,并且数据传输需要经过网卡,受到网卡带宽的限制。AF_UNIX数据到达内核缓冲区后,由内核根据指定路径名找到接收方socket对应的内核缓冲区,直接将数据拷贝过去,不经过协议层编解码,节省系统cpu,并且不经过网卡,因此不受网卡带宽的限制。

3 AF_UNIX的传输速率远远大于AF_INET

4 AF_INET不仅可以用作本机的跨进程通信,同样的可以用于不同机器之间的通信,其就是为了在不同机器之间进行网络互联传递数据而生。而AF_UNIX则只能用于本机内进程之间的通信。

5. 使用场景

AF_UNIX由于其对系统cpu的较少消耗,不受限于网卡带宽,及高效的传递速率,本机通信则首选AF_UNIX域。

不用多说,AF_INET则用于跨机器之间的通信。

上文参考:

https://blog.csdn.net/mayue_web/article/details/92798529

6.AF_UNIX编程示例(C语言)

客户端

#include <stdio.h>
#include <sys/un.h>      //sockaddr_un
#include <sys/socket.h>  //AF_UNIX、SOCK_STREAM

typedef struct
{
    char recvBuff[100];
    char sendBuff[100];
}CLIENT_PARAM;

int main(int argc, char *argv[])
{
    int serverFd,clientFd,addrLen,nwrite,nread;
    struct sockaddr_un serverAddr,clientAddr;
    CLIENT_PARAM param;

    if (argc < 2)
    {
        printf("please input param !\n");
        return 0;
    }

    unlink("./client.socket");   /* in case it already exists */

    //填充属性
    memset(&clientAddr,0,sizeof(clientAddr));
    memset(&serverAddr,0,sizeof(serverAddr));
    clientAddr.sun_family = AF_UNIX;
    sprintf(clientAddr.sun_path,"%s","./client.socket");

    //创建套接字
    if ((clientFd = socket(AF_UNIX,SOCK_STREAM,0)) < 0)
    {
        printf("err -1\n");
        return -1;
    }

    if (bind(clientFd, (struct sockaddr *)&clientAddr, sizeof(clientAddr)) < 0)
    {
        printf("err -2\n");
        close(clientFd);
        return -2;
    }

    //填充服务端信息
    serverAddr.sun_family = AF_UNIX;
    sprintf(serverAddr.sun_path, "./server.socket");
    if (connect(clientFd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)
    {
        printf("err -3\n");
        close(clientFd);
        return -3;
    }

    memset(&param,0,sizeof(param));
    sprintf(param.sendBuff,"%s",argv[1]);
    if ((nwrite = write(clientFd, param.sendBuff, 100)) < 0)
    {
        printf("err -4\n");
        close(clientFd);
        return -4;
    }

    while ((nread = recv(clientFd, param.recvBuff, 100, 0)) > 0)
    {
        printf("%s\n", param.recvBuff);
        memset(param.recvBuff, 0, sizeof(param.recvBuff));
    }

    close(clientFd);
    return 0;
}

服务器

#include <stdio.h>
#include <sys/un.h>      //sockaddr_un
#include <sys/socket.h>  //AF_UNIX、SOCK_STREAM

typedef struct
{
    char recvBuff[100];
    char sendBuff[100];
}SERVER_PARAM;

int main()
{
    int serverFd,clientFd,addrLen;
    struct sockaddr_un serverAddr,clientAddr;
    SERVER_PARAM param;

    //填充属性
    memset(&serverAddr,0,sizeof(serverAddr));
    serverAddr.sun_family = AF_UNIX;
    sprintf(serverAddr.sun_path,"%s","./server.socket");

    unlink("./server.socket");   /* in case it already exists */

    //创建套接字
    if ((serverFd = socket(AF_UNIX,SOCK_STREAM,0)) < 0)
    {
        printf("err -1\n");
        return -1;
    }

    //将套接字绑定到属性对应的本地文件
    if (bind(serverFd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) < 0)
    {
        printf("err -2\n");
        close(serverFd);
        return -2;
    }

    //监听
    if(listen(serverFd,10) < 0)  //最多监听10个
    {
        printf("err -3\n");
        return -3;
    }

    while(1)
    {
        addrLen = sizeof(clientAddr);
        memset(&clientAddr,0,sizeof(clientAddr));
        memset(&param,0,sizeof(param));

        //阻塞,收到请求后返回用于本次通信的fd
        if((clientFd = accept(serverFd,(struct sockaddr*)&clientAddr,&addrLen)) < 0)
        {
            printf("err -4\n");
            continue;
        }

        //接收数据
        if(recv(clientFd,param.recvBuff,100,0) < 0)
        {
            printf("err -5");
            close(clientFd);
            continue;
        }
        printf("======== server : get cmd %s ========\n",param.recvBuff);

        //发送数据
        sprintf(param.sendBuff,"I have got your cmd %s !",param.recvBuff);
        send(clientFd,param.sendBuff,strlen(param.sendBuff),0);

        close(clientFd);
    }
    close(serverFd);

    return 0;
}

执行结果

编译客户端得到可执行文件client

编译服务器得到可执行文件server

分别运行服务器和客户端:

./server
./client a

在这里插入图片描述
在这里插入图片描述

执行之后的目录情况:

自动生成了client.socket和server.socket
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值