You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker Compose下本地套接字客户端-服务端通信故障排查

AF_LOCAL本地套接字Docker Compose部署异常排查

我有一套基于AF_LOCAL本地套接字的C语言客户端与服务端代码,常量SOCKET_PATH设为"/tmp/my_socket",在Ubuntu虚拟机直接编译运行正常,但用Docker Compose部署时出现两个异常:

  • 初始使用主机卷映射/tmp/my_socket:/tmp/my_socket时,服务端报Errore bind strano: Address already in use绑定错误,客户端连接被拒绝;
  • 改用Docker命名卷映射/tmp目录后,绑定错误消失,但服务端仅发送欢迎消息后无输出,客户端接收消息后直接断开连接,无法等待用户输入。

相关代码与配置

客户端代码

#define SOCKET_PATH "/tmp/my_socket"
//rest of the code

int main(int argc, char *argv[])
{
    struct sockaddr_un server_addr;

    server_addr.sun_family = AF_LOCAL; /*socket locali*/
    strcpy(server_addr.sun_path, SOCKET_PATH);

    if ((socket_fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0)
    {
        perror("Errore creazione welcoming socket");
        return 1;
    }

    if ((connect(socket_fd,
                 (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0))
    {
        perror("Errore connect");
        return 1;
    }

    clientActions();

    /*Pulizia prima della terminazione*/
    close(socket_fd);
    return 0;
}

服务端代码

#define SOCKET_PATH "/tmp/my_socket"
//rest of the code

int main(int argc, char* argv[]) {
    int socket_fd, conn_fd;
    struct sockaddr_un server_addr, client_addr;

    server_addr.sun_family=AF_LOCAL; /*socket locali*/
    unlink(SOCKET_PATH); /*Rimuove la socket se già esiste*/

    strcpy(server_addr.sun_path, SOCKET_PATH);

    printf("Server in esecuzione...\n");
    sleep(1);

    if ((socket_fd=socket(PF_LOCAL,SOCK_STREAM,0)) < 0){
        perror("Errore creazione welcoming socket");
        return 1;
    }
    
    if ((bind(socket_fd,
    (struct sockaddr*)&server_addr,sizeof(server_addr))<0)) {
        perror("Errore bind strano");
        return 1;
    }

    if ((listen(socket_fd, 5)) < 0){ /*5 è la dim della coda di attesa*/
        perror("Errore nel listen");
        return 1;
    }

    while (1) {
        if ((conn_fd = accept(socket_fd,NULL,NULL)) < 0) {
            perror("Errore accept");
            return 1;
        }
        addClientToList(conn_fd);
        pthread_t tid;
        int* conn_fd_ptr = malloc(sizeof(int));
        *conn_fd_ptr = conn_fd; //Per evitare problemi con var condivise
        // TODO: gestione thread
        if (pthread_create(&tid, NULL, connection_handler, conn_fd_ptr) != 0) {
            perror("Errore creazione thread");
            free(conn_fd_ptr);
            close(conn_fd);
            removeClientFromList(conn_fd);
            continue;
        }

        pthread_detach(tid);
    }

    /*Pulizia prima della terminazione*/
    close(socket_fd);
    unlink(SOCKET_PATH);
    return 0;
}

Dockerfile.server

FROM gcc:11.4

# Crea una cartella per l'app
RUN mkdir -p /opt/app
WORKDIR /opt/app

# Copia i file del server
COPY server.c /opt/app

# Compila il server e collega la libreria pthread
RUN gcc -o server.out server.c -lpthread

# EXPOSE 9200

CMD ["./server.out"]

Dockerfile.client

FROM gcc:11.4

# Crea una cartella per l'app
RUN mkdir -p /opt/app
WORKDIR /opt/app

# Copia i file del client
COPY client.c /opt/app

# Compila il client e collega la libreria pthread
RUN gcc -o client.out client.c -lpthread

CMD ["./client.out"]

初始Docker Compose配置

version: '3'
services:
  server:
    build:
      context: .
      dockerfile: Dockerfile.server
    container_name: my_server
    # ports:
    #   - "9000:9100"  # Espone la porta del server sulla macchina host
    volumes:
      - /tmp/my_socket:/tmp/my_socket  # Condivide il socket file

  client:
    build:
      context: .
      dockerfile: Dockerfile.client
    container_name: my_client
    depends_on:
      - server  # Il client parte solo dopo che il server è pronto
    volumes:
      - /tmp/my_socket:/tmp/my_socket  # Usa lo stesso socket file

修改后的Docker Compose配置

version: '3'

volumes:
  socket:  # Definiamo un volume nominato

services:
  server:
    build:
      context: .
      dockerfile: Dockerfile.server
    container_name: my_server
    # ports:
    #   - "9000:9100"  # Espone la porta del server sulla macchina host
    volumes:
      - socket:/tmp # Condivide il socket file

  client:
    build:
      context: .
      dockerfile: Dockerfile.client
    container_name: my_client
    depends_on:
      - server  # Il client parte solo dopo che il server è pronto
    volumes:
      - socket:/tmp  # Usa lo stesso socket file

服务端connection_handler函数

void* connection_handler(void* conn_fd) {
int fd = *(int*) conn_fd;
free(conn_fd);
printf("Gestendo richiesta client %d...\n", fd);
char buffer[256];
int byte_letti = sprintf(buffer, "Benvenuto! Cosa vuoi fare? Premi:\n"
    "1 Per unirti a una partita\n"
    "2 Per creare una partita\n"
    "Qualsiasi altro tasto per uscire\n");
buffer[byte_letti] = 0;
send(fd, buffer, byte_letti, 0);

byte_letti=recv(fd, buffer, 256, 0);
buffer[byte_letti]=0;
// printf("Ricevuto: %s\n", buffer); // Debug per vedere il messaggio ricevuto

int scelta = atoi(buffer);
// printf("Scelta convertita: %d\n", scelta); // Debug
switch (scelta) {
    case 1: //Un giocatore si unisce ad una partita
        send_game_list(fd);
        int chosen_game = wait_game_choice(fd);
        if (chosen_game != -1) {
            Game* gameFound = find_game_and_join(chosen_game, fd);
            while(gameFound==NULL) {
                sprintf(buffer, "Nessuna partita corrisponde a quella scelta! Riprovare.\n");
                send(fd, buffer, strlen(buffer), 0);
                chosen_game = wait_game_choice(fd);
                gameFound = find_game_and_join(chosen_game, fd);
                continue;
            }
            play_game(gameFound, fd);
        }
        break;
    case 2: //Un giocatore crea una partita
        Game* createdGame = create_and_join(fd);
        initialize_game(createdGame);
        play_game(createdGame, fd);
        break;
    default:
        close(fd);
        removeClientFromList(fd);
        printf("Connessione con il client %d interrotta\n", fd);
}
}

客户端clientActions函数

void clientActions()
{
    nbytes=recv(socket_fd, buffer, 256, 0);
    buffer[nbytes]=0;
    printf("%s", buffer);

    int scelta;
    scanf("%d", &scelta);
    snprintf(buffer, sizeof(buffer), "%d", scelta);
    send(socket_fd, buffer, strlen(buffer), 0);
    
    switch (scelta) {
        case 1:
            receiveGamesList();
            chooseGame();
            waitJoinConfirmed(); 
            gameLoop();
            break;
        case 2:
            createGame();
            break;
        default:
            close(socket_fd);
            printf("Connessione con il server interrotta\n");
    }
}

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 18:57:04