You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何修改Unix域Socket服务端代码解决‘Bind: address already in use’错误?

解决Unix域Socket服务端的"Bind: address already in use"错误

这个问题我之前踩过坑,核心原因是Unix域Socket依赖文件系统中的路径作为标识——如果服务端上次退出时没清理掉这个socket文件,下次启动调用bind()就会直接报错。下面给你具体的修改方案和完整代码:

关键修改点

  • 绑定前清理残留的socket文件:在调用bind()之前,先尝试删除已存在的socket路径文件,避免因残留文件导致绑定失败。
  • 注册退出清理钩子:确保程序无论正常退出还是异常退出(比如按Ctrl+C),都能自动删除socket文件,防止残留。
  • 可选:设置套接字复用选项:部分系统下配合SO_REUSEADDR选项可以增强兼容性,但核心还是文件清理。

修改后的完整代码

/* socket demonstrations: this is the server side of a "unix domain" socket connection */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>

#define SOCKET_PATH "/tmp/my_unix_socket"

// 清理socket文件的函数
void cleanup_socket() {
    if (unlink(SOCKET_PATH) == -1) {
        if (errno != ENOENT) { // 只有当不是"文件不存在"的错误时才打印
            perror("Failed to unlink socket file");
        }
    }
}

// 处理中断信号(比如Ctrl+C),确保清理socket
void handle_signal(int sig) {
    cleanup_socket();
    exit(EXIT_SUCCESS);
}

int main() {
    int fd, clientfd;
    int len;
    socklen_t size;
    char buf[80];
    struct sockaddr_un r, q;

    // 注册退出清理钩子,正常退出时自动执行
    atexit(cleanup_socket);
    // 注册信号处理,捕获Ctrl+C等中断信号
    signal(SIGINT, handle_signal);
    signal(SIGTERM, handle_signal);

    /* create a socket */
    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        return 1;
    }

    // 设置SO_REUSEADDR选项(可选,增强兼容性)
    int reuse = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {
        perror("setsockopt");
        close(fd);
        return 1;
    }

    /* setup the socket address structure */
    memset(&r, 0, sizeof(r));
    r.sun_family = AF_UNIX;
    strncpy(r.sun_path, SOCKET_PATH, sizeof(r.sun_path) - 1);

    // 绑定前先清理残留的socket文件
    cleanup_socket();

    /* bind the socket to the address */
    len = sizeof(r);
    if (bind(fd, (struct sockaddr *)&r, len) == -1) {
        perror("bind");
        close(fd);
        return 1;
    }

    /* listen for connections */
    if (listen(fd, 5) == -1) {
        perror("listen");
        close(fd);
        return 1;
    }

    printf("Server listening on %s\n", SOCKET_PATH);

    /* accept a connection */
    size = sizeof(q);
    if ((clientfd = accept(fd, (struct sockaddr *)&q, &size)) == -1) {
        perror("accept");
        close(fd);
        return 1;
    }

    /* read from client */
    memset(buf, 0, sizeof(buf));
    if (read(clientfd, buf, sizeof(buf)) == -1) {
        perror("read");
        close(clientfd);
        close(fd);
        return 1;
    }
    printf("Received: %s\n", buf);

    /* write response to client */
    const char *response = "Hello from server!";
    if (write(clientfd, response, strlen(response)) == -1) {
        perror("write");
        close(clientfd);
        close(fd);
        return 1;
    }

    /* close connections */
    close(clientfd);
    close(fd);

    return 0;
}

代码说明

  1. cleanup_socket()函数:专门负责删除socket文件,会忽略"文件不存在"的错误(因为第一次启动时文件本来就不存在)。
  2. atexit(cleanup_socket):注册一个程序退出时的钩子,不管是main()正常结束还是调用exit(),都会自动执行清理。
  3. 信号处理:捕获SIGINT(Ctrl+C)和SIGTERM(kill命令)信号,确保程序被中断时也能清理socket文件。
  4. 绑定前的清理:在bind()前主动调用cleanup_socket(),彻底避免残留文件导致的绑定失败。
  5. SO_REUSEADDR选项:虽然Unix域下这个选项的作用不像TCP那样明显,但部分系统中可以允许快速重启服务,加上更稳妥。

这样修改后,你就不会再碰到"Bind: address already in use"的错误啦!

内容的提问来源于stack exchange,提问作者rebel ion

火山引擎 最新活动