Программирование сокетов домена UNIX на C, проблема с печатью

У меня проблема с печатью на моем сервере. Я хочу, чтобы была одновременная печать, когда у меня есть 2 или более клиентов, активных на терминалах. Однако я печатаю только с одного клиента за раз. Как только я закрываю клиент, другие клиенты могут свободно писать на сервер. Что я могу сделать, чтобы решить мою проблему?

Я попытался разветвить раздел печати, который, я думаю, ничего не сделал. (Только что понял, что если я это сделаю, то системный вызов select будет пустой тратой времени, я бы предпочел использовать системный вызов select) *edit

while(TRUE) {

    FD_ZERO(&readfds);

    FD_SET(socket1, &readfds);
    FD_SET(socket2, &readfds);
    FD_SET(socket3, &readfds);

    select(socket3+1, &readfds, NULL, NULL, NULL);

    //add socket1
    if(FD_ISSET(socket1, &readfds)) {
        if((client_socket1 = accept(socket1, NULL, NULL)) < 0) {
            perror("accept1");
            exit(EXIT_FAILURE);
        }
        printf("New Connection\n");

        puts("Welcome message1 sent successfully\n");
    }

    //add socket2
    if(FD_ISSET(socket2, &readfds)) {
        if((client_socket2 = accept(socket2, (struct sockaddr *)&addr2, (socklen_t*)&addr2)) < 0) {
            perror("accept2");
            exit(EXIT_FAILURE);
        }
        printf("New Connection\n");

        puts("Welcome message2 sent successfully\n");
    }

    //add socket 3
    if(FD_ISSET(socket3, &readfds)) {
        if((client_socket3 = accept(socket3, (struct sockaddr *)&addr3, (socklen_t*)&addr3)) < 0) {
            perror("accept3");
            exit(EXIT_FAILURE);
        }
        printf("New Connection\n");

        puts("Welcome message3 sent successfully\n");
    }

    //print from socket 3
    while( (ready = read(client_socket3, buffer, sizeof(buffer))) > 0) {
        printf("%s\n", buffer);
    }

    //print from socket 2
    while( (ready = read(client_socket2, buffer, sizeof(buffer))) > 0) {
        printf("%s\n", buffer);
    }

    //print from socket 1
    while( (ready = read(client_socket1, buffer, sizeof(buffer))) > 0) {
        printf("%s\n", buffer);
    }
}

person Michael    schedule 10.05.2015    source источник


Ответы (1)


Вам необходимо добавить свои клиентские сокеты в операторы fd_set и select перед попыткой чтения из них. Кроме того, вы должны сделать все ваши сокеты неблокирующими. В противном случае вызов read будет заблокирован, пока вы не получите данные.

Вот быстрое исправление, которое использует recv вместо read для чтения сокетов, но с асинхронным флагом MSG_DONTWAIT.

Я нигде не видел, чтобы вы закрывали свои клиентские сокеты или правильно обрабатывали ошибки. Поэтому я вставил код в качестве подсказки. Кроме того, никогда не рекомендуется «печатать» буфер данных напрямую из сокета. Потому что вы никогда не знаете, завершаются ли данные, которые вы получили, нулем. Всегда нулевое завершение буфера после считывания данных из сокета.

Измените этот блок кода:

//print from socket 3
while( (ready = read(client_socket3, buffer, sizeof(buffer))) > 0) {
    printf("%s\n", buffer);
}

К этому:

while (1)
{
    int result;
    result = recv(client_socket3, buffer, sizeof(buffer)-1, MSG_DONTWAIT);
    if ((result == -1) && 
             ((errno == EAGAIN) || (errno==EWOULDBLOCK)) )
    {
        // no more data available, but could be available later
        // use the socket with "select" above to wait for more data
    }
    else if ((result == -1) || (result == 0))
    {
        // remote close or unrecoverable error
        close(client_socket3);
        client_socket3=-1;
    }
    else
    {
        // null terminate the buffer before printing
        buffer[result] = '\0';
        printf("%s\n", buffer);
    }
}
person selbie    schedule 10.05.2015
comment
Просто интересно, придется ли мне менять все мои блоки while для печати на это? - person Michael; 10.05.2015
comment
@Майкл - Да. Но почему бы вам не преобразовать приведенный выше фрагмент кода в отдельную функцию, которая принимает сокет в качестве параметра. например receive_and_print(socket_t sock) {...} Тогда вам не придется повторять этот блок кода снова и снова. - person selbie; 10.05.2015