UNIX 域协议

1. UNIX域协议

利用socket 编程接口实现本地进程间通信
UNIX域协议套接字:可以使用TCP,也可以使用UDP

SOCK_STREAM -----> TCP 面向字节流

SOCK_DGRAM -----> UDP 面向数据报

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

UNIX域数据报 (SOCK_DGRAM)是可靠的,不会丢失消息
IP协议中使用 IP地址 + 端口号标识客户端和服务端

UNIX域协议使用普通文件系统中的路径名 (绝对路径) 标识客户端和服务端

cpp 复制代码
如果这个路径不存在则创建后标识其为 客户/服务器 的地址
如果这个路径已经存在了则会报错:bind failed: Address already in use
所以在运行前先删掉,再去创建并且绑定

unlink(绝对路径名);

UNIX域协议的特点:

和TCP比较,速度快,数据报不要传递到主机外,也不需要进行封包和拆包

UNIX域协议使用一个绝对路径作为"IP"地址

#include <sys/un.h>

cpp 复制代码
//vim /usr/include/linux/un.h
#define UNIX_PATH_MAX 108

// Unix域协议的地址结构体的具体描述
// #include <sys/un.h>
struct sockaddr_un {
    __kernel_sa_family_t sun_family; // 协议族
    char sun_path[UNIX_PATH_MAX];
    /* 
        Unix域协议地址,是以'\0'结束的本地文件系统中的绝对路径名,
        如: "/tmp/xxx.socket"
    */
}

编程方法:TCP / UDP
问题:客户端成功发送数据出去,服务端成功接收到数据,但是并没有成功将客户端的地址打印出来?

客户端没有绑定地址。如果说服务端要向客户端发送数据的话,没有"IP"地址就没办法定位对方的具体位置了,所以要实现能获取客户端地址,则客户端需要绑定地址。

使用UDP实现UNIX域协议如果客户端不绑定,服务器就不能发送信息给客户端

2. 代码实现

2.1 基于TCP实现UNIX域协议

unix_tcp_client.c

cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>

#define SERVICEPATHNAME "/home/china/s"
#define CLIENTPATHNAME "/home/china/c"

int main(int argc, char *argv[]) {

    unlink(CLIENTPATHNAME);

    // (1) socket:创建一个套接字
    int sockfd =  socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("unix socket failed");
        return -1;
    }

    // (2) bind
    // 需要一个网络地址结构体
    struct sockaddr_un cAddr;
    memset(&cAddr, 0, sizeof(cAddr));
    cAddr.sun_family = AF_UNIX; // 协议族
    strcpy(cAddr.sun_path, SLIENTPATHNAME);

    int res = bind(sockfd, (struct sockaddr *)&cAddr, sizeof(cAddr));
    if (res == -1) {
        perror("unix bind failed");
        close(sockfd);
        return -1;
    }

    printf("bind success\n");

    // (3) connect
    struct sockaddr_un sAddr;
    memset(&sAddr, 0, sizeof(sAddr));
    sAddr.sun_family = AF_UNIX; // 协议族
    strcpy(sAddr.sun_path, SERVICEPATHNMAE);

    res = connect(sockfd, (struct sockaddr *)&sAddr, sizeof(sAddr));
    if (res == -1) {
        perror("unix connect failed");
        return -1;
    }

    printf("unix connect succeed\n");

    while (1) {

        // (3) 进行通信,读写数据
        char buf[250] = {0};
        scanf("%s", buf);
        int w = send(sockfd, buf, strlen(buf), 0);
        printf("w = %d\n", w);
    
        if (buf[0] == '#') {
            break;
        }

        char buff[250] = {0};
        int r = recv(sockfd, buff, 250, 0);

        printf("r = %d,message = %s\n", r, buff);
    }

    // (4) 关闭socket套接字
    close(sockfd);

    return 0;
} 

unix_tcp_server.c

cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>

#define SERVICEPATHNAME "/home/china/s"

int main(int argc, char *argv[]) {

    unlink(SERVICEPATHNMAE);

    // (1) socket:创建一个套接字
    int sockfd =  socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("unix socket failed");
        return -1;
    }

    // (2) bind:把一个套接字和网络地址绑定到一起
    // 需要一个网络地址结构体
    struct sockaddr_un sAddr;
    memset(&sAddr, 0, sizeof(sAddr));
    sAddr.sun_family = AF_UNIX; // 协议族
    strcpy(sAddr.sun_path, SERVICEPATHNAME);

    int res = bind(sockfd, (struct sockaddr *)&sAddr, sizeof(sAddr));
    if (res == -1) {
        perror("unix bind failed");
        close(sockfd);
        return -1;
    }

    printf("bind success\n");

    // (3) listen:让套接字进入"监听模式"
    res = listen(sockfd, 5);
    if (res == -1) {
        perror("unix listen failed");
        close(sockfd);
        return -1;
    }

    // 保存客户端的网络地址
    struct sockaddr_un cAddr;
    // 保存客户端的网络地址的长度
    socklen_t addrlen = sizeof(cAddr);

    // (4) 当没有客户端请求的时候,accept是阻塞的
    int confd = accept(sockfd, (struct sockaddr *)&cAddr, &addrlen);
    if (confd == -1) {
        close(sockfd);
        return -1;
    }

    printf("accept success\n");
    printf("client path:%s\n", cAddr.sun_path);

    while (1) {
    
        // (5) 进行通信,读写数据
        char buf[250] = {0};
        int r = recv(confd, buf, 250, 0);
        if (r != -1) {
            printf("r = %d,messagee = %s\n", r, buf);
        }

        if (buf[0] == '#') {
            close(confd);
            break;
        }
        send(confd, "hello", sizeof("hello"), 0);
    }

    // (6) 关闭socket套接字:"四次挥手"
    close(sockfd);

    return 0;
}
2.2 基于UDP实现UNIX域协议

unix_udp_client.c

cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>

#define SERVICEPATHNAME "/home/china/s"
#define CLIENTPATHNAME "/home/china/c"

int main(int argc, char *argv[]) {

    unlink(CLIENTPATHNAME);

    // 1. socket
    int sockfd =  socket(AF_UNIX, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("udp socket failed");
        return -1;
    }

    // 2. 必须bind
    // 需要一个网络地址结构体
    struct sockaddr_un cAddr;
    memset(&cAddr, 0, sizeof(cAddr));
    cAddr.sun_family = AF_UNIX; // 协议族
    strcpy(cAddr.sun_path, CLIENTPATHNAME);

    int res = bind(sockfd, (struct sockaddr *)&cAddr, sizeof(cAddr));
    if (res == -1) {
        perror("unix bind failed");
        close(sockfd);
        return -1;
    }
    
    // 需要一个网络地址,指定发给哪一个服务器
    struct sockaddr_un sAddr;
    memset(&sAddr, 0, sizeof(sAddr));
    sAddr.sun_family = AF_UNIX; // 协议族
    strcpy(sAddr.sun_path, SERVICEPATHNAME);

    // 当服务器发信息给我时,保存该服务器的网络地址
    struct sockaddr_un server_addr;
    socklen_t addrlen = sizeof(server_addr);

    while (1) {
        // 2. sendto
        char buf[250] = {0};
        scanf("%s", buf);
        int w =  sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&sAddr, sizeof(sAddr));
        printf("w = %d\n", w);

        if (buf[0] == '#') { // 退出条件
            break;
        }

        // 3. recvfrom
        char buff[250] = {0};
        int r = recvfrom(sockfd, buff, 250, 0, (struct sockaddr*)&server_addr, &addrlen);
        if (r > 0) {
            printf("r = %d, buff = %s\n", r, buff);
            printf("server path:%s\n", server_addr.sun_path);
        }
    }

    // 4. close
    close(sockfd);

    return 0;
}

unix_udp_server.c

cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>

#define SERVICEPATHNMAE "/home/china/s"

int main(int argc, char *argv[]) {

    unlink(SERVICEPATHNMAE);

    // 1. socket
    int sockfd =  socket(AF_UNIX, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("unix socket failed");
        return -1;
    }

    // 2. bind
    // 需要一个网络地址结构体
    struct sockaddr_un sAddr;
    memset(&sAddr, 0, sizeof(sAddr));
    sAddr.sun_family = AF_UNIX; // 协议族
    strcpy(sAddr.sun_path, SERVICEPATHNMAE);

    int res = bind(sockfd, (struct sockaddr *)&sAddr, sizeof(sAddr));
    if (res == -1) {
        perror("unix bind failed");
        close(sockfd);
        return -1;
    }

    // 保存客户端的网络地址
    struct sockaddr_un cAddr;
    // 保存客户端的网络地址的长度
    socklen_t addrlen = sizeof(cAddr);

    while (1) {

        // 3. recvfrom
        char buf[250] = {0};
        int r = recvfrom(sockfd, buf, 250, 0, (struct sockaddr *)&cAddr, &addrlen);
        if (r > 0) {
            printf("r = %d, buf = %s\n", r, buf);
            printf("client path:%s\n", cAddr.sun_path);
        }

        // 4. sendto
        char buff[250] = {0};
        scanf("%s", buff);
        int w = sendto(sockfd, buff, strlen(buff), 0, (struct sockaddr *)&cAddr, addrlen);
        printf("w = %d\n", w);
    }

    // 5. close
    close(sockfd);

    return 0;
}
相关推荐
Hejjon5 小时前
SpringBoot 整合 SQLite 数据库
笔记
西洼工作室7 小时前
【java 正则表达式 笔记】
java·笔记·正则表达式
初学者7.8 小时前
Webpack学习笔记(2)
笔记·学习·webpack
新手上路狂踩坑9 小时前
Android Studio的笔记--BusyBox相关
android·linux·笔记·android studio·busybox
stm 学习ing10 小时前
HDLBits训练3
c语言·经验分享·笔记·算法·fpga·eda·verilog hdl
尘觉10 小时前
算法的学习笔记—扑克牌顺子(牛客JZ61)
数据结构·笔记·学习·算法
bohu8311 小时前
sentinel学习笔记1-为什么需要服务降级
笔记·学习·sentinel·滑动窗口
初学者7.12 小时前
Webpack学习笔记(3)
笔记·学习·webpack
bohu8313 小时前
sentinel学习笔记5-资源指标数据统计
笔记·sentinel·statisticslot
璞~13 小时前
MQTT 课程概览 (学习笔记)02
笔记·学习