Отключите и повторно подключите подключенный сокет дейтаграммы
-
06-09-2019 - |
Вопрос
Я пытаюсь создать итеративный сервер на основе сокетов дейтаграмм (UDP).Он вызывает connect к первому клиенту, который он получает от первого вызова recvfrom() (да, я знаю, что это не настоящее connect).После обслуживания этого клиента я отключаю сокет UDP (вызываю connect с помощью AF_UNSPEC) Затем я вызываю recvfrom(), чтобы получить первый пакет от следующего клиента.
Теперь проблема в том, что вызов recvfrom() на второй итерации цикла возвращает 0.Мои клиенты никогда не отправляют пустые пакеты, так что же могло бы происходить.
Это то, что я делаю (псевдокод):
s = socket(PF_INET, SOCK_DGRAM, 0)
bind(s)
for(;;)
{
recvfrom(s, header, &client_address) // get first packet from client
connect(s,client_address) // connect to this client
serve_client(s);
connect(s, AF_UNSPEC); // disconnect, ready to serve next client
}
Редактировать:Я обнаружил ошибку в том, что мой клиент случайно отправил пустой пакет.Теперь моя проблема заключается в том, как заставить клиента ждать обслуживания вместо того, чтобы отправлять запрос в никуда (сервер подключен к другому клиенту и пока не обслуживает ни одного другого клиента).
Решение
connect() действительно совершенно не нужен в SOCK_DGRAM.
Вызывающий коннект не мешает вам получать пакеты от других хостов, и это не мешает вам отправлять их.Просто не беспокойтесь, на самом деле это не очень помогает.
ИСПРАВЛЕНИЕ:да, по-видимому, это действительно мешает вам получать пакеты от других хостов.Но делать это на сервере немного глупо, потому что любые другие клиенты были бы заблокированы, пока вы подключались () к одному из них.Кроме того, вам все равно нужно будет ловить "мякину", которая плавает вокруг.Вероятно, существуют некоторые условия гонки, связанные с connect() в сокете DGRAM - что произойдет, если вы вызовете connect, а пакеты с других хостов уже находятся в буфере?
Кроме того, 0 является допустимым возвращаемым значением из recvfrom(), поскольку пустые пакеты (без данных) допустимы и могут существовать (действительно, люди часто ими пользуются).Таким образом, вы не можете проверить, удалось ли что-то таким образом.
По всей вероятности, пакет с нулевым байтом уже находился в очереди.
Ваш протокол должен быть спроектирован таким образом, чтобы свести к минимуму вероятность неправильного толкования ошибочной дейтаграммы;по этой причине я бы посоветовал вам не использовать пустые дейтаграммы, а вместо этого использовать магическое число.
UDP-приложения ДОЛЖНЫ быть способны распознавать пакеты "мякины" и отбрасывать их;рано или поздно они появятся.
Другие советы
man connect
:
... If the initiating socket is not connection-mode, then connect() shall set the socket’s peer address, and no connection is made. For SOCK_DGRAM sockets, the peer address identifies where all datagrams are sent on subsequent send() functions, and limits the remote sender for subsequent recv() functions. If address is a null address for the protocol, the socket’s peer address shall be reset. ...
Просто поправка на случай, если кто-нибудь наткнется на это, как я.Для отключения необходимо вызвать функцию connect(), для члена sa_family sockaddr которой задано значение AF_UNSPEC.Не просто прошел AF_UNSPEC.