В настоящее время я пытаюсь реализовать чат-сервер / чат с помощью именованных каналов. Сервер принимает имя канала в качестве аргумента командной строки. Затем он приступает к созданию каналов и открывает их для чтения. Другие процессы открывают канал для записи и запрашивают ввод у пользователя (клиент). Как только пользователь нажимает ввод, сообщение записывается в канал. Когда пользователь нажимает ввод, не вводя никаких символов, клиент закрывает канал и завершает работу. Сервер не должен поддерживать переподключения, а клиенты должны подключаться в порядке, указанном в командной строке. Это прекрасно работает только для одного клиента, но не для второго или третьего. Мой код для реализации этого поведения следующий:
Примечание: хештег плюс имя клиента служит для идентификации клиента. Он отправляется вначале для регистрации клиента на сервере, чтобы он знал, с каким клиентом он общается. В файле fifo.h
соответственно fifo.c
есть мои пользовательские реализации create_fifo
, open_fifo
и close_fifo
, которые обрабатывают создание, открытие и закрытие пафосов.
Редактировать : Теперь я на шаг ближе к тому, чего я должен достичь. Мне пришлось добавить несколько строк в код, который я отмечу в коде ниже, добавив комментарий. Единственная проблема, которая сохраняется, заключается в том, что сервер не распознает, когда второй пользователь покидает сервер первым, а затем первый. Таким образом, он оставляет каналы открытыми и не возвращается к функции clean_up
.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <string.h>
#include "fifo.h"
void clean_up(int* fifos, char** argv, int n);
int main(int argc, char** argv){
int clients = argc - 1;
int fifos[clients];
char reg[clients][PIPE_BUF];
fd_set active_clients;
FD_ZERO(&active_clients);
for (int i = 0; i < clients; i++){
create_fifo(argv[i + 1]);
fifos[i] = open_fifo(argv[i + 1], O_RDONLY);
FD_SET(fifos[i], &active_clients);
}
fd_set tmpset;
memcpy(&tmpset, &active_clients, sizeof(fd_set)); // <-- I added this line
setbuf(stdout, NULL);
while (clients > 0){
if (select(fifos[clients - 1] + 1, &active_clients, NULL, NULL, NULL) < 0){
fprintf(stderr, "Failed to select fifos\n");
clean_up(fifos, argv, argc);
return EXIT_FAILURE;
}
for (int i = 0; i < clients; i++){
if (FD_ISSET(fifos[i], &active_clients)){
char buf[PIPE_BUF];
read(fifos[i], buf, sizeof(buf));
if (buf[0] == '#'){
strncpy(reg[i], buf + 1, PIPE_BUF);
printf("%s joined the server\n", reg[i]);
continue;
}
if (buf[0] == '\n'){
clients--;
FD_CLR(fifos[i], &tmpset);
FD_CLR(fifos[i], &active_clients);
printf("%s left the server\n", reg[i]);
continue;
}
printf("%s: %s", reg[i], buf);
}
}
memcpy(&active_clients, &tmpset, sizeof(fd_set)); // <-- I added this line
}
clean_up(fifos, argv, argc);
return EXIT_SUCCESS;
}
void clean_up(int* fifos, char** argv, int n){
for (int i = 1; i < n; i++){
close(fifos[i - 1]);
close_fifo(argv[i]);
}
}
А на стороне клиента реализовано так:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include "fifo.h"
int main(int argc, char** argv){
int fp = open_fifo(argv[argc - 1], O_WRONLY);
char id[BUF_SIZE] = "";
char* client_name = argv[argc - 1];
strncat(id, "#", 1);
strncat(id, client_name, strlen(client_name));
write(fp, id, strlen(id) + 1);
while (1){
char buf[PIPE_BUF];
printf("Message: ");
fgets(buf, PIPE_BUF, stdin);
write(fp, buf, strlen(buf) + 1);
if (buf[0] == '\n'){
break;
}
}
close(fp);
return EXIT_SUCCESS;
}
Насколько я понял выход, это как-то связано с командой выбора. Он блокируется до тех пор, пока у одного из fifos не будет готов ввод данных, но только для первого клиента, который отправит сообщение. К сожалению, он не разблокируется для других клиентов. Кроме того, я не знаю, является ли мой аргумент, переданный для nfds
в команде выбора, правильным. Я также попытался вставить FD_SETSIZE
, но это не сильно изменилось. Кто-нибудь знает, что я здесь делаю не так?