![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Протокол обмена между клиентом и сервером
Gek1 © (2005-05-18 18:30:00)Имееться ClientSocket, работающий в неблокирующим режиме.
Протокол обмена устроен так, что пакеты уходящие от сервера сжимаются алгоритмом Хаффмана. Каждый пакет имеет разную длинну.
Как известно - по пути пакеты могут резаться и склеиватся. Но получается что расжатый Хаффманом кусок пакета даст совсем другие данные. Практически всегда эти первая часть данных будет не поврежденна, что даст мне понять что за пакет идет и я смогу дождатся пока прийдет его полная длинна.
Но иногда рассжатый кусочек пакета настолько искажается, что мой алгоритм дает збои.
Сам протокол обмена я поменять не могу. Он придуман не мною и очень давно. Сервер тоже написан не мною. Я написал всего лишь клиент для сервера.
Возникает вопрос:
Можно ли всетаки как-то узнать длинну целого пакета, который отправлял сервер?
Мастера, подскажите пожалуйста что можно сделать в данной ситуации?
P.S. если понадобится код - приведу.
Digitman (2005-05-19 14:49:00) [81]
> распаковывать кусок
какой кусок ?
Alexander Panov (2005-05-19 14:45:00) [82]Gek1 (19.05.05 14:42) [21]
Как понял единственный вариант - распаковывать кусок и молится, чтобы первые пару байтиков небыли испорченны.
Это вообще не вариант. Вернее, неправильный вариант.
Длину упакованного блока данных тебе нужно передавать в неупакованномвиде.
Получив блок соответствующей длины, распаковываешь его. И всё.
Gek1 (2005-05-19 14:42:00) [83]Получается вариантов никаких...
Как понял единственный вариант - распаковывать кусок и молится, чтобы первые пару байтиков небыли испорченны.
Digitman (2005-05-19 14:28:00) [84]
> он же должен передавать полную длинну?
какую такую "длину" ?
у потока НЕТ длины ! на то он и поток ...
TCP - поточный протокол !
Polevi (2005-05-19 14:22:00) [85]никак
Gek1 (2005-05-19 14:15:00) [86]
> Digitman © (19.05.05 14:12) [17]
Ну режет же не Серверное приложение... режет же сам протокол TCP ... он же должен передавать полную длинну? Вот как можно ее узнать?
Digitman (2005-05-19 14:12:00) [87]без знания тобой установленного прикл.протоколом признака размера сообщения в потоке "склеивать" что-либо бессмысленно
чтобы что-то "склеить", нужно знать полный исходный размер "неразбитого"
Маркони (2005-05-19 14:04:00) [88]А это невозможно вычеслить представь что приходит пакет содержаший только нули... но чтоб обработать его надо получить его вначале весь ...
Polevi (2005-05-19 14:01:00) [89]осталось найти признак по которому определить конец блока
я понял что такого признака у тебя нет
Gek1 (2005-05-19 14:00:00) [90]
> ты уверен что сжатые пакеты не предваряются каким либо заголовком,
> содержащим размер
В данные размер не встаивается...
Иначе бы расжатые данные с длинной давали бы мне неверный результат всегда ....
Маркони прав.
Хочу добавить, проблема возникает очень редко. И как я заметил именно тогда, когда перегружен сервер и плохой интернет. Тогда сам TCP начинает жудко дробить данные на мелкие кусочки и посылать по кусочкам.
Как я понял надо на клиенской стороне заставить его сначала все это склеить назад и только потом дать знать что пришел пакет данных.
Маркони (2005-05-19 13:54:00) [91]Я описал проблему так как есть +) Ты можешь вообще забыть про сжатие и тд итп представь что это один блок даных , чтоб его обработать надо получить его весь.
Polevi (2005-05-19 13:48:00) [92]дело не в данной ошибке
клиенту просто недостаточно данных для реализации
ты уверен что сжатые пакеты не предваряются каким либо заголовком, содержащим размер ?
Gek1 (2005-05-19 13:40:00) [93]
> и возвращает она число РЕАЛЬНО прочитанных указанный в буфер
> байт, которое вовсе НЕ обязано быть точно равно требуемому
> значению в TempBufLen (т.е. результат ф-ции - число, меньшее
> или равное значению 2-го параметра)
Проверка стояла и я ее убрал когда переделывал алгоритм в очередной раз ...
По наблюдениям скажу что были проблемы и раньше, когда стояла проверка и ниразу не выловил чтобы ReceiveBuf() <> ReceiveLength.
Спасибо, что подметил... щас доделаю... Но почему то я уверен почти на 100% что дело не в ней.
Маркони (2005-05-19 13:35:00) [94]Итого опишу задачу:
Сервер посылает блок данных большой, разом, одним куском. Клиент при приеме почему то получает эти данные кусками, хотя сервер ничего не режет а посылает одним блоком. Для правильной обработки данных Клиентом ему надо получить весь блок данных, а потом обработать. Вопрос... каким образом можно определить сколько данных надо принять и надо ли их дальше принимать если сервер послал целый пакет а клиент принимает его кусками... Таким пакетов может быть много так что надо как то определить это всё куски одно и того же пакета или это новый пакет.
Digitman (2005-05-19 13:33:00) [95]
> Socket.ReceiveBuf(TempBuf^,TempBufLen);
вот и первая же ошибка , грубая причем
ReceiveBuf - это функция !
и возвращает она число РЕАЛЬНО прочитанных указанный в буфер байт, которое вовсе НЕ обязано быть точно равно требуемому значению в TempBufLen (т.е. результат ф-ции - число, меньшее или равное значению 2-го параметра)
а дальше смотреть твой код даже нет смысла..
Gek1 (2005-05-19 13:29:00) [96]
> сервер формирует пакет данных, длина пакета разная в зависимости
> от типа
> пакет сжимается и отправляется клиенту
Сервер формирует цепочку N целых пакетов данных (пакет может быть один, а может и быть несколько в цепочке)
Далее все это дело сервер сжимает и отправляет.
> правильность декомпресии возможна только в том случае если
> на вход декомпрессора подается сжатый пакет целиком - не
> больше и не меньше, в противном случае данные будут не правильно
> распакованы
абсолютно верно.
> поскольку клиент не знает размера сжатого пакета посланного
> сервером - "подать на вход декомпрессора сжатый пакет целиком"
> он не в состоянии
Вот если какието средства, чтобы узнать полную длинну сжатых данных, не трогая сервер?
> задача не имеет решения, кроме чегонибудь кривого типа таймаута..
> да и то только в том случае если очередной пакет возвращается
> сервером по запросу
Таймаут отпадает. Пакет может быть один.... маленьким ... и на его приход необходимо немедленно ответить.
Все таки есть какоето решение, но как это сделать - я не могу понять. Разработчики сервера сделали клиент давно и он рабочий. Узнать в чем изюминка этого алгоритма - невозможно. Вот у вас, Мастера, спрашиваю может у вас есть идеи как определить эту изюминку?
Polevi (2005-05-19 13:02:00) [97]если я правильно понял:
сервер формирует пакет данных, длина пакета разная в зависимости от типа
пакет сжимается и отправляется клиенту
правильность декомпресии возможна только в том случае если на вход декомпрессора подается сжатый пакет целиком - не больше и не меньше, в противном случае данные будут не правильно распакованы
поскольку клиент не знает размера сжатого пакета посланного сервером - "подать на вход декомпрессора сжатый пакет целиком" он не в состоянии
задача не имеет решения, кроме чегонибудь кривого типа таймаута.. да и то только в том случае если очередной пакет возвращается сервером по запросу
Polevi (2005-05-19 12:56:00) [98]кошмар
Gek1 (2005-05-19 12:30:00) [99]
> тогда сервис должен работать на стороне сервера
К сожалению никаких вещей на сервере производить нельзя. Сервер - будем считать черным ящиком, который никому не доступен физически.
> ТСР - протокол с гарантированной доставкой : поток данных,
> отправляемый передатчиком, гарантированно доставляется приемнику
> точно в той же последовательности, в какой он формируется
> на передающей стороне.
Я не говорю что TCP - протокол неправильно работает. Я имел ввиду другое, что когда я принимаю куски пакетов, порезанные TCP - протоколом, и пытаюсь расжать - там выходит проблема.
> Сжимается каждый пакет отдельно, или весь поток?
Сжимается поток данных. Причем в поток данных может входить один пакет или же несколько пакетов.
Прозьба не путать пакет TCP протокола с "этим" пакетом.
Под пакетом от сервера я имею ввиду некий тип данных, который дает мне полную информацию о определенной ситуации.
> что у тебя ошибки в алгоритме клиента
Сам подозревал. уже не впервый раз все проверял и с нуля переписывал - ошибок небыло обнаружено, а результат один и тот же.
Вот мой код:
procedure TCharacter.GameClientOnRead(Sender: TObject; Socket: TCustomWinSocket);
{Принимаем от Server-а некие данные}
var i,c,index : Integer;
SHex : String;
TempBuf : Parray;
TempBufLen : Integer;
MD5Out : PArray;
PacketLen : Integer;
begin
{ fIncomingBuf : PArray;
fIncomingBufLen : Integer; }
{Если fIncomingBufLen меньше нуля то выходим дисконектом}
if fIncomingBufLen < 0 then
begin
AddtoDebugLog(GetCharName+': '+'Error: IncomingBuf < 0');
GameClient.Close;
Exit;
end;
{Принимаем данные в TempBuff и дописываем содержимое в IncomingBuff}
New(TempBuf);
TempBufLen:=Socket.ReceiveLength;
try
Socket.ReceiveBuf(TempBuf^,TempBufLen);
SHex := '';
for i := 0 to TempBufLen - 1 do SHex := SHex + IntToHex(TempBuf^[i],2);
AddPacket2Debug(0,SHex);
except
AddToDebugLog(GetCharName+': '+'Error with receiving Some Data');
end;
for i := 0 to (TempBufLen-1) do
begin
fIncomingBuf^[fIncomingBufLen+i] := TempBuf^[i];
end;
fIncomingBufLen:=fIncomingBufLen+TempBufLen;
Dispose(TempBuf);
{Принятые данные добавлены в конец IncomingBuf}
{Если EncryptMetod 4, то есть еще MD5}
if fEncryptMetod = 4 then
begin
New(MD5Out);
MD5Encrypt(@MD5Obj,fIncomingBuf,MD5Out,fIncomingBufLen);
for i := 0 to fIncomingBufLen - 1 do fIncomingBuf^[i] := MD5Out^[i];
Dispose(MD5Out);
end;
{Расжимаем буфер}
PHuffDecompressLen := fIncomingBufLen * 4 + 4;
DecompressNew(fIncomingBuf,PHuffDecompress,PHuffDecompressLen,fIncomingBufLen);
{Если длинна PHuffDecompressLen равна нулю то выходим}
if PHuffDecompressLen <= 0 then Exit;
{Далее проверяем чтобы в буффере было N целых пакетов}
index:=0;
PacketLen := 1; {Любое положительное число}
while (index < PHuffDecompressLen) and (PacketLen > 0) do
begin
New(TempBuf);
for i := 0 to PHuffDecompressLen - (index + 1) do TempBuf^[i] := PHuffDecompress^[i+index];
TempBufLen := PHuffDecompressLen - index;
{Ф-ция GetPacketLen вернет:
0 если пакет маленький и надо еще кусок
-1 если пакет неизвестный
длинну, если длина >= пакету}
PacketLen := GetPacketLen(TempBuf,TempBufLen);
{Освободим TempBuf}
Dispose(TempBuf);
{Если длинна больше нуля, то увеличиваем индекс}
if PacketLen > 0 then index := index + PacketLen;
{Если длинна равна 0, то выходим}
if PacketLen = 0 then Exit;
{Если длинна меньше нуля, то:}
if PacketLen < 0 then
begin
{Выдаем ошибку о неизвестном пакете}
SHex := '';
for i := index to PHuffDecompressLen - 1 do SHex:= SHex + IntToHex(PHuffDecompress^[i],2);
AddtoDebugLog(GetCharName+': '+'Внимание! Обнаружен неизвестный пакет: '+SHex);
{Останавливаем соединение}
GameClient.Close;
{Выходим}
Exit;
end;
{Если длинна больше $8000, то:}
if PacketLen > $8000 then
begin
{Выдаем ошибку о неверной длинне пакета}
SHex := '';
for i := index to PHuffDecompressLen - 1 do SHex:= SHex + IntToHex(PHuffDecompress^[i],2);
AddtoDebugLog(GetCharName+': '+'Внимание! Неверная длинна пакета: '+SHex);
{Останавливаем соединение}
GameClient.Close;
{Выходим}
Exit;
end;
{Далее или у нас будет только N целых пакетов
или выйдем ранее из процедуры}
end; {End while (index < PHuffDecompressLen) and (PacketLen > 0)}
{Далее обработаем данные}
while (PHuffDecompressLen > 0) do
begin
PacketLen := GetPacketLen(PHuffDecompress,PHuffDecompressLen);
i := UnPackGamePackets(PHuffDecompress,PHuffDecompressLen);
if (i <> PacketLen) or (i = -1) then
begin
{Выдаем ошибку о неверной обработке пакета}
SHex := '';
for c := 0 to PHuffDecompressLen - 1 do SHex:= SHex + IntToHex(PHuffDecompress^[c],2);
AddtoDebugLog(GetCharName+': '+'Внимание! Неверно обработали пакет: '+SHex);
AddtoDebugLog(GetCharName+'i = '+IntToStr(i)+', PacketLen = '+IntToStr(PacketLen));
{Останавливаем соединение}
GameClient.Close;
{Выходим}
Exit;
end;
{Занесем в Debug обработанный пакет}
SHex := '';
for c := 0 to PacketLen - 1 do SHex:= SHex + IntToHex(PHuffDecompress^[c],2);
AddPacket2Debug(1,SHex);
{Вырезаем занесенный пакет}
for i := 0 to PHuffDecompressLen - (PacketLen + 1) do
PHuffDecompress^[i] := PHuffDecompress^[i+PacketLen];
PHuffDecompressLen := PHuffDecompressLen - PacketLen;
end;
{После обработки N целых пакетов
необходимо сбросить накапливаемый буффер}
fIncomingBufLen := 0;
end;
Slym (2005-05-19 10:38:00) [100]Что ты имеешь ввиду под "пакеты уходящие от сервера"?
TCP пакеты или пакеты протокол обмена...
Сжимается каждый пакет отдельно, или весь поток?
Если сжимается поток то и приходящие пакеты должны распаковываться с учетом поточности...