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

 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的进程间通信(下)

 

posted @ 2022-05-20 14:35  朱果果  阅读(1146)  评论(0编辑  收藏  举报