关于Unix Domain Socket的使用入门

起因

最近在看ceph的动态参数修改。就观察到了Unix Domain Socket这个技术。

那这个UDS具体是个啥呢?

Unix Domain Socket(UDS) 是一种 在同一台计算机上的不同进程之间进行通信的机制。你可以把它理解为"本地版的网络套接字",但它不走网络,只在操作系统内部完成数据交换。

一个例子

假定我有个c++程序,里面有个变量,我现在想修改它怎么办?

简单说一个c++每隔几秒钟打印一个字符串,这个字符串就存在进程的内存里,并不是从磁盘里实时读取的。现在想让程序打印另一个字符串怎么办?

来看代码:

服务端代码

cpp 复制代码
// server.cpp
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <signal.h>

const char* SOCKET_PATH = "/tmp/uds_example.sock";

// 全局变量,被循环输出
std::string message = "Hello from C++ server!";

// 清理 socket 文件
void cleanup() {
    unlink(SOCKET_PATH);
    std::cout << "\n[Server] Shutdown: socket cleaned up.\n";
}

// 信号处理(Ctrl+C)
void sigint_handler(int sig) {
    cleanup();
    exit(0);
}

int main() {
    int server_fd, client_fd;
    struct sockaddr_un addr;
    socklen_t len;

    // 注册信号处理
    signal(SIGINT, sigint_handler);

    // 创建 UDS socket
    server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (server_fd == -1) {
        perror("[Server] socket");
        return 1;
    }

    // 绑定地址
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);

    // 删除旧 socket(如果存在)
    unlink(SOCKET_PATH);

    if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        perror("[Server] bind");
        close(server_fd);
        return 1;
    }

    if (listen(server_fd, 1) == -1) {
        perror("[Server] listen");
        close(server_fd);
        return 1;
    }

    std::cout << "[Server] Listening on " << SOCKET_PATH << std::endl;
    std::cout << "[Server] Initial message: \"" << message << "\"" << std::endl;
    std::cout << "[Server] Waiting for client..." << std::endl;

   // 正确设置非阻塞模式
   int flags = fcntl(server_fd, F_GETFL, 0);        // 先获取原有标志
   if (flags == -1) {
       perror("fcntl getfl");
       close(server_fd);
       return 1;
   }
   fcntl(server_fd, F_SETFL, flags | O_NONBLOCK);  // 再加上 O_NONBLOCK

    while (true) {
        // 尝试接受连接(非阻塞)
        client_fd = accept(server_fd, (struct sockaddr*)&addr, &len);
        if (client_fd != -1) {
            char buffer[1024];
            ssize_t bytes = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
            if (bytes > 0) {
                buffer[bytes] = '\0';
                std::cout << "\n[Server] Received from client: \"" << buffer << "\"" << std::endl;
                message = buffer;  // 更新全局变量
            }
            if (config.message=="shutdown"){
                std::cout << "[Server] i get shutdown command" << std::endl;
                break;
            }
            close(client_fd);
        } else if (errno != EAGAIN && errno != EWOULDBLOCK) {
            // 真正的错误
            if (errno != EINTR) {
                usleep(100000); // 短暂休眠避免忙等
            }
        }

        // 持续输出当前消息
        std::cout << "[Server] Current message: \"" << message << "\"" << std::endl;
        usleep(500000); // sleep 0.5s
    }

    close(server_fd);
    cleanup();
    return 0;
}

client端代码

cpp 复制代码
// client.cpp
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>

const char* SOCKET_PATH = "/tmp/uds_example.sock";

int main(int argc, char* argv[]) {
    if (argc != 2) {
        std::cerr << "Usage: " << argv[0] << " <new_message>\n";
        std::cerr << "Example: " << argv[0] << " \"Hello World!\"\n";
        return 1;
    }

    std::string message = argv[1];

    int sockfd;
    struct sockaddr_un addr;

    // 创建 UDS socket
    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("[Client] socket");
        return 1;
    }

    // 设置地址
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);

    // 连接服务端
    if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        perror("[Client] connect");
        std::cerr << "[Client] Is the server running?\n";
        close(sockfd);
        return 1;
    }

    // 发送消息
    if (send(sockfd, message.c_str(), message.length(), 0) == -1) {
        perror("[Client] send");
        close(sockfd);
        return 1;
    }

    std::cout << "[Client] Sent: \"" << message << "\"\n";

    close(sockfd);
    return 0;
}

大家试试呗

相关推荐
2401_868534783 小时前
软考网络规划设计师SSL协议知识点详解
服务器·网络
额额额对了6 小时前
嵌入式 Linux 实战:i.MX6ULL 上的 CAN 通信与摄像头采集
linux·运维·服务器
灯澜忆梦7 小时前
【Linux】#10 | Linux 终端实用快捷键 + 安装应用
linux·运维·服务器
码士集团小青8 小时前
面霸篇:高频 Java 基础问题(核心卷一)
linux·服务器·数据库·redis·缓存·1024程序员节
OnlyEasyCode9 小时前
IIS开放端口转发后端
运维·服务器
96245611 小时前
一次 Dell PowerEdge R750xs 启动故障复盘:从 iDRAC 带外管理到 Linux 多内核回退与 initramfs 重建
linux·运维·服务器
Ruiery12 小时前
Linux 6.6内核 CPU 深度解析(零):x86 内核引导协议 — 从 bootloader 到 main()
linux·运维·服务器
Ruiery12 小时前
Linux 6.6内核 CPU 深度解析(十六):x86 保护机制 — 从 ring 到页表的权限防线
linux·运维·服务器
贵州诚鑫致达科技有限公司13 小时前
PHP两个9.2分RCE漏洞,速查版本
服务器·php·1024程序员节