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
相关产品推荐
相关产品推荐

