LINUX.ORG.RU

Сокеты: блокирующийся вызов write, или как дождаться ACK


0

0

Пишу клиент-серверный проект для организации торговли.

Возникла проблема: юзаю select() для установки таймаута, с read()/recv() всё шикарно. Немного хуже с write()/send(). Мне надо точно знать, дошёл ли мой ответ до клиента. Я использую TCP, т.е. мне как-то надо заставить дождаться возвращения пакета ACK функцию write() и только потом продолжить выполнение программы.

Пример: открыли соединение, всё шикарно. обениваемся данными. В один прекрасный момент кто-то выдёргивает сетевой кабель, а клиент, допустим, отправляет серверу сообщение о том, что платёж проведён. Что будет: write() скопирует данные из моего буфера в системный и потом пойдёт дальше. Но данные то не дойдут (если, к примеру, кабель врубят через день), а приложение об этом не узнает. Так вот, надо бы, чтоб программа продолжила выполнение только после приёма ACK-пакета.

Понимаю, что можно потом самому послать TCP-пакет с подтверждением, но что-то это некрасиво. Не зря же TCP существует) Transmission Control Protocol, как никак)

Подскажите, плз, если ли выход? Книжка Стивенса чёт надежды не даёт(

Re: Сокеты: блокирующийся вызов write, или как дождаться ACK

Отправлять явный ответ самому. API к TCP ожидания ACK'ов не поддерживает, это в RFC заложено. Ну и поботать TCP ещё лучше, ещё подробнее, если действительно интересует вопрос.

anonymous ()

Re: Сокеты: блокирующийся вызов write, или как дождаться ACK

везде так будет. Обратись к первоисточнику (вот тут с переводом: http://ts.psc.ru/rfc/rfc_793.html), пункт 3.8 "Functional specification / Interfaces", абзацы про команду "SEND".

anonymous ()

Re: Сокеты: блокирующийся вызов write, или как дождаться ACK

In the simplest implementation, SEND would not return control to
the sending process until either the transmission was complete or the timeout had been exceeded.

Вот что тут понимается под "transmission was complete"? Передача в буфер, или полная передача и доставка ACK'а.=//

dimaz-z ()

Re: Сокеты: блокирующийся вызов write, или как дождаться ACK

там же ясно описана элементарная ситуация, из-за которой нежелательно ждать ack'ов:

Дано: две стороны - однопоточные проги - обмениваются данными, делают send/receive в некотором порядке. Допустим, окна передачи вмещают только один пакет. Тогда если обе стороны ждут ack'и и одновременно делают send(), то произойдёт взаимоблокировка, т к ack'и не будут отправлены, пока данные не будут прочитаны через recv(). Получается, что ждать ack'и крайне нежелательно.

Дальше смотрим API BSD-сокетов и понимаем, что в этих рамках поведение с немедленным возвратом безальтернативно.

anonymous ()

Re: Сокеты: блокирующийся вызов write, или как дождаться ACK

да, и самое главное - ожидание ack'ов всё равно не гарантирует доставку, потому что, к примеру, на принимающей стороне питание может обрубиться во время возврата из системного вызова recv(), и к этому моменту ack может уже уйти на провод. То есть ack ушёл, а приложение данные так и не прочитает.

anonymous ()

Re: Сокеты: блокирующийся вызов write, или как дождаться ACK

выход есть - не трогать тсp а использовать поверх него свой протокол и подтверждать не получение а завершение бизнес транзакции

anonymous ()

Re: Сокеты: блокирующийся вызов write, или как дождаться ACK

Как уже тут написали, SYN, SYN/ACK и FIN пакеты недоступны для TCP. Сокет ведь создавался как socket(PF_INET,SOCK_STREAM,6), а не socket(AF_INET,SOCK_RAW,6),верно? Да и не нужно настолько глубоко копать - придётся контролировать формирование TCP/IP-заголовков из приложения, контролировать порядок принятых пакетов, проверять/формировать контрольную сумму...

В данном случае наиболее простым вариантом будет примерно такая схема:
1) посылается пакет
2) Назначается таймаут на приём данных (минута, например)
3) Принимается ответный пакет (пусть это будет банальных три байта "ok\n", которые ддругая сторона обязана послать через обычный write())
4) Если ответа нет (выход по таймауту), то переход на пункт 1 данного алгоритма
5) здесь продолжаем работу при получении положительного ответа (который может придти хоть через неделю).

Всё довольно просто, я даже не представляю, что могло вызвать затруднение...

Работа самого TCP для данного приложения абсолютно прозрачна и никоем образом не должна волновать разработчика :) Разработчик просто должен твёрдо знать, что все данные придут именно в том порядке, в котором они были отправлены и именно в том виде, в каком они были отправлены. TCP имеет свои механизмы (ip_sum, tcp_seq, tcp_sum), но тебя они не касаются :) Ты должен сам реализовать "запрос-отклик".

Slavaz ★★★★★ ()

Re: Сокеты: блокирующийся вызов write, или как дождаться ACK

Всё, ребят, понял... Сам читал довольно подробно про интерфейс BSD-сокетов и его реализацию, ничё там про то, что мне надо было не нашёл, вот и решил спросить))


Пасиба, буду делать свой протокольчик поверх)

dimaz-z ()

Re: Сокеты: блокирующийся вызов write, или как дождаться ACK

> Пасиба, буду делать свой протокольчик поверх)

может, тебе подойдёт какая-нить шина сообщений? Их очень много. Если не лень, посмотри google protocol buffers и скажи мне как оно и умеет ли подтверждать доставку сообщения :). А то у меня всё никак руки не дойдут посмотреть на этого зверя...

true_admin ★★★★★ ()

Re: Сокеты: блокирующийся вызов write, или как дождаться ACK

если тебе нужна гарантия *отправки* (куда-то - в сеть, в tcp или ещё куда - всё равно точно не узнаешь), используй send, как раньше. Если нужна гарантия *доставки*, используй явные подтверждения на уровне своего клиентского протокола (как уже написали, это не есть функция TCP).

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

PS. TCP - TRANSMISSION control protocol, а не transaction control protocol ;-) Предназначен лишь для управления *передачей*, т.е. обеспечивает только упорядоченность доставляемых данных (упорядоченный поток байт) и правильное использование ширины канала.

alexsaa ()

Re: Сокеты: блокирующийся вызов write, или как дождаться ACK

может, тогда лучше отсылать данные по UDP?
а потом досылать (в случае обрыва и пр.),
как это реализовано в UFTP http://www.tcnj.edu/~bush/uftp.html

Valeriy_Onuchin ★★ ()

Re: Сокеты: блокирующийся вызов write, или как дождаться ACK

ИМХО если речь идёт про экономическое приложение, которое может работать с финансовой информацией, то абсолютно необходимо отдельно посылать подтверждение о выполнении транзакции, желательно ещё с контрольной суммой информации о транзакции (так, чтобы другая сторона могу проверить, что выполнена именно нужная ей транзакция).

> Понимаю, что можно потом самому послать TCP-пакет с подтверждением, но что-то это некрасиво. Не зря же TCP существует) Transmission Control Protocol, как никак)

Издержки на дополнительные пакеты не идут ни в какое сравнение с проблемами, в случае если питание оборвётся именно когда будет проводиться важный платёж.

anonymous ()

Re: Сокеты: блокирующийся вызов write, или как дождаться ACK

этим ты берёшь на себя неслабую часть задач TCP. Зачем? Здесь мультикаст не нужен.

// угадал капчу со 2-го раза

anonymous ()

Re: Сокеты: блокирующийся вызов write, или как дождаться ACK

> // угадал капчу со 2-го раза

кто мы?
капча взята после пьянки в Нёшателе, возможно, не похож (или наоборот :)

> Зачем?

просто UDP легче & лёгче

Valeriy_Onuchin ★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.