Обработка коротких чтений с использованием epoll () - PullRequest
1 голос
/ 24 июня 2011

Допустим, клиент отправил 100 байтов данных, но каким-то образом сервер получил только 90 байтов.Как мне справиться с этим делом?Если сервер вызывает функцию «чтения» внутри цикла while, проверяя общее количество полученных данных, то сервер будет ждать последние 10 байтов пакета.

Также может случиться, что клиент будет отключен в серединеОбмен данными.В этом случае сервер также будет ждать вечно, пока не получит все данные, которые не будут получены.

Я использую tcp, но в реальной сетевой среде такая ситуация может возникнуть.Заранее спасибо ...

Ответы [ 4 ]

1 голос
/ 24 июня 2011

Вы не вызываете функцию read() в цикле, пока не получите необходимое количество байтов.Вместо этого вы устанавливаете сокет в неблокирующее состояние и вызываете функцию read() в цикле, пока она не вернет 0 (указывая на конец потока) или ошибку.

В обычном случае цикл завершится на read() возвращает -1, для errno установлено значение EAGAIN.Это указывает на то, что соединение не было закрыто, но в настоящее время больше нет доступных данных.На этом этапе, если у вас еще недостаточно данных от клиента, вы просто сохраняете данные, которые вы делаете , на потом и возвращаетесь в основной цикл epoll().

Если и когда прибудет остаток данных, сокет будет возвращен как читаемый epoll(), вы read() остальных данных извлечете, восстановите сохраненные данные и обработаете их все.

Это означает, чточто вам необходимо пространство в структуре данных для каждого сокета для хранения данных, которые читаются, но еще не обработаны.

1 голос
/ 24 июня 2011

Вы должны тщательно проверить возвращаемое значение read.Он может вернуть любую из трех вещей:

Положительное число, указывающее, что некоторые байты были прочитаны.

Ноль, указывающее, что другой конец изящно закрыл соединение.

-1, что означает, что произошла ошибка.(Если сокет неблокирующий, то ошибка EAGAIN или EWOULDBLOCK означает, что соединение все еще открыто, но данные для вас сейчас не готовы, поэтому вам нужно подождать, пока epoll сообщит, что для вас есть больше данных.)

Если ваш код не проверяет каждую из этих трех вещей и обрабатывает их по-разному, то он почти наверняка не работает.

Они охватывают все случаи, о которых вы спрашиваете, например, отправка клиентом90 байтов, затем закрытие или грубое разрыв соединения (потому что read () вернет 0 или -1 для этих случаев).

Если вы беспокоитесь, что клиент может отправить 90 байтов, а затем никогда больше не отправлять, иНикогда не закрывайте соединение, тогда вы должны установить свои собственные тайм-ауты.Для этого лучше всего ставить неблокирующие сокеты и устанавливать таймаут на select () / poll () / epoll (), отключая соединение, если оно простаивает слишком долго.

0 голосов
/ 20 июля 2011

В дополнение к тому, что сказал caf, я бы порекомендовал просто подписаться на EPOLLRDHUP, потому что это единственный безопасный способ выяснить, было ли соединение закрыто (read () == 0 ненадежно, так как caf упомянул об этом тоже, может быть правдой в случае ошибки). EPOLLERR подписан на всегда , даже если вы его специально не просили. Правильное поведение - закрыть соединение с помощью close () в случае EPOLLRDHUP и, возможно, даже когда EPOLLERR установлен.

Для получения дополнительной информации я дал аналогичный ответ здесь: epoll_wait () получает закрытый сокет дважды (read () / recv () возвращает 0)

0 голосов
/ 24 июня 2011

TCP-соединение является двунаправленным потоком, расположенным поверх сети на основе пакетов. Это обычное явление - читать только часть того, что отправила другая сторона. Вы должны читать в цикле, добавляя, пока не получите полное сообщение. Для этого вам нужен протокол прикладного уровня - типы, структура и семантика сообщений - которые вы используете поверх TCP (такие протоколы - FTP, HTTP, SMTP и т. Д.).

Чтобы ответить на конкретную вторую часть вопроса - добавьте EPOLLRDHUP к набору событий epoll(7), чтобы получать уведомления при разрыве соединения.

...