[Moscow.pm] Perl Sockets: определить флаги tcp

Anton Yuzhaninov citrin на citrin.ru
Чт Июл 4 05:02:06 PDT 2013


On 06/28/13 16:26, Геннадий Евгеньевич wrote:
> Есть приложение типа cliet-server, между ними бегают туда-сюда пакеты по
> протоколу SMPP, в ходе работы возникает ситуация, когда серверная сторона шлет
> tcp с флагами FIN+ACK, но т.к. я работаю с SMPP/perlsockets на уровень выше, я
> об этом не знаю, и пока сокет не закроется, моя клиентская часть по прежнему
> продолжает слать SMPP пакеты, что является не правильно.

1. А почему собственно не правильно? В tcp есть возможность закрыть соединение 
на-половину (half-close). Т. е. просто отправка сервером fin+ack означает, что 
сервер не будет больше передавать данные, но при этом может их принимать.

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

В случае какого то сбоя (падения канала, процесса на удаленной стороне и т. п.) 
следующий wirte() вернёт ошибку, и надо будет закрыть соединение и попытаться 
переключиться.

2. Вопреки распространенному заблуждения про TCP, если syscall write() отработал 
без ошибки, это еще не означает что все данные получены приложением на удаленной 
стороне. Единственно что это означает, что ядро ОС приняло данные от приложение 
(поместил их в свой буфер) и дальше будет пытаться их отправить до тех пор, пока 
на истечет retransmit timeout или от удаленной стороны не придет RST. Причин по 
которым данные могут потеряться после того, как write отработал без ошибки может 
было множество: где то посередине между хостами сломался канал на длительное 
время, удаленный сервер внезапно перезагрузился или завис, процесс на удаленном 
сервер умер по сигналу...

И заблуждение это настолько распространенное, что так думают не только рядовые 
кодеры, но и некоторые разработчики протоколов (например в базовом стандарте 
XMPP таких подтверждений не предусмотрено, и они появились в одном из XEP, и 
поддерживаются AFAIK не везде).

Т. е. если нужна гарантированная доставка сообщений, то должно быть 
подтверждение на уровне прикладного протокола.

Пример того как это сделано в SMTP - клиент ждет после точки ответа 250 от 
сервера. Если 250 не было, клиент считает что сообщение не отправлено и через 
некоторое время пытается отправить повторно. В свою очередь сервер отвечает 250 
только после того как записал сообщение в очередь.

Если таких подтверждений в прикладном нет (или сервер реализует протокол 
неправильно), то сообщения иногда будут теряться, все что возможно сделать это 
без переделки протокола - это немножко снизить вероятность потери.

Если вы не хотите слать данные после отправки сервером FIN, потому что сообщение 
в этом случае будет потеряно и способа об этом узнать, то это уже достаточно плохо.



Подробная информация о списке рассылки Moscow-pm