forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   PureBasic (http://forum.boolean.name/forumdisplay.php?f=90)
-   -   Непонятки с TCP/IP (http://forum.boolean.name/showthread.php?t=17047)

DStalk 18.07.2012 21:55

Непонятки с TCP/IP
 
Сервер на пурике принимает "пакет". В нем сначала идет 4-байтовый integer, в котором содержится полная длина последующего сообщения. Проблема в том, что очень редко и в рандомных случаях этот integer теряется, сразу идет сообщение. В таком случае сообщение обрабатывается, но следующий пакет, если он уже пришел, может быть утерян, так как неизвестно где данный заканчивается.:(
Может кто сталкивался? Очень неприятная проблема...

moka 18.07.2012 22:21

Ответ: Непонятки с TCP/IP
 
Читаешь всегда ТОЧНО столько сколько ожидаешь?
При этом проверяй что прочтённая длина равна ожидаемой. Если меньше чем ожидал, значит жди ещё пока остаток данных не прийдёт, это редкость, т.к. обычно данные приходят обычно слитые, нежели разбитые (но и такое бывает).
Так что читая 4 байта (заголовок), далее читай данные длины, и убедись что прочитал ровно столько, сколько ожидал, лишь потом освобождай блок для последующего чтения.

Тут больше логическая проблема кода.

DStalk 18.07.2012 22:29

Ответ: Непонятки с TCP/IP
 
Да я вообще не в понятках в чем проблема. Читаю ровно столько сколько отправляю - проверял в логах. Потеря заголовка проявляется очень редко и рандомно. Даже подряд когда отправляю 100 разных пакетов (без задержки между ними), проблема не вылезает, ну если только очень редко, - значит проблема не в логике. хз даже что делать то...((

moka 18.07.2012 22:41

Ответ: Непонятки с TCP/IP
 
Ты уверен что прочтённые данные длиной которой ожидаешь, и ты принял все что ожидал? При приёме, есть переменная с длиной.
Также, ты блокируешь чтение между началом чтения хедера и тела до полного прочтения тела?

DStalk 18.07.2012 22:55

Ответ: Непонятки с TCP/IP
 
Вот код:
Код:

ReceiveNetworkData(TCPclientID,*TCPBuffer,4)
TCPlength.i=PeekL(*TCPBuffer)
If TCPlength>1000:TCPlength=1000:PrintN("Packet error!"):EndIf
ReceiveNetworkData(TCPclientID,*TCPBuffer,TCPlength)
TCPPacket=PeekS(*TCPBuffer,TCPlength)

Сколько тестил - он всегда(!) читает сообщение необходимой длины, не больше и не меньше. Но очень редко TCPlength захватывает 4 байта самого сообщения. Просто integer куда-то пропадает...

В этом коде еще нет проверки на то, если сообщение пришло не полностью, но когда на одном компьютере тестишь - это проблем не вызывает, проверял.

moka 18.07.2012 22:58

Ответ: Непонятки с TCP/IP
 
Ты запускаешь данную функцию по ассинхронному каллбеку, или один раз в цикле опрашиваешь?
Ты проверил что данные хедера все пришли (4 байта)?

DStalk 18.07.2012 23:00

Ответ: Непонятки с TCP/IP
 
в цикле, проверка не нужна (в сообщение выше дописал)

moka 18.07.2012 23:03

Ответ: Непонятки с TCP/IP
 
У меня сомнения по этому коду:
Код:

If TCPlength>1000:TCPlength=1000:PrintN("Packet error!"):EndIf
Ты проверяешь если длина больше 1000, значит ошибка (типо число в заголовке никогда не больше 1000), но затем продолжаешь читать.
Ты уверен что проблем нету с подсчётом длины пакета?

DStalk 18.07.2012 23:08

Ответ: Непонятки с TCP/IP
 
Уверен. А вот эта строчка выдает ошибку, но принудительно читает пакет, вместе с возможными кусками последующих пакетов. Этой строчки вообще не будет, если проблема перестанет проявлятся.
А выдает TCPlength.i=PeekL(*TCPBuffer) больше тысячи, потому что в памяти находится первые четыре байта пакета, а там символы, если считать их из памяти как integer, всегда получается больше 1000. Это типа проверка на ошибку пакета...

Да, и по идее, у нормальных пакетов длина всегда меньше 1000.

Забыл сказать - пакеты отправляю прогой на Blitz3D, пробовал двумя способами:
Код:

;1 способ
WriteString streamIP,msg1$
;2 способ
WriteInt streamIP,Len(msg1$)+2
WriteLine streamIP,msg1$

Оба способа дают одинаковые результаты...

moka 18.07.2012 23:16

Ответ: Непонятки с TCP/IP
 
Ну почему число TCPlength в итоге больше 1000 это и так понятно.

Тут нету проверки сколько данных ты принял, это и вызывает проблему скорее всего. Т.к. в очень редких случаях ты примешь не достаточно данных (в сетевом стёке не всё ещё), а ожидаешь достаточно.
Попробуй проверку замутить:
Код:

ReceiveNetworkData(TCPclientID,*TCPBuffer,TCPlength)
MessageLength.i=PeekL(*TCPBuffer)
If MessageLength != TCPlength : PrintN("Not the same!") : EndIf

Также снова, убедись что подсчёт длины пакета корректный и сливаешь ты их тоже корректно.
Лучше запости код где ты это делаешь, и опиши, делаешь ассинхронно или синхронно?

DStalk 18.07.2012 23:20

Ответ: Непонятки с TCP/IP
 
Проверял я это все. Если бы принял недостаточно данных - то следующий PeekL(*TCPBuffer) выдал бы данные с середины предыдущего пакета - такого ни разу не было. Он всегда выдает или правильную длину пакета, или, очень редко, 4 байта начала тела сообщения.

В сообщении выше дописал про отправку пакетов))

moka 18.07.2012 23:25

Ответ: Непонятки с TCP/IP
 
Длина текста будет не соответствовать длине пакета.
Хз как блиц, но в более низкого уровня, нужно сперва чётко конвертировать строки в бинарное представление и вычислять длину бинарного массива. Т.к. тот же символ в UTF8 или ASCII уже разных размеров.
Также WriteLine будет писать дополнительные данные конца строки.

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

Короче, в пурике всё ок, это блиц тупит.
Если есть возможность конвертировать в блице строку в бинарный вид и отправлять массив байтов - делай лучше так.
Если есть возможность использовать тот же RakNet - лучше юзай его.

DStalk 18.07.2012 23:28

Ответ: Непонятки с TCP/IP
 
Все то что ты написал, и про кодировки и про дополнительные символы я учел. Смысл в том что проблема специфическая очень - пропадают именно 4 байта длинны сообщения, все остальное в порядке, и всегда нормально читается.

Есть вариант решения, тупой правда) Отправлять перед телом сообщения не один а два одинавковых integer`a. Даже если один пропадет, второй нормально прочитается, и пакеты после данного не будут потеряны:)

moka 18.07.2012 23:30

Ответ: Непонятки с TCP/IP
 
Теперь я скорее всего догадался в чём проблема:
Блиц возможно применяет собственный алгоритм склеивания сообщений, тем самым если слишком часто отсылать, может клеить стринги вместе.
Костыль который предлагаешь - супер изврат..

Короче говоря, лучше возьми либку.

DStalk 18.07.2012 23:39

Ответ: Непонятки с TCP/IP
 
Насчет склеивания сообщений - вторым методом отправляю integer отдельно, и все равно такая фигня. Кстати тестил и на удаленных соединениях с несколькими клиентами. Точно та же проблема.

Странно все это, ну да ладно:) А костыль изврат, ага, но зато не будет проблемы, и нагрузка от него на сервер и соединение очень незначительные.

MoKa, отдельное спасибо за интерес и инфу))


Часовой пояс GMT +4, время: 23:49.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot