• 进程间通信 本地域套接字


     inux本地进程间通讯,大概有如下几种方式,socket本地域套接字是其中的一种。

    一、UNIX域套接字

    unix域套接字常用于本地进程间通信,相当于其他进程间通信方式使用更简洁方便,效率更高。
    虽然其他协议族比如AF_INET(IPV4互联网协议栈),也可以通过绑定本地回环地址进行进程间通信,
    但是由于要经过内核网络协议栈,打包拆包,计算校验和/维护信号和应答等操作,效率自然没有AF_UNIX(本地通信的UNIX协议栈)高。

    1. AF_INET域socket通信过程

    2. AF_UNIX域socket通信过程

     两者的不同点:

    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则只能用于本机内进程之间的通信。

    2. UNIX域套接字模型

     本地域套接字接口bind 传入的结构体是sockaddr_un

    #define UNIX_PATH_MAX    108  //大小为96~108
    
    struct sockaddr_un {
         sa_family_t sun_family;               /* AF_UNIX */
         char        sun_path[UNIX_PATH_MAX];  /* pathname */
     };

    unix下流式套接字tcp与数据报套接字udp的区别

    1)TCP面向流,数据分包、连包,UDP面向消息,不分包。
    
    2)TCP必须连接后才能收发数据,UDP直接发送。
    
    3)TCP不必记录客户端地址,直接收发数据,而UDP必须记录客户端地址后向具体地址发送数据。
    
    4)通信流程不同,tcp需要保证可靠连接需要connect,accept,listen,udp不需要
    
    5)收发数据接口不一样,tcp使用send,recv(),udp使用recvfrom(),sendto()

    注意:
    1)在sock()函数中

    int socket(int domain _, int type J int protocol); 
    // domain 套接字中使用的协议族( Protocol Famjly )信息。
    // type 套接字数据传输类型信息
    // protocol 计算机间通信中使用的协议信息

    协议族确定了通信规则,套接字类型确定了数据传输方式。,决定了协议族并不能同时决定数据传输方 式,换言之, socket 函数第1个参数PF INET协议族中也存在多种数据传输方式。

    2)使用sendto、recvfrom 时的缓冲区大小设置

    UDP仅提供数据报作为IP数据包的数据部分,IP数据包具有16位长度的字段,因此数据大小限制为2 ^ 16字节(65536),超过该大小时,需要分包发送。接收端接收时不能保证数据包的顺序与完整性。

    3)在bind前,通过unlink移除已存在的文件连接

    int unlink(const char *__name);   //Remove the link NAME.
    /*
    首先你要明确一个概念,一个文件是否存在取决于它的inode是否存在,你在目录里看到的是目录项里一条指向该inode的链接,
    而不是文件的本身.当你调用unlink的时候他直接把目录项里的该条链接删除了,但是inode并没有动,该文件还是存在的,
    这时候你会发现,目录里找不到该文件,但是已经打开这个文件的进程可以正常读写.只有当打开这个inode的所有文件描述符
    被关闭,指向该inode的链接数为0的情况下,这个文件的inode才会被真正的删除.
        从unlink的名字上就应该能判断出来,unlink含义为取消链接,remove才是删除的意思
    */

    3. 代码示例

    AF_UNIX,SOCK_DGRAM, 面向消息的数据报套接字

    #ifndef _COMM_H
    #define _COMM_H
    
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <sys/un.h>
    #include <unistd.h>
    
    #include <fstream>
    #include <iostream>
    #include <string>
    
    #include "spdlog/spdlog.h"
    
    #endif
    comm.h
    int main() {
        int fd = socket(AF_UNIX, SOCK_DGRAM, 0);
    
        char* path = "/tmp/test.data";
        struct sockaddr_un endPoint;
        endPoint.sun_family = AF_UNIX;
        strcpy(endPoint.sun_path, path);
    
        std::vector<char> buf(1, '0');
        while (true) {
            sleep(1);
            auto ret = sendto(fd, buf.data(), buf.size(), MSG_DONTWAIT,
                              (const sockaddr*)&endPoint, sizeof(endPoint));
            if (ret < 0) {
                spdlog::error("send error with {}", strerror(errno));
                continue;
            }
            spdlog::info("send size {}", ret);
            buf.push_back('0');
        }
        close(fd);
    
        return 0;
    }
    client.cc
    int main() {
        // AF_UNIX: 本地通信的UNIX协议族
        // SOCK_DGRAM: 面向消息的数据报套接字类型
        // 前两个参数就可确定协议类型时,默认为0
        int fd = socket(AF_UNIX, SOCK_DGRAM, 0);
        if (fd <= 0) {
            spdlog::error("get socket fd failed!");
            return fd;
        }
    
        char* path = "/tmp/test.data";
        struct sockaddr_un endPoint;
        endPoint.sun_family = AF_UNIX;
        strcpy(endPoint.sun_path, path);
    
        //删除该文件对应的inode链接,防止重复创建
        unlink(endPoint.sun_path);
    
        if (bind(fd, (const sockaddr*)&endPoint, sizeof(sockaddr_un)) < 0) {
            spdlog::error("bind error with {}!", strerror(errno));
            close(fd);
            return -1;
        }
    
        std::vector<char> buf(1024 * 100);
        socklen_t sockLen = sizeof(sockaddr_un);
        while (true) {
            sleep(1);
            auto ret = recvfrom(fd, buf.data(), buf.size(), MSG_DONTWAIT,
                                (sockaddr*)&endPoint, &sockLen);
            if (ret < 0) {
                printf("recvfrom error with %s\n", strerror(errno));
                continue;
            }
            spdlog::info("recvform size={}", ret);
        }
    
        return 0;
    }
    server.cc

    面向连接的流式套接字模型可见:示例

    4、参考资料

    嵌入式linux网络编程,UNIX域套接字,进程间通信机制
    AF_UNIX 本地通信
    关于PF_INET和AF_INET的区别
    细说linux IPC系列
    细说linux IPC(二):基于socket的进程间通信(下)

  • 相关阅读:
    小短文1-判别法浅谈
    高等代数半期考试
    让CNN跑起来,以下是调参的所有秘密
    杂谈
    自适应中值滤波器
    用色调,饱和度,亮度表示颜色
    用类处理彩色图像
    操作像素
    机器学习前言
    直方图均衡化
  • 原文地址:https://www.cnblogs.com/y4247464/p/16291216.html
Copyright © 2020-2023  润新知