如何使用Docker组成的本地客户端应用程序在本地插座上进行通信? 我的客户端代码这样,在其中常数socket_path为“/tmp/my_socket”: #Define socket_path“/tmp/my_socket” //其余代码 int main(int argc,char *argv [] ...

问题描述 投票:0回答:1

#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; }

在我的Ubuntu虚拟机上使用GCC编译器在不使用Docker的情况下执行代码很好。但是,当我必须使用Docker时,服务器端绑定出了问题。
这是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"]

最终我的码头人组成的是:

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构成时,Docker给我的错误 - 建造就是这样:

[+] Running 3/3 ✔ Network progettotrislso_default Created 0.1s ✔ Container my_server Created 0.2s ✔ Container my_client Created 0.3s Attaching to my_client, my_server my_client | Errore connect: Connection refused my_client exited with code 1 my_server | Server in esecuzione... my_server | Errore bind strano: Address already in use my_server exited with code 1

我可以理解客户端错误,但是为什么服务器错误?在我的Ubuntu上,它可以正常工作,这是怎么回事?这与Docker卷有关吗?

eDit

因此,我尝试使用所建议的命名卷,现在我的Docker组成的文件看起来像:

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

当我运行Docker撰写时 - 现在不再有绑定错误,但是现在的输出就是这样:

[+]运行2/2

✔重新创建的容器my_client 11.5s ✔重新创建的容器my_server 11.1s 附加到my_client,my_server
my_client|本文托! cosa vuoi票价? premi://这是我的菜单

my_client|每次Uniriri a unapartita

my_client|每个creare unapartita

my_client| qualsiasi altro tasto peruscire

my_client| connessione con Il服务器intertrotta //连接用服务器终止

My_client用代码0

退出 SO,基本上,服务器发送了他的欢迎消息,但是随后它在循环的情况下进行了循环,而无需在他自己的终端上打印任何内容,而它至少应该打印“服务器启动”并等待客户端输入和反应,加上客户端将失去与服务器的连接,并且不会按要求输入用户输入,这不应发生。在Ubuntu虚拟机上测试时,我没有所有这些问题。作为参考,这是我的服务器代码,它将您看到的消息发送给客户端:

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 // printf("SCELTO 1\n"); 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) { //gestione del caso l'input non sia giusto lato server // if (gameFound != NULL) { // printf("QUI!\n"); // sprintf(buffer, "Partita trovata!\n"); // send(fd, buffer, strlen(buffer), 0); // break; // } else { 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); //Operazione bloccante finché un altro giocatore non si unisce initialize_game(createdGame); play_game(createdGame, fd); break; default: close(fd); removeClientFromList(fd); printf("Connessione con il client %d interrotta\n", fd); }

} 正如您所看到的那样,它更加复杂,但是似乎在循环循环时,同时不打印任何东西,而客户只是突然终止,这是怎么回事?这是用于参考的客户端代码:

void clientActions() { nbytes=recv(socket_fd, buffer, 256, 0); buffer[nbytes]=0; printf("%s", buffer); int scelta; scanf("%d", &scelta); // getchar(); 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"); } }

UNIX插座是一个从一个过程到另一个过程的内核管理的传球。 使它们起作用的最可靠的方法是创建一个名为“插入插座”目录并将相同音量安装到两个容器中的码头。
在您的示例中,插座存储在
/tmp

中,因此您的设置需要看起来像

version: '3.8'
services:
  server:
    ...

    volumes:
      - socket:/tmp
  client:
    volumes:
      - socket:/tmp
volumes:
  socket:
c linux docker docker-compose client-server
1个回答
0
投票
另一个重要的细节是UNIX插座是特定于内核的。 如果您使用的是Docker桌面或其他基于VM的设置,则可以在Docker VM中同时使用主机内核和单独的内核。 这意味着您不一定使用UNIX套接字在容器和主机之间进行通信,并且根据特定文件系统设置,绑定座可能不起作用。 (如果您直接在本机Linux主机上使用Docker Engine,则主机的内核与容器和绑定安装应起作用。)

您围绕容器添加了一个其他问题,试图从自己的stdin中读取并立即退出。 取决于直接控制台I/O的服务不一定在撰写中运行良好。 您可以使用

volumes:

选项来确保有stdin;如果您没有此选项,则您的过程将看到封闭的文件描述符并立即退出。 即使是,您将无法使用本机组合命令在过程中输入。 您需要使用
stdin_open:

开始输入运行的容器,该容器涉及查找容器名称(或用Compose
docker attach

手动指定它)。

另外,请参阅

docker-compose和用户输入stdin
.


最新问题
© www.soinside.com 2019 - 2025. All rights reserved.