#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_servermy_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:
您围绕容器添加了一个其他问题,试图从自己的stdin中读取并立即退出。 取决于直接控制台I/O的服务不一定在撰写中运行良好。 您可以使用
volumes:
选项来确保有stdin;如果您没有此选项,则您的过程将看到封闭的文件描述符并立即退出。
即使是,您将无法使用本机组合命令在过程中输入。 您需要使用stdin_open:
开始输入运行的容器,该容器涉及查找容器名称(或用Compose
docker attach
手动指定它)。
另外,请参阅
docker-compose和用户输入stdin