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, отдельное спасибо за интерес и инфу))

jimon 19.07.2012 00:26

Ответ: Непонятки с TCP/IP
 
Цитата:

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

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

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

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

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

а где уверенность что блиц это кладёт в ОДИН TCP ПАКЕТ ? :) TCP пакетный протокол (когда udp - дейтаграммный), и в нём гарантируется прием в точно такой же последовательности как была произведена передача

DStalk 19.07.2012 01:37

Ответ: Непонятки с TCP/IP
 
Пакетный? TCP насколько я помню - непрерывный поток данных, не подразделяющийся на пакеты. Пакетами в данном случае я называю отдельные сообщения в TCP потоке... Как я писал выше оба способа дают одинаковый результат.

moka 19.07.2012 02:07

Ответ: Непонятки с TCP/IP
 
TCP - это поток данных. Протокол может разбивать данные на сегменты, либо наоборот на уровне протокола в системе ждать пока не будет достаточно данных для полного сегмента (Нагглэ Алгоритм).
Тут дело не с протоколом, а с блицем, и не тем как он кладёт всё в кучу или наоборот, а в том что видимо число порой не отправляется последовательно. Блиц старик, и я бы не юзал стандартный функционал для сети (снова).

dimanche13 19.07.2012 10:29

Ответ: Непонятки с TCP/IP
 
доверяй wireshark-у юный падаван
советы его мудры, око его всевидяще
и да прибудет с тобой сила

impersonalis 19.07.2012 10:55

Ответ: Непонятки с TCP/IP
 
Цитата:

Сообщение от MoKa (Сообщение 233287)
Блиц возможно применяет собственный алгоритм склеивания сообщений, тем самым если слишком часто отсылать, может клеить стринги вместе.


Я что-то не очень понял. Разделители типа EOL между строками никуда не денутся. А дейтаграммы и на уровне движка, и на уровне tcp всегда стремятся склеится (лучше отправить 1 раз 500 байт, чем два раза по 250). Функции запроса конкретного типа данных (типа ReadLine) очевидно работают в блокирующем режиме (дожидаясь приёма объекта целиком) с крахом по таймауту, задаваемому функцией TCPTimeouts.

тред не читал @ сразу овтечал

Пётр 19.07.2012 14:20

Ответ: Непонятки с TCP/IP
 
Цитата:

Сообщение от Dstalk (Сообщение 233271)
Вот код:
Код:

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 захватывает 4 байта самого сообщения. Просто integer куда-то пропадает...

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

Я вообще удивляюсь что работает.
Где гарантия что первый вызов ReceiveNetworkData() запишет в буфер именно 4 байта? А если пришло только, скажем, 2 байта, что тогда?

Где гарантия что второй вызов функции, поместить в буфер именно столько байт, сколько указанно в TCPlength?
Может на момент чтения еще не все данные пришли, вот и будет выше описанный баг.

Если хотите чтобы все работало без сбоев, делайте следующим образом:
Заведите буфер, размером в несколько раз больше чем максимальный размер пакета.
Читайте данные в этот буфер и анализируйте сколько байт реально было прочитано функцией ReceiveNetworkData().
Если меньше положенного, то ждите и через время опять читайте, помещая результат в конец буфера.
Как только приняли все данные, тогда обрабатываете.
Буфер очищаете и опять в него пишите принимаемые данные.

DStalk 19.07.2012 14:52

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

Цитата:

Сообщение от Пётр (Сообщение 233345)
Где гарантия что второй вызов функции, поместить в буфер именно столько байт, сколько указанно в TCPlength? Может на момент чтения еще не все данные пришли, вот и будет выше описанный баг.

Если на момент чтения не все данные пришли, и это я тоже писал выше, он бы считал середину следующего пакета. Такого никогда не происходило. Этот баг проявляется именно в пропаже четырех байт integer`а и очень редко. Скорее всего все-таки блитц виноват.

Прежде чем отвечать, внимательно прочитайте предыдущие сообщения в треде. Спасибо.

Пётр 19.07.2012 16:24

Ответ: Непонятки с TCP/IP
 
Ну смотрите дело ваше.
Я сталкивался с подобным и выше описал как решил проблему.

В таком варианте как сейчас, есть вероятность глюков из-за приема не всех данных пакета.

Пётр 20.07.2012 12:18

Ответ: Непонятки с TCP/IP
 
Кстати, вот один из сценариев пропадания первых 4-ёх байт пакета.

Пакеты отправляются друг за другом и возникает такая ситуация что первые 4 байта нового пакета принимаются с предыдущим пакетом.

Как бороться с этим, описал выше.

DStalk 20.07.2012 12:24

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

Пётр 20.07.2012 14:02

Ответ: Непонятки с TCP/IP
 
Цитата:

Сообщение от Dstalk (Сообщение 233429)
это я тоже проверял

Как проверял?

DStalk 20.07.2012 14:09

Ответ: Непонятки с TCP/IP
 
логи отправленных и принятых данных, сравнивал, анализировал;)

Я уже в этом треде раз 5 написал, что код работает абсолютно нормально. Но очень редко и рандомно пропадают именно 4 байта, других ошибок и багов никогда не было. Пётр, извини конечно, но внимательнее читай тему...

Пётр 20.07.2012 14:29

Ответ: Непонятки с TCP/IP
 
Цитата:

Сообщение от Dstalk (Сообщение 233457)
логи отправленных и принятых данных, сравнивал, анализировал;)

Мне это ни о чем не говорит. Код записи в логи в студию.
Ну или как альтернатива, гадание на кофейной гуще, раз кода нет.

Цитата:

Сообщение от Dstalk (Сообщение 233457)
Пётр, извини конечно, но внимательнее читай тему...

Зачем тогда была создана эта тема если не пытаешься услышать других?
Кому нужно избавится от глюков в проге, мне или тебе?
Я тебе уже несколько раз писал чтобы проверял сколько реально было прочитано байт, но видимо ты невнимательно читаешь.

DStalk 20.07.2012 15:24

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

PrintN(TCPPacket)
не?)) Практически тоже самое при отправке сообщения.
Ты мне не веришь что других ошибок не возникало, кроме пропажи integer`а. Ну извини, логи у меня выводятся в консоль, и я их не сохраняю.
Предпологалось, что последнее сообщение было на первой странице треда, где я поблагодарил MoKa.
Глюков в программе нет, единственное что нужно проверку на неполность сообщения, но это я и сразу знал, и писал об этом.

Подправил кстати TCPPacket=PeekS(*TCPBuffer,TCPlength) строчку в коде на первой странице, на прием никак не влияет, но в стринг TCPPacket могут записатся куски старых пакетов из буффера, так как он не очищается...

Закрыть бы темку, а то ничего кроме срача тут не предвидится...

Пётр 20.07.2012 15:48

Ответ: Непонятки с TCP/IP
 
Цитата:

Сообщение от Dstalk (Сообщение 233472)
Код:

PrintN(TCPPacket)
не?))

Конечно не! Вот как должно быть.
Код:

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

RealCountBytes_2 = ReceiveNetworkData(TCPclientID,*TCPBuffer,TCPlength)
TCPPacket=PeekS(*TCPBuffer)

PrintN(Str(RealCountBytes_1)+"        "+Str(RealCountBytes_2))

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

Цитата:

Сообщение от Dstalk (Сообщение 233472)
Закрыть бы темку, а то ничего кроме срача тут не предвидится...

Раз ты не слушаешь тех, кто уже сталкивался с подобным и старается тебе помочь, то пожалуй да, тему нужно закрыть. Раз тебе помощь не нужна, то зачем вообще создавал тему?

Не выйдет из тебя программиста. Нет логического мышления и не слушаешь тех, кто старается тебе помочь!

DStalk 20.07.2012 16:05

Ответ: Непонятки с TCP/IP
 
Все что я хотел услышать, сказал MoKa. TCPlength тоже логировалось, но раньше, потом убрал за ненадобностью. Когда пропадает те самые 4 байта, тогда TCPlength принимает значение первых 4-х байт тела самого сообщения - а первые 4 байта - символы, то есть если их перевести в integer, получится всегда больше 1000, на что и стояла последующая проверка. Я об этом уже писал.

Ты сталкивался что пропадают конкретные 4 байта сообщения? Причем их нет в предыдущем сообщении, и нет нигде - просто не пришли, как будто их и не отправляли. Это не те случаи, когда пришло еще не все, что должно было, и не те, когда сообщение читается неправильно. Это конкретный, очень специфический случай...

На самом деле, в данном случае надо написать кусок клиента на пурике, и посмотреть как он будет себя вести.

Ну уж если не выйдет из меня программиста, то все - пойду повешусь на люстре, смысла жизни нет:(

п.с. за старание помочь конечно спасибо, но по-моему, мы просто неправильно друг друга понимаем...

DStalk 20.07.2012 16:16

Ответ: Непонятки с TCP/IP
 
Цитата:

Сообщение от Dstalk (Сообщение 233478)
Ну уж если не выйдет из меня программиста, то все - пойду повешусь на люстре, смысла жизни нет:(

И винить в этом буду тебя:) По ночам являться буду>>:(

moka 20.07.2012 17:45

Ответ: Непонятки с TCP/IP
 
Сразу отвечаю, не читаю.

Ты проверяешь длину буффера так? А буффер уже создан давно, и ты лишь записываешь в него прочтённые данные так? Значит длина у него не будет соответствовать на самом деле длине принятых данных.
Плюс ты вроди как переиспользуешь буффер для приёма заголовка (4 байта) и затем в него же читаешь само сообщение, так?
Получается что в определённом сценарии, при попытки прочитать 4 байта заголовок, ничего не было прочитано, и в буфере будет 4 байта с прошлого буффера сообщения.
Если я ничего не упустил - это твоя проблема описана выше.
Она заключается в правильной проверке принятых данных и корректной реакции на не соответствия.

DStalk 20.07.2012 18:12

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

еще раз код:
Код:

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

Я не считал полную длину именно считанных данных, как прелагал Пётр. Но все пришедшие пакеты полностью видны в консольке, соответственно можно посмотреть, где пакет "недочитался", где слишком много, что не пришло.

То что закоменчено, это как раз было логирование пришедших данных. И в случае ошибки в переменной temp было как раз начало следующего пакета, а должна быть по идее длина пакета - integer.

moka 20.07.2012 18:21

Ответ: Непонятки с TCP/IP
 
Ты считаешь длину буффера а не принятых данных.
Длина принятых данных - это число возвращаемое функцией ReceiveNetworkData.
http://www.purebasic.com/documentati...tworkdata.html

DStalk 20.07.2012 18:27

Ответ: Непонятки с TCP/IP
 
эмм, я тут вообще длину не считаю:) длина принимается от клиента - 4 байта integer...

moka 20.07.2012 22:34

Ответ: Непонятки с TCP/IP
 
Длина не сообщения, а длина принятых не сервере данных при использовании функции ReceiveNetworkData.

DStalk 20.07.2012 22:55

Ответ: Непонятки с TCP/IP
 
Да знаю я что это функция возвращает, хелп читал, примеры других прог смотрел:) но в данном случае эта длина мне не нужна - предпологается что пакеты приходят в одном и том же формате. Следующая после этого функция уже парсит считанные данные - и если они не подходят под формат просто пропускаются. В принципе от нужного клиента данные в другом формате и не приходят, так что тут все пучком))

moka 20.07.2012 23:08

Ответ: Непонятки с TCP/IP
 
Смотри.

Ты читаешь заголовок 4 байта в буффер. Затем в этот же буффер ты читаешь само сообщение по длине из заголовка. Вроди всё ок.
Далее следующий раз читаешь ещё один заголовок 4 байта, и функция ReceiveNetworkData возвращает 0 т.к. по каким-то обстаятельствам не прочла данные, следственно не трогает сам буффер с данными. А ты не проверил это, и сразу переходишь к получению числа из ожидаемых данных заголовка, а это на самом деле прошлые данные (с прошлого сообщения), следственно и длина будет не верная.
Так понятнее?

С самого начала, я и Пётр говорили именно об этой проверке, не самого буффера с данными, а того сколько принялось данных функцией ReceiveNetworkData.

DStalk 20.07.2012 23:25

Ответ: Непонятки с TCP/IP
 
Не возвращает она ноль, если она возвратила 0, значит данных нет для чтения. А эта процедура вызывается только если есть данные. Вот так - тут ошибки тоже нет.

Кстати где закоменчено там - вывод в консоль и str и int из этих 4 байт, всегда показывал начало следующего пакета, ни разу предыдущего.


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

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