Приложение периодически запрашивает сервер: отправляет udp пакет запроса и ждет ответ. Ответ ожидается в течение определенного тайм-аута.
Возможна такая ситуация:
— приложение посылает запрос R1,
— сервер отвечает A1. Но датаграмма A1 где-то задерживается и приложение не дожидается ее.
На очередном витке цикла приложение пошлет запрос R2 (на который сервер ответит A2), но когда начнет читать ответ сервера, сначала будет получена датаграмма A1, которая не была прочитана в прошлый раз (и буферизировалась где-то в tcp/ip стеке), и только потом A2.
Скажите, на сколько корректно для отсева таких опоздавших датаграмм открывать/закрывать клиентский сокет на каждом витке цикла опроса? Нет ли тут каких подводных камней? (попробовал -- работает да и задержки по времени на открытие/закрытие сокета нет... как-то подозрительно просто )
Здравствуйте, idontsov, Вы писали:
I>Скажите, на сколько корректно для отсева таких опоздавших датаграмм открывать/закрывать клиентский сокет на каждом витке цикла опроса? Нет ли тут каких подводных камней? (попробовал -- работает да и задержки по времени на открытие/закрытие сокета нет... как-то подозрительно просто )
А вы не боитесь, закрыв сокет, потерять уже полученный ответ A2 вместе с мусорными данными? Помоему такая ситуация возможна. Лучше уж отсеивать после прочтения данных.
Здравствуйте, idontsov, Вы писали:
I>Приложение периодически запрашивает сервер: отправляет udp пакет запроса и ждет ответ. Ответ ожидается в течение определенного тайм-аута...
butcher есть еще опасность получить A1 сразу после нового открытия сокета. ИМХО для решения Вашей задачи надо в пакете передавать еще и некоторый идентификатор, по которому клиент определит актуальность ответа: клиент генерирует некоторое значение и шлет его на сервер, сервер в свой ответ включает значение из запроса... Ну, в общем, понятно.
ЗЫ. А вообще есть подозрение, что архитектура хромает...
I>>Скажите, на сколько корректно для отсева таких опоздавших датаграмм открывать/закрывать клиентский сокет на каждом витке цикла опроса? Нет ли тут каких подводных камней? (попробовал -- работает да и задержки по времени на открытие/закрытие сокета нет... как-то подозрительно просто ) B>А вы не боитесь, закрыв сокет, потерять уже полученный ответ A2 вместе с мусорными данными? Помоему такая ситуация возможна. Лучше уж отсеивать после прочтения данных.
Это как так?
// псевдокод
while (true)
{
Socket s = new Socket(recvTimeout);
s.Connect(epServer);
s.Send(req);
s.Recv(ans);
s.Close();
}
Сокет закрывается после того, как произведено чтение из него. Поэтому если A2 получен в срок, он был вычитан. Если нет -- отброшен. Или имеется ввиду возможность получить "мусорную" датаграмму перед ответом A2?
Здравствуйте, idontsov, Вы писали:
B>>А вы не боитесь, закрыв сокет, потерять уже полученный ответ A2 вместе с мусорными данными? Помоему такая ситуация возможна. Лучше уж отсеивать после прочтения данных.
I>Это как так? I>
странный какой-то псевдокод.. Connect, Recv..
Для начала, не могли бы вы привести более приближённый к действительности псевдо-код? Сокеты блокирующие или нет?
I>Сокет закрывается после того, как произведено чтение из него. Поэтому если A2 получен в срок, он был вычитан. Если нет -- отброшен. Или имеется ввиду возможность получить "мусорную" датаграмму перед ответом A2?
А смысл всего этого? Для чего постоянно создавать/закрывать сокет? (Если я, плохой дядя, возьму и сгенерирую миллион датаграм на адрес и порт вашей программы, это ж какой оверхед получится? Создание/привязка/чтение/закрытие, когда возможно только чтение, ну это так, отвлекся
B>Для начала, не могли бы вы привести более приближённый к действительности псевдо-код? Сокеты блокирующие или нет?
Хорошо, пример ниже. Использую блокирующие сокеты.
class DummyClient
{
private const int BUFF_SIZE = 1024;
private byte[] _buff = new byte[BUFF_SIZE];
// метод периодически вызывается (по таймеру, например).private void onTick()
{
queryServer();
}
private void queryServer()
{
// создаем новый блокирующий udp сокет
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
try
{
// выставляем тайм-аут на чтение из сокета
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 500);
// "соединяем" сокет с server end point
// Any datagrams that arrive from an address other than the specified default will be discarded.
socket.Connect(_epServer);
// формируем пакет запроса и отправляем его на серверbyte[] req = new byte[REQ_LEN];
// ...
// пакет отсылается серверуint sent = socket.Send(req);
// читаем из сокета. если в течение time-out'а не прочитаем, будет выброшено SocketException с кодом time out.int recv = socket.Receive(_buff);
// сюда попали, если что-то прочитали. обрабатываем полученный пакет.
// ...
}
finally
{
// закрываем сокет. если я правильно понимаю, после того, как сокет будет закрыт, все несчитанные полученные
// датаграммы будут выброшены а новые датаграммы на этот сокет отправить уже никто не сможет.
socket.Close();
}
}
}
B>А смысл всего этого? Для чего постоянно создавать/закрывать сокет?
Таким образом я хочу "чистить" из буфера tcp/ip стека те датаграммы, которые не были считаны клиентом из-за того, что пришли позже, чем истек тайм-аут ожидания ответа (но которые все-таки пришли и ждут, когда очередной socket.Receive() вычитает их).
Это очень дико?
Просто я в этом ничего плохого не вижу. Зачем городить идентификацию пакетов, когда можно пересоздавать сокет?
B>(Если я, плохой дядя, возьму и сгенерирую миллион датаграм на адрес и порт вашей программы, это ж какой оверхед получится? B> Создание/привязка/чтение/закрытие, когда возможно только чтение, ну это так, отвлекся
Это злонамеренное вмешательство, я не рассматриваю такую ситуацию. Да и зла особого не будет -- просто клиент вместо ответа сервера заберет один пакет "мусора". Т.е. связь с сервером будет нарушена, но никакого оверхеда не будет.
Здравствуйте, idontsov, Вы писали:
B>>Для начала, не могли бы вы привести более приближённый к действительности псевдо-код? Сокеты блокирующие или нет? I>Хорошо, пример ниже. Использую блокирующие сокеты.
Если это будет работать действительно так как описано в комментариях, то я думаю единственным неудобством будет то что всётаки возможно получение датаграммы, которую так не хочется получать, на следующей итерации цикла.
I>Это очень дико?
Ну как сказать, если уж вы пишете на c#, то проблемы с лишним вызовом пары функция думаю вас не волнуют
I>Это злонамеренное вмешательство, я не рассматриваю такую ситуацию. Да и зла особого не будет -- просто клиент вместо ответа сервера заберет один пакет "мусора". Т.е. связь с сервером будет нарушена, но никакого оверхеда не будет.
Ну это зависит от того как вы реализуете своё приложение и какие пакеты я буду посылать
B>Если это будет работать действительно так как описано в комментариях, то я думаю единственным неудобством будет то что всётаки возможно получение датаграммы, которую так не хочется получать, на следующей итерации цикла.
Не понимаю Откуда на следующей итерации цикла она возьмется? Разве закрытие сокета не гарантирует, что tcp/ip стек удалит все накопившиеся датаграммы и не будет принимать новые для данного ip/port?
Здравствуйте, idontsov, Вы писали:
I>Не понимаю Откуда на следующей итерации цикла она возьмется? Разве закрытие сокета не гарантирует, что tcp/ip стек удалит все накопившиеся датаграммы и не будет принимать новые для данного ip/port?
Ну если конструкция
socket.Connect(_epServer);
гарантирует, что следующий сокет будет связан с новым портом, и сервер отвечает клиенту на тот же порт, с которого датаграмма пришла, то в принципе вероятность получения датаграммы будет ничтожна, но она всё же есть
I> // "соединяем" сокет с server end point
I> // Any datagrams that arrive from an address other than the specified default will be discarded.
I> socket.Connect(_epServer);
I>
I>> // "соединяем" сокет с server end point
I>> // Any datagrams that arrive from an address other than the specified default will be discarded.
I>> socket.Connect(_epServer);
I>>
MC>Это вместо Bind так теперь модно делать?
На сколько я понимаю, у Socket.Bind и Socket.Connect разные назначения. Bind ассоциирует сокет с локальным end point (ip/port). Connect соединяет с удаленным сокетом.
Например, если бы мне необходимо было, чтобы сокет клиента имел конкретный адрес/порт, можно было бы сделать так:
Здравствуйте, idontsov, Вы писали:
I>На сколько я понимаю, у Socket.Bind и Socket.Connect разные назначения. Bind ассоциирует сокет с локальным end point (ip/port). Connect соединяет с удаленным сокетом.
Так вот и я о чем. А теперь смотрим на заголовок и видим ключевую аббревиатуру UDP. Ткните меня носом, что такое "соединение" в UDP.
Здравствуйте, idontsov, Вы писали:
I>На сколько я понимаю, у Socket.Bind и Socket.Connect разные назначения. Bind ассоциирует сокет с локальным end point (ip/port). Connect соединяет с удаленным сокетом.
А, я понял. Вас совершенно не интересует, на какой порт сядет клиент (сервер и так об этом узнает), а connect используется только для того чтобы отсеять все пакеты, идущие не от данного сервера.
Ок, проехали. Это не имеет отношения к топику.
можно организовать отсеивание запоздавших датаграмм, присваивая каждой из них ID. т.е. уходит датаграмма-запрос с ID и должна прийти датаграмма ответ с тем же ID. Если в ответ приходит датаграма с другим ID — она явно запоздалая и в ней мы не нуждаемся. пропускаем её и продолжаем ждать датаграмму с нужным ID.
, что если порт новый каждый раз будет -- то все ок. Но это ясно. Хочу на досуге попробовать переоткрывать сокет на том же порту и смотреть, будут ли его ждать датаграммы "из прошлой жизни".
, что если порт новый каждый раз будет -- то все ок. Но это ясно. Хочу на досуге попробовать переоткрывать сокет на том же порту и смотреть, будут ли его ждать датаграммы "из прошлой жизни".
Если они придут после того, как сокет закроется и до того, как новый сокет откроется то они просто отбросятся, но возможен вариант, когда старая датаграмма придёт уже на новый сокет.
, что если порт новый каждый раз будет -- то все ок. Но это ясно. Хочу на досуге попробовать переоткрывать сокет на том же порту и смотреть, будут ли его ждать датаграммы "из прошлой жизни".
Нет, не будут. Они умрут вместе с убитым сокетом (с его буффером). Но если сервер отправит пакет в промежутке времени между закрытием предыдущего сокета и получением запроса от следующего, то (находящийся на том же порту сокет) этот пакет получит. Сначала в буфер, а потом Вы его прочтете.
Я одного не понимаю. ЗАЧЕМ Вы так упорно хотите сделать это таким... хмм... нетрадиционным путем?
B>Если они придут после того, как сокет закроется и до того, как новый сокет откроется то они просто отбросятся, но возможен вариант, когда старая датаграмма придёт уже на новый сокет.
Дошло до меня, наконец Да, пожалуй, возможно. Хотя вероятность и правда весьма мала -- порт-то каждый раз будет новый.
Hello, idontsov!
You wrote in conference rsdn.network on Thu, 18 Nov 2004 20:49:34 GMT:
i> Приложение периодически запрашивает сервер: отправляет udp пакет запроса i> и ждет ответ. Ответ ожидается в течение определенного тайм-аута.
UDP — НЕНАДЕЖНЫЙ протокол. Поэтому он может:
a) терять датаграммы
б) менять их местами т.е. A2 может придти раньше A1 и тд.
i> Скажите, на сколько корректно для отсева таких опоздавших датаграмм i> открывать/закрывать клиентский сокет на каждом витке цикла опроса?
Бесполезно. UDP сокет не имеет понятия "соединение".
Например, если твои датаграммы передаются "голубиной почтой" и
придут через полгода, ты их словишь и на новый сокет.
Единственное, что можно посоветовать, это в вставлять пакеты запросов и ответов "метку транзакции". То есть например в R1 (и соответственно в A1), метка будет "1", в R2 и A2 метка будет "2" и т.д.
Posted via RSDN NNTP Server 1.9 gamma
__________
16.There is no cause so right that one cannot find a fool following it.
DEA>UDP — НЕНАДЕЖНЫЙ протокол. Поэтому он может: DEA>б) менять их местами т.е. A2 может придти раньше A1 и тд.
ТЦП тоже такое умеет. Другое дело, что при сборке потока пакеты, не влезающие в окно, будут выброшены. Поэтому, кстати, приходится при мультироутинге пускать все ТЦП-соединение по одному маршруту. Иначе из-за неочередности прихода пакетов будет слишком большой оверхед по траффику. Это чисто админский взгляд на жизнь Но программерам нужно его учитывать