Отключить и повторно подключить подключенный сокет дейтаграммы

Я пытаюсь создать итеративный сервер на основе сокетов датаграмм (UDP). Он вызывает соединение с первым клиентом, которое он получает от первого вызова recvfrom () (да, я знаю, что это не настоящее соединение). После обслуживания этого клиента я отключаю сокет UDP (вызывая соединение с 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
}

РЕДАКТИРОВАТЬ: я обнаружил ошибку в моем клиенте, случайно отправившем пустой пакет. Теперь моя проблема в том, как заставить клиента ждать обслуживания вместо отправки запроса в никуда (сервер подключен к другому клиенту и еще не обслуживает других клиентов).


person codymanix    schedule 26.05.2009    source источник


Ответы (3)


connect () действительно совершенно не нужен для SOCK_DGRAM.

Вызов connect не останавливает ни получение пакетов от других хостов, ни их отправку. Только не беспокойтесь, это бесполезно.

ИСПРАВЛЕНИЕ: да, очевидно, это мешает вам получать пакеты от других хостов. Но делать это на сервере немного глупо, потому что любые другие клиенты будут заблокированы, пока вы подключены () к одному. Также вам все равно нужно будет ловить плывущую мякину. Вероятно, существуют некоторые состояния гонки, связанные с connect () на сокете DGRAM - что произойдет, если вы вызовете connect, а пакеты с других хостов уже находятся в буфере?

Кроме того, 0 является допустимым значением, возвращаемым recvfrom (), поскольку пустые (без данных) пакеты действительны и могут существовать (действительно, люди часто их используют). Так что вы не можете проверить, удалось ли что-то таким образом.

По всей видимости, в очереди уже был пакет с нулевым байтом.

Ваш протокол должен быть спроектирован так, чтобы свести к минимуму вероятность неверной интерпретации ошибочной дейтаграммы; по этой причине я бы посоветовал вам не использовать пустые датаграммы, а вместо этого использовать магическое число.

Приложения UDP ДОЛЖНЫ быть способны распознавать "болтовни" пакеты и отбрасывать их; они рано или поздно появятся.

person MarkR    schedule 26.05.2009
comment
Connect устанавливает внутренний фильтр, с которого адрес получен и отправлен при использовании send () или recv (). recv (): для сокетов SOCK_DGRAM данные извлекаются из первой поставленной в очередь дейтаграммы для адреса назначения, указанного в вызове соединения. - person codymanix; 26.05.2009
comment
Вы правы, мой клиент отправлял (из-за ошибки) пустой пакет. Теперь у меня другая проблема. Клиент, который должен ждать в очереди, чтобы его обслужили, отправляет свой первый пакет запроса (который никогда не поступает от сервера). Затем он пытается получить ответ, и recv возвращает -1. Как заставить клиента ждать? - person codymanix; 27.05.2009
comment
Вы все еще сталкиваетесь с этой проблемой (часть редактирования вашего сообщения) или она решена путем удаления соединения. - person Aditya Sehgal; 01.07.2009
comment
-1: соединение с UDP часто бывает очень полезным. Если вы не подключитесь, вы не получите никаких ответов ICMP. Если вы не подключаетесь и не используете sendto, ваши пакеты уходят в пустоту, и вы никогда ничего не слышите, если только одноранговый узел не получил пакет и не отправил ответ, который прошел. Но если вы подключаетесь и возникает проблема с IP-адресом, ОС, вероятно, получила объяснение ICMP и вернет его вам только в том случае, если вы подключены. - person Nicholas Wilson; 31.05.2013
comment
@NicholasWilson connect () не очень полезен на сервере, который хочет обслуживать несколько клиентов. Это довольно полезно в клиентском приложении, которое хочет общаться только с одной стороной. - person MarkR; 03.06.2013

Просто поправка на тот случай, если кто-то наткнется на это, как я. Для отключения connect () необходимо вызвать с параметром sa_family sockaddr, установленным на AF_UNSPEC. Не раз прошел AF_UNSPEC.

person badi95    schedule 01.06.2015

person    schedule
comment
Ну и что? Как это отвечает на мой вопрос? - person codymanix; 26.05.2009
comment
В чем конкретно был вопрос? - person Nikolai Fetissov; 26.05.2009
comment
Извините, если я не понял. Вопрос был в том, почему второй вызов recvfrom () возвращает 0. Теперь я обнаружил, что это ошибка в моем коде. Моя проблема сейчас в том, как заставить клиента ждать, пока его обслужат, вместо того, чтобы позволить ему отправлять данные в никуда. - person codymanix; 27.05.2009