Unix域套接字—基于文件的Socket通信

2024年 1月 6日 74.5k 0

一.概述

在Linux中,.sock 文件通常是Unix域套接字(Unix Domain Socket)的一部分。

Unix域套接字是一种用于进程间通信的特殊类型的套接字,它不依赖于网络协议,而是使用文件系统作为通信的载体

Unix域套接字的特点:

  • 通信基于文件系统:Unix域套接字通过在文件系统中创建一个特殊类型的文件(.sock 文件)来实现进程间通信。这个文件既可以在本地文件系统上,也可以在共享文件系统上。
  • 高性能:与基于网络的套接字相比,Unix域套接字通常具有更低的延迟和更高的吞吐量,因为它们不需要经过网络协议栈。
  • 进程间通信:进程可以通过Unix域套接字相互发送消息,进行数据传输,实现进程间通信。

二.Unix套接字建立流程

图片[1]-Unix域套接字—基于文件的Socket通信-不念博客整体流程图

Unix域套接字的创建通常涉及以下步骤:

  • 创建套接字: 使用 socket() 系统调用创建一个Unix域套接字。
  • int socket(int domain, int type, int protocol);
  • 绑定地址: 使用 bind() 系统调用将套接字与一个文件路径绑定,创建一个.sock文件。
  • int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);`
  • 监听连接(可选): 如果需要,可以使用 listen() 系统调用监听连接。
  • int listen(int sockfd, int backlog);
  • 接受连接(可选): 使用 accept() 系统调用接受客户端的连接请求。
  • int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • 进行通信: 使用 send() 和 recv() 等系统调用进行进程间通信。
  • ssize_t send(int sockfd, const void *buf, size_t len, int flags); ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  • 关闭套接字: 使用 close() 关闭Unix域套接字。
  • int close(int sockfd);

    7.销毁文件

     // 删除创建的文件
        unlink(SOCKET_PATH);

    三.综合示例

    server.c文件

    #include <sys/socket.h>
    #include <sys/un.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    #define SOCKET_PATH "example.sock"
    #define BUFFER_SIZE 1024
    
    int main() {
        int server_socket, client_socket;
        struct sockaddr_un server_address, client_address;
        socklen_t client_address_len = sizeof(client_address);
        char buffer[BUFFER_SIZE];
    
        // 创建套接字
        server_socket = socket(AF_UNIX, SOCK_STREAM, 0);
        if (server_socket == -1) {
            perror("Socket creation failed");
            exit(EXIT_FAILURE);
        }
    
        // 设置服务器地址结构
        memset(&server_address, 0, sizeof(server_address));
        server_address.sun_family = AF_UNIX;
        strncpy(server_address.sun_path, SOCKET_PATH, sizeof(server_address.sun_path) - 1);
    
        // 绑定地址
        if (bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) {
            perror("Bind failed");
            exit(EXIT_FAILURE);
        }
    
        // 监听连接
        if (listen(server_socket, 5) == -1) {
            perror("Listen failed");
            exit(EXIT_FAILURE);
        }
    
        printf("等待客户端连接...n");
    
        // 接受连接
        client_socket = accept(server_socket, (struct sockaddr*)&client_address, &client_address_len);
        if (client_socket == -1) {
            perror("Accept failed");
            exit(EXIT_FAILURE);
        }
    
        printf("客户端已连接n");
    
        // 接收数据
        ssize_t bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);
        if (bytes_received == -1) {
            perror("Receive failed");
            exit(EXIT_FAILURE);
        }
    
        buffer[bytes_received] = ''; // 添加字符串结束符
        printf("接收到的数据: %sn", buffer);
    
        // 发送响应
        const char* response = "收到消息";
        if (send(client_socket, response, strlen(response), 0) == -1) {
            perror("Send failed");
            exit(EXIT_FAILURE);
        }
    
        // 关闭套接字
        close(client_socket);
        close(server_socket);
    
        // 删除创建的文件
        unlink(SOCKET_PATH);
    
        return 0;
    }

    client.c文件:

    #include <sys/socket.h>
    #include <sys/un.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    #define SOCKET_PATH "example.sock"
    #define BUFFER_SIZE 1024
    
    int main() {
        int client_socket;
        struct sockaddr_un server_address;
        char buffer[BUFFER_SIZE];
        ssize_t bytes_received;
    
        // 创建套接字
        client_socket = socket(AF_UNIX, SOCK_STREAM, 0);
        if (client_socket == -1) {
            perror("Socket creation failed");
            exit(EXIT_FAILURE);
        }
    
        // 设置服务器地址结构
        memset(&server_address, 0, sizeof(server_address));
        server_address.sun_family = AF_UNIX;
        strncpy(server_address.sun_path, SOCKET_PATH, sizeof(server_address.sun_path) - 1);
    
        // 连接服务器
        if (connect(client_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) {
            perror("Connection failed");
            exit(EXIT_FAILURE);
        }
    
        // 发送数据
        const char* message = "Hello, Server!";
        if (send(client_socket, message, strlen(message), 0) == -1) {
            perror("Send failed");
            exit(EXIT_FAILURE);
        }
    
        printf("发送消息: %sn", message);
    
        // 接收响应
        bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);
        if (bytes_received == -1) {
            perror("Receive failed");
            exit(EXIT_FAILURE);
        }
    
        buffer[bytes_received] = ''; // 添加字符串结束符
        printf("服务器响应: %sn", buffer);
    
        // 关闭套接字
        close(client_socket);
    
        return 0;
    }

    效果展示:

    图片[2]-Unix域套接字—基于文件的Socket通信-不念博客client/server

    大家可以根据实际的需求,对这个Demo进行修改,通过这个例子我们可以基本认识到通过文件建立Unix域套接字的通信过程。

    相关文章

    服务器端口转发,带你了解服务器端口转发
    服务器开放端口,服务器开放端口的步骤
    产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
    如何使用 WinGet 下载 Microsoft Store 应用
    百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
    百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

    发布评论