Нужно ли устанавливать события вручную в соединении TCP-сервер - клиент с помощью Poll ()?Я никогда не достигаю POLLOUT для записи в сокет - PullRequest
0 голосов
/ 21 мая 2019

Я установил соединение между клиентом и сервером в c с помощью poll () на одном fd. Я хочу, чтобы клиент получил сообщение, когда серверу есть, что отправлять, и наоборот.

Насколько я знаю, poll () прослушивает события в файловых дескрипторах. Однако мне неясно, как эти события (или события) инициируются, чтобы определить, когда пришло время отправлять или получать на FD.

Я пытался использовать чтение и запись (или отправку и получение в этом отношении) в цикле для клиентской стороны, но они блокируются, поэтому я переключился на poll () для клиентской стороны.

На стороне клиента, я никогда не получаю доступ к части }else if (fds[0].revents & POLLOUT), что означает, что сокет никогда не доступен для записи.

   //Create - bind - listen to a socket named listeningSocket

    struct pollfd fds[1];
    fds[0].fd = listeningSocket;
    fds[0].events = POLLIN | POLLPRI;


 if (poll(fds, 1, 3000)) {
            (client_sock = accept(listeningSocket, &client, (socklen_t *) &c));
            spdlog::info("Connection accepted");

            std::thread thread(&ConnectionHandler::Handle, std::ref(requestHandler), client_sock);
            thread.detach();

}

Клиентский блок


 while (true) {
        if ((rv = poll(fds, 1, 100) > 0)) {

            if (fds[0].revents & POLLIN ){
                recv(sockfd, buff, 200, 0);
                printf("From Server : %s", buff);
                bzero(buff, sizeof(buff));

            }
        }else if (fds[0].revents & POLLOUT){
            puts(buff);
            strcpy(buff, "HELLO WORLD");
            write(sockfd, buff, sizeof(buff));
            break;
        }
    }


При подключении сервер отправляет клиенту приветственное сообщение с write(). Клиент recv() с этим, но затем никогда не возвращается на сервер.

Я что-то упустил? Разве сокет не должен быть готов к записи, когда на нем нет данных, которые могут быть получены?

Ответы [ 2 ]

1 голос
/ 21 мая 2019

Позволяет переформатировать ваш цикл while, чтобы его было легче читать:

while (true)
{
    if ((rv = poll(fds, 1, 100) > 0))
    {
        if (fds[0].revents & POLLIN)
        {
            recv(sockfd, buff, 200, 0);
            printf("From Server : %s", buff);
            bzero(buff, sizeof(buff));
        }
    }
    else if (fds[0].revents & POLLOUT)
    {
        puts(buff);
        strcpy(buff, "HELLO WORLD");
        write(sockfd, buff, sizeof(buff));
        break;
    }
}

Теперь должно быть понятно, почему ваш блок POLLOUT никогда не выполняется - этот оператор if достигается только тогда, когдаpoll() возвращает <= 0, что не то, что вам нужно.

Вместо этого вам нужна такая логика:

while (true)
{
    if ((rv = poll(fds, 1, 100) > 0))
    {
        if (fds[0].revents & POLLIN )
        {
            recv(sockfd, buff, 200, 0);
            printf("From Server : %s", buff);
            bzero(buff, sizeof(buff));
        }
        else if (fds[0].revents & POLLOUT)
        {
            puts(buff);
            strcpy(buff, "HELLO WORLD");
            write(sockfd, buff, sizeof(buff));
            break;
        }
    }
}

, которая при переформатировании обратно в исходный стиль кодированияВыглядит так:

while (true) {
    if ((rv = poll(fds, 1, 100) > 0)) {

        if (fds[0].revents & POLLIN ){
            recv(sockfd, buff, 200, 0);
            printf("From Server : %s", buff);
            bzero(buff, sizeof(buff));

        } else if (fds[0].revents & POLLOUT){
            puts(buff);
            strcpy(buff, "HELLO WORLD");
            write(sockfd, buff, sizeof(buff));
            break;
        }
    }
}

Видите разницу, которую делает хорошее форматирование?

При этом знайте, что сокет входит в состояние записи, как только он подключается.Таким образом, ваш код, скорее всего, отправит приветствие клиента, прежде чем ждать приветствия сервера.Если вам нужно дождаться приветствия сервера перед ответом, вам нужно сначала прочитать приветствие, например:

while (true)
{
    if ((rv = poll(fds, 1, 100) > 0))
    {
        if (fds[0].revents & POLLIN)
        {
            rv = recv(sockfd, buff, 200, 0);
            if (rv <= 0) break;
            printf("From Server : %.*s", rv, buff);

            if (/* the complete greeting has been read */) // MAY take multiple reads!
            {
                strcpy(buff, "HELLO WORLD");
                write(sockfd, buff, strlen(buff));
                break;
            }
        }
    }
}

Обратите внимание, что при использовании блокирующих сокетов необязательно обрабатывать POLLOUT.Но если вместо этого вы используете неблокирующие сокеты, вы несете ответственность за проверку, если write() завершается с ошибкой EWOULDBLOCK, и если это так, тогда буферизируйте данные, которые вы пытались отправить, которые не удалось, а также любые последующие данные доСообщается POLLOUT, затем вы можете отправлять буферизованные данные (продолжая обрабатывать EWOULDBLOCK и POLLOUT), пока буфер не будет пуст.Только после этого вы можете отправлять новые данные через сокет, не обрабатывая POLLOUT снова, пока не появится новая ошибка EWOULDBLOCK.Например:

int sendData(int fd, void *data, int len)
{
    char *pdata = (char *) buff;

    if (/* fd's buffer is empty */)
    {
        while (len > 0)
        {
            int rv = send(fd, pdata, len, 0);
            if (rv < 0)
            {
                if (errno != EWOULDBLOCK)
                    return rv;
                break;
            }

            pdata += rv;
            len -= rv;
        }
    }

    if (len > 0)
    {
        // add pdata up to len bytes to fd's buffer...
    }

    return 0;
}

...

while (true)
{
    if ((rv = poll(fds, 1, 100) > 0))
    {
        if (fds[0].revents & POLLIN)
        {
            rv = recv(sockfd, buff, 200, 0);
            if (rv <= 0) break;
            printf("From Server : %.*s", rv, buff);

            if (/* the complete greeting has been read */) // MAY take multiple reads!
            {
                strcpy(buff, "HELLO WORLD");
                sendData(sockfd, buff, strlen(buff));
            }
        }

        if (fds[0].revents & POLLOUT)
        {
            char *pdata = ...; // fd's buffer data
            int len = ...; // fd's buffer length
            int sent = 0;

            while (len > 0)
            {
                rv = send(fd, pdata, len, 0);
                if (rv < 0)
                    break;

                pdata += rv;
                len -= rv;
                sent += rv;
            }

            if (sent > 0)
            {
                // remove sent bytes from fd's buffer...
            }
        }
    }
}

Затем вы можете использовать sendData() в любое время, когда вам нужно отправить какие-либо данные на fd, вместо прямого вызова write() / send().

0 голосов
/ 21 мая 2019

Удалите } перед else if. Затем поместите его после else if -блока.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...