Здравствуйте!
Помогите пожалуйста разобраться с одной проблемкой
Я хочу отправить с клиентского приложения на сервер пакет данных (Сокет уже открыт, готов отправлять). При чём я хочу быть на 100% уверен что данные действительно дошли. Но в MSDN чёрным по белому написано: функция send возвращает количество байт, которые были поставлены в очередь на отправку, и если эта функция возвратила положительное значение, это не значит что данные дошли до адресата...
подскажите пожалуйста, как можно узнать действительно ли пакет был получен сервером, или нет?
30.05.08 11:02: Перенесено модератором из 'C/C++' — Кодт
TCP сам занимается вопросами проверки доставки... Единственное, надо выставлять лингер.
Если очень беспокоит — то пропроитарный протокол или стандартный? Если пропроитарный — добавь сообщени контроля доставки.
Хотя смысла особого не вижу.
Обьясню вам ситуацию: я пишу СОМ объект, который будет общаться по ТСР протоколу с устройством. Устройство является сервером. На каждый мой запрос устройство должно мне дать ответ. Получается что я отправляю данные функцией Send, получаю в ответ количество отправленных данных (точнее отчёт о том, что данные положены в буффер), и тут-же выполняю функцию recv с заданным периодом ожидания ответа (который, в силу некоторых причин очень большой, до 10 минут).
Здесь есть 3 варианта:
1. Данные нормально дошли до адресата, я получил ответ, и все рады
2. Устройство получило пакет, но не дало в заданный таймаут ответ. В данном случае я так и сообщаю вышестоящей логике, и тоже все рады
2. Устройство не получило посылку, и я 10 минут буду просто так отсижываться, ожидая неизвестно чеого..
Вот по этому я и хочу сразу после отправки данных устройству-серверу уточнить, дошли ли они или нет...
Здравствуйте, Edik, Вы писали:
E>Здесь есть 3 варианта: E>1. Данные нормально дошли до адресата, я получил ответ, и все рады E>2. Устройство получило пакет, но не дало в заданный таймаут ответ. В данном случае я так и сообщаю вышестоящей логике, и тоже все рады E>2. Устройство не получило посылку, и я 10 минут буду просто так отсижываться, ожидая неизвестно чеого..
E>Вот по этому я и хочу сразу после отправки данных устройству-серверу уточнить, дошли ли они или нет...
[quote] An acknowledgment by TCP does not guarantee that the data has been
delivered to the end user, but only that the receiving TCP has taken
the responsibility to do so.[/quote]
RFC 793
Здравствуйте, Edik, Вы писали:
E>Вот по этому я и хочу сразу после отправки данных устройству-серверу уточнить, дошли ли они или нет...
А нельзя попросить сервер сразу слать уведомление о вручении, а уже потом 10 минут тормозить?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>А нельзя попросить сервер сразу слать уведомление о вручении, а уже потом 10 минут тормозить?
Дело в том, что сервер на самом деле то отвечает.. Я просмотрел трафик с помощью ТСР снифера — Сразу же после получения севрером данных он возвращает клиенту пакет с ACK-ом. Но я не знаю как его перехватить.. По идее этот пакет должна функция send обрабатывать, и меня извещать о том, что пакет доставлен...
Вот как выглядит обмен данными с сервером на уровне ТСР:
Когда я после команды send выполняю команду recv, я получаю только данные
72 32.782131 10.0.2.48 10.0.2.116 TCP 1001 > 1600 [PSH, ACK] Seq=105 Ack=16 Win=5840 Len=26
Как перехватить пакет — ACK
71 32.773814 10.0.2.48 10.0.2.116 TCP 1001 > 1600 [ACK] Seq=105 Ack=16 Win=5840 Len=0
— убейте не знаю
Здравствуйте, Edik, Вы писали:
E>Здравствуйте, Erop, Вы писали:
E>>А нельзя попросить сервер сразу слать уведомление о вручении, а уже потом 10 минут тормозить?
E>Дело в том, что сервер на самом деле то отвечает.. Я просмотрел трафик с помощью ТСР снифера — Сразу же после получения севрером данных он возвращает клиенту пакет с ACK-ом. Но я не знаю как его перехватить.. По идее этот пакет должна функция send обрабатывать, и меня извещать о том, что пакет доставлен...
E>Вот как выглядит обмен данными с сервером на уровне ТСР:
E>70 32.767340 10.0.2.116 10.0.2.48 TCP 1600 > 1001 [PSH, ACK] Seq=13 Ack=105 Win=65197 [TCP CHECKSUM INCORRECT] Len=3 E>71 32.773814 10.0.2.48 10.0.2.116 TCP 1001 > 1600 [ACK] Seq=105 Ack=16 Win=5840 Len=0 E>72 32.782131 10.0.2.48 10.0.2.116 TCP 1001 > 1600 [PSH, ACK] Seq=105 Ack=16 Win=5840 Len=26 E>73 32.909858 10.0.2.116 10.0.2.48 TCP 1600 > 1001 [ACK] Seq=16 Ack=131 Win=65171 [TCP CHECKSUM INCORRECT] Len=0
E>Когда я после команды send выполняю команду recv, я получаю только данные E>72 32.782131 10.0.2.48 10.0.2.116 TCP 1001 > 1600 [PSH, ACK] Seq=105 Ack=16 Win=5840 Len=26 E>Как перехватить пакет — ACK E>71 32.773814 10.0.2.48 10.0.2.116 TCP 1001 > 1600 [ACK] Seq=105 Ack=16 Win=5840 Len=0 E> — убейте не знаю
Перехватить ACK? Можно.... ROW_SOCKET тебе в руку, но тогда придётся весь TCP реализовать самостоятельно, вот взгляни на диаграмму состояний TCP-протокола и забудь про эту идею.
На самом деле вы идёте не по правильному пути. Вот не получили вы ACK или его аналог в рамках вашего высокоуровневого протокола, что же на самом деле произошло — пациент жив, или он просто тормозит и ещё ничего не отправил, или где-то сетевой затор? Единственный способ придумать ответ — это задать timeout на получение ACK. Именно придумать ответ, потому что однозначно узнать вы его не сможете никогда . А раз снова timeout, то чем тогда кроме значения отличиется timeout на ACK от вашего timeout`а (кстати timeout на получение ACK, после которого соединение считается потерянным при желании может быть и больше 10 минут ). Правильно ничем...
Теперь, как проверить что принимающая сторона не отправила ACK за заданное время? Очень просто: по истечению этого времени операции с сокетом (запись / чтение) будут падать с ошибками типа WSAECONNABORTED, WSAETIMEDOUT, WSAECONNRESET и т.д. Но вы зависите от этого самого системного тайм-аута, его конечно можно подкрутить, подрегулировать, но зачем? Это может негативно сказаться на всём . Поэтому лучше немного усложнить свой протокол следующим образом:
— после приёма сообщения сервер отправляет короткий ответ типа "ПОЛУЧИЛ";
— клиент ждёт это сообщение в течении небольшого промежутка времени и если не дожидается, то считает, что всё пропало, а если дожидается, то начинает ждать указанные 10 минут расширенного ответа;
— сервер после отправки короткого ответа, начинает собирать и готовить данные для большого ответа, собрав отправляет клиенту.
Вот такие пирожки с котятами... И не забывайте, что send и recv блокирующие операции .
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Здравствуйте, Mr. None, Вы писали: MN>И не забывайте, что send и recv блокирующие операции .
send не совсем блокирующая
If no buffer space is available within the transport system to hold the data to be transmitted, send will block unless the socket has been placed in nonblocking mode.
Таким образом, если у системы еще достаточно памяти, что бы записать передаваемые данные send не будет блокироваться и сразу вернет управление.
Здравствуйте, Tujh, Вы писали:
T>Здравствуйте, Mr. None, Вы писали: MN>>И не забывайте, что send и recv блокирующие операции .
T>send не совсем блокирующая T>
If no buffer space is available within the transport system to hold the data to be transmitted, send will block unless the socket has been placed in nonblocking mode.
T>Таким образом, если у системы еще достаточно памяти, что бы записать передаваемые данные send не будет блокироваться и сразу вернет управление.
Если бы да кабы да во рту росли грибы, тогда был бы не рот, а целый огород.
(C) Народ.
Я прекрасно осведомлён, что даже recv может оказаться не блокирующей, если до той поры как вы её вызовете данные поступят. Но гарантировано неблокирующими являются только асинхронные сокеты и сокеты работающие в неблокирующем режиме. Во всех остальных случаях send и recv потенциально блокирующие.
И вообще это было всего лишь напутсвие в стиле культового сериала "Блюз Хилл-Стрит": И будьте по осторожнее... Кто смотрел — тот поймёт...
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Здравствуйте, Edik, Вы писали:
E>Как перехватить пакет — ACK убейте не знаю
Есть один извращенный способ.
Значит так.
1. Нам надо передать N байт. Ставим SO_SNDBUF в N, таким образом размер буфера на отправку становится N байт. Причем весь буфер пуст.
2. Делаем send(sock, data, N). В буфере остается 0 свободных байт, функция send возвращает N.
3. Делаем select на sock по write-сету с некоторым таймаутом. Select вернется когда
а) истечет таймаут — значит ACK не пришел
б) в сокетном отправочном буфере освободится хотя бы один байт. А место там освободится, когда система получит ACK на посланные данные.
Итого: схватить момент получения ACK можно! Хотя и с оговорками:
1. Если сервер будет подтверждать данные не целиком (мало ли чего ему взбредет), то второй проход этого "алгоритма" в течении одного соединения будет некорректным (т.к. нарушится условие из п.1 "весь буфер пуст" — ведь сервер подтвердил тока часть данных, а точную занятость буфера узнать нельзя (только на пусто/не пусто с помощью select из п.3).
2. Скорость передачи данных существенно падает — до 5 сообщений в секунду, т.к. на каждый ACK сервер отдает через 200 мс после приема данных (алгоритм TCP Delayed Ack, в принципе отключаемо, но не везде).
Кроме того, такая извращенная схема позволяет контролировать то, как данные разбиваются на TCP сегменты.
Но, для того чтобы все это использовать, нужно быть очень сильно припертым к стенке тупым заказчиком, который очень хочет чтобы (цитирую) "каждое сообщение отсылалось в отдельном TCP пакете" (поубивал бы!, бред ведь...) или иметь какие либо веские мотивы (хотя я их не представляю).
Все. Я только хотел показать, что оказывается, можно ловить ACK стандартными способами.
Здравствуйте, AlexCrush, Вы писали:
AC>Здравствуйте, Edik, Вы писали:
E>>Как перехватить пакет — ACK убейте не знаю
AC>Есть один извращенный способ.
[skipped]
В общем случае ваш метод не будет работать так как вы описали. SO_SNDBUF задаёт только размер буфера, но не размер TCP фрэйма. Как раз принимающая сторона всегда будет отвечать на один TCP пакет с данными одним ACK ответом. А вот отправляющая сторона совсем не обязательно поместит ваши N байт в один TCP пакет. Так что даже для решения проблемы "каждое сообщение отсылалось в отдельном TCP пакете" этот подход в общем случае не годится...
Говорю "в общем случае" потому, что вам может повести, если размер буфера окажется меньше размера TCP фрэйма.
Что же на самом деле вы словите. А вы словите именно то что должны — select вернёт управление когда send потенциально может быть успешной, например, когда в буфере появится хоть сколько-нибудь места (читайте придёт ACK по крайней мере на один TCP пакет).
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Здравствуйте, Mr. None, Вы писали:
MN>Говорю "в общем случае" потому, что вам может повести, если размер буфера окажется меньше размера TCP фрэйма.
Да, забыл упомянуть это условие в списке "когда оно работает". Отсылаемые посылки должны быть меньше, чем размер TCP сегмента (т.е. около 1500 байт).
А вообще, в моем случае (небольшие сообщения, максимум 60 байт) метод работает. О как бы я хотел чтобы "с той стороны" перестали выдвигать дурацкое требование "каждое ваше сообщение — в отдельном TCP пакете" (дословно). Но — увы, госструктуры тупы и непробиваемы.
Здравствуйте, AlexCrush, Вы писали:
AC>Здравствуйте, Mr. None, Вы писали:
AC>А вообще, в моем случае (небольшие сообщения, максимум 60 байт) метод работает. О как бы я хотел чтобы "с той стороны" перестали выдвигать дурацкое требование "каждое ваше сообщение — в отдельном TCP пакете" (дословно). Но — увы, госструктуры тупы и непробиваемы.
Бред: любой брандмауэр или прокси со сборкой — разборкой пакетов отправит лесом все ваши усилия...
Если заказчик выдвигает вам глупые или не выполнимые требования — виноваты вы, а не заказчик. Потому что не смогли ему объяснить что это невозможно, не эффективно и так далее.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Здравствуйте, Mr. None, Вы писали:
MN>Здравствуйте, AlexCrush, Вы писали:
MN>Бред: любой брандмауэр или прокси со сборкой — разборкой пакетов отправит лесом все ваши усилия.
Это понятно. MN>Если заказчик выдвигает вам глупые или не выполнимые требования — виноваты вы, а не заказчик. Потому что не смогли ему объяснить что это невозможно, не эффективно и так далее.
Увы, в нашем случае это практически политическая проблема.
Заказчик технически понимает, что хочет бред (хотя и неофициально). Этот самый бред они хотят потому как у них есть задорого купленный сервер, который писали какие-то идиоты, видимо считающие что ф-ия recv всегда возвращает один пакет.
Мы — клиенты этого горе-сервера. Мы вполне можем отказаться с ними работать, но потеряем некоторый лакомый кусочек рынка. Заказчик (напомню, это госструктура) требует чтобы мы с сервером работали. Причем, это наши проблемы — если мы с ним работать не будем, то сертификат не получим и идем домой. Они вообще-то понимают, что косяк в их сервере, но попросить авторов поправить почему-то не могут/не хотят (или денег жалко). Вместо этого они выставляют бредовые требования нам.
Причем, понятно, что вполне может получиться так, что даже получая пакеты, содержащие ровно по одному сообщению, сервер прочитает их recv-ом по частям. Вот только проблема в том, что теперь требование "один пакет — одно сообщение" включено в официальные требования на протокол передачи данных (который поверх TCP ). Не ту страну назвали гондурасом....
Намеренно не называю предметную область, чтобы не пугать никого. ЕГАИС по сравнению с этим — детские шалости.
Так что не все так однозначно. Никому такой задницы не пожелаю. Кроме того, я просто хотел поделиться способом В СПЕЦИФИЧЕСКИХ УСЛОВИЯХ контролировать исходящие TCP сегменты и их ACK-и. Зачем и почему это все нужно — другой вопрос.