Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Протокол обмена между клиентом и сервером


Страницы:   1 2 3 4 5 6

Gek1 ©   (2005-05-18 18:30:00) 

Имееться ClientSocket, работающий в неблокирующим режиме.
Протокол обмена устроен так, что пакеты уходящие от сервера сжимаются алгоритмом Хаффмана. Каждый пакет имеет разную длинну.

Как известно - по пути пакеты могут резаться и склеиватся. Но получается что расжатый Хаффманом кусок пакета даст совсем другие данные. Практически всегда эти первая часть данных будет не поврежденна, что даст мне понять что за пакет идет и я смогу дождатся пока прийдет его полная длинна.

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

Сам протокол обмена я поменять не могу. Он придуман не мною и очень давно. Сервер тоже написан не мною. Я написал всего лишь клиент для сервера.

Возникает вопрос:
Можно ли всетаки как-то узнать длинну целого пакета, который отправлял сервер?

Мастера, подскажите пожалуйста что можно сделать в данной ситуации?

P.S. если понадобится код - приведу.


Digitman   (2005-05-20 14:23:00) [41]


> Gek1   (20.05.05 14:08) [58]



> 1. Принимаю данные


"шматок" данных ты рпринял, а не "данные".


> 2. Дописываю в конец буфера


да на здоровье ..


> 3. Расжимаю данные алгоритмом Хаффмана


какие "данные"-то ? только что принятый "шматок" или N "шматков", принятых ранее + текущий "шматок" ?

разжимать следует не непонятно что, а данные, соответствующие ЦЕЛОСТНОМУ сообщению передатчика !

а размер этого сообщения ты не знаешь !


Eraser   (2005-05-20 14:20:00) [42]

Alexander Panov ©   (20.05.05 14:18) [61]

Ну или так.
Только терми "пакет" в данном случае лучше не использовать, чтобы путаницы небыло.


Alexander Panov   (2005-05-20 14:18:00) [43]


> Где-то ДОЛЖНА передаваться длина данных... или стоп-последовательность
> (что врядли).


Возможен еще вариант - каждый запакованный пакет имеет одинаковую длину, определенную разработчиком.


Eraser   (2005-05-20 14:15:00) [44]

Gek1   (20.05.05 14:11) [59]

1. Что за сервер? Приведи название программы.
2. каждый пакет может в свою очередь иметь дин. длинну

в TCP впринципе очень сложно формировать каждый пакет по отдельности - свой драйвер ядра для этого писать надо. Так что ты заблуждаешься. Где-то ДОЛЖНА передаваться длина данных... или стоп-последовательность (что врядли).


Gek1   (2005-05-20 14:11:00) [45]


> Eraser ©   (20.05.05 13:59) [56]

Так не будет работать.
Поток данных может (и очень часто так бывает) содержать несколько пакетов с данными. каждый пакет может в свою очередь иметь дин. длинну, которую мне надо определить, чтобы посчитать полную длинну потока данных.

Проще некуда.


Gek1   (2005-05-20 14:08:00) [46]


> Digitman ©   (20.05.05 13:42) [55]

Алгоритм работает так:
1. Принимаю данные
2. Дописываю в конец буфера
3. Расжимаю данные алгоритмом Хаффмана
4. Далее проверяем расжатый блок данных на наличие N целых пакетов. И если там оказывается не полные пакеты, то выходим и ждем пока прийдет новый кусок данных. Смотри [54]
5. Далее (если у нас N целых пакетов):
Обрабатываем каждый пакет и сбрасываем входной буфер на ноль.

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


> ГДЕ в [54] обращение к тем самым "первым 2-ум байтам" ?
> ... покажи мне ! ... ткни меня носом ...

В [54] смотри ф-цию GetPacketLen

А в описании этой ф-ции [52]:
PacketID := PacketObj^[0];
Здесь обратилиcь к первому байтику и узнали ID пакета.

Далее в [52] по коду:

{Если динамическая длинна, то посчитаем ее
или выдадим 0 если нехватает данных}
if PacketLen >= $8000 then
begin
if Size > 3 then
  begin
  ResSize := strtoint('$'+IntToHex(PacketObj^[1],2)+IntToHex(PacketObj^[2],2));
  {Если пакет неполный, то выдадим 0}
  if ResSize <= Size then Result := ResSize
  else Result := 0; {if ResSize > Size}
  end
else Result := 0; {if Size < 3}
end


Обрати внимание на:
ResSize := strtoint('$'+IntToHex(PacketObj^[1],2)+IntToHex(PacketObj^[2],2));
Здесь как раз в случае дин. длинны - определяем нужную длину пакета из 2-го и 3-го байта.

Т.е все динамические пакеты имеют один и тот же формат в начале:
· BYTE cmd
· BYTE[2] packet length


Digitman   (2005-05-20 14:04:00) [47]


> Eraser ©   (20.05.05 13:59) [56]


да не знает он ни черта протокол этот..
тычется как слепой котенок .. на основании ОБС ...


Eraser   (2005-05-20 13:59:00) [48]

Gek1

Как-то всё в твоём алгоритме перепутано.
Переработай алгоритм, чтобы приём данных осуществлялся так.

1. Чтение первых 4 байт - размер принимаемых данных.
2. Чтение потока данных, размер которых взят из п. 1.
3. Распаковка/дефифровка потока данных считанного из п. 2.

Куда уж проще.


Digitman   (2005-05-20 13:42:00) [49]


> Gek1   (20.05.05 13:28) [54]


бред какой-то...

какие нафих "пакеты" ? Хаффман не знает никаких "пакетов" !
Хаффман сжимает некий блок инф-ции (ему по барабану, пакеты там или не пакеты), а сервер тебе передает то что выдал Хаффман...

ГДЕ в [54] обращение к тем самым "первым 2-ум байтам" ? ... покажи мне ! ... ткни меня носом ...


Gek1   (2005-05-20 13:28:00) [50]

Вот в этом коде я проверяю блок данных на целостность пакетов:


{Далее проверяем чтобы в буффере было 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)}


Digitman   (2005-05-20 13:02:00) [51]

в [5] - "кошмар" (см. [6])

ГДЕ в [5] интерпретация первых 2-х байт чего-то там ? ткни меня носом ....


Gek1   (2005-05-20 11:35:00) [52]


> и как ты интерпретируешь и используешь содержимое этих первых
> 2-х байт ?

Смотри сам алогритм из [5]

А также ф-цию определения длинны пакета:

function TCharacter.GetPacketLen(PacketObj:PArray; Size : integer) : Integer;
{Ф-ция вернет:
0 если пакет маленький и надо еще кусок
-1 если пакет неизвестный
длинну, если длина >= пакету}
var PacketID : Word;
   PacketLen : Word;
   ResSize : Integer;
const
PacketLengths : array[0..255] of Word =
{Если длинна равна $00, то неизвестный пакет
Если длинна >= $8000 - значит определяем динамическую длинну}
( { $00 } $0068, $0005, $0007, $8000, $0002, $0005, $0005, $0007, $000E, $0005, $000B, $010A, $8000, $0003, $8000, $003D,
{ $10 } $00D7, $8000, $8000, $000A, $0006, $0009, $0001, $8000, $8000, $8000, $8000, $0025, $8000, $0005, $0004, $0008,
{ $20 } $0013, $0008, $0003, $001A, $0007, $0014, $0005, $0002, $0005, $0001, $0005, $0002, $0002, $0011, $000F, $000A,
{ $30 } $0005, $0001, $0002, $0002, $000A, $028D, $8000, $0008, $0007, $0009, $8000, $8000, $8000, $0002, $0025, $8000,
{ $40 } $00C9, $8000, $8000, $0229, $02C9, $0005, $8000, $000B, $0049, $005D, $0005, $0009, $8000, $8000, $0006, $0002,
{ $50 } $8000, $8000, $8000, $0002, $000C, $0001, $000B, $006E, $006A, $8000, $8000, $0004, $0002, $0049, $8000, $0031,
{ $60 } $0005, $0009, $000F, $000D, $0001, $0004, $8000, $0015, $8000, $8000, $0003, $0009, $0013, $0003, $000E, $8000,
{ $70 } $001C, $8000, $0005, $0002, $8000, $0023, $0010, $0011, $8000, $0009, $8000, $0002, $8000, $000D, $0002, $8000,
{ $80 } $003E, $8000, $0002, $0027, $0045, $0002, $8000, $8000, $0042, $8000, $8000, $8000, $000B, $8000, $8000, $8000,
{ $90 } $0013, $0041, $8000, $0063, $8000, $0009, $8000, $0002, $8000, $001A, $8000, $0102, $0135, $0033, $8000, $8000,
{ $a0 } $0003, $0009, $0009, $0009, $0095, $8000, $8000, $0004, $8000, $8000, $0005, $8000, $8000, $8000, $8000, $000D,
{ $b0 } $8000, $8000, $8000, $8000, $8000, $0040, $0009, $8000, $8000, $0003, $0006, $0009, $0003, $8000, $8000, $8000,
{ $c0 } $0024, $8000, $8000, $8000, $0006, $00CB, $0001, $0031, $0002, $0006, $0006, $0007, $8000, $0001, $8000, $004E,
{ $d0 } $8000, $0002, $0019, $8000, $8000, $8000, $8000, $8000, $8000, $010C, $8000, $8000, $0009, $0000, $0000, $0000,
{ $e0 } $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000,
{ $f0 } $8000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0004 );
begin
{Если размер нулевой, то выходим}
if Size = $00 then
   begin
   Result := 0;
   Exit;
   end;
PacketID := PacketObj^[0];
PacketLen := PacketLengths[PacketID];
{Если пакет неизвестный, то вернем - 1}
if PacketLen = $00 then
   begin
   Result := -1;
   Exit;
   end;
{Если динамическая длинна, то посчитаем ее
или выдадим 0 если нехватает данных}
if PacketLen >= $8000 then
begin
if Size > 3 then
   begin
   ResSize := strtoint('$'+IntToHex(PacketObj^[1],2)+IntToHex(PacketObj^[2],2));
   {Если пакет неполный, то выдадим 0}
   if ResSize <= Size then Result := ResSize
   else Result := 0; {if ResSize > Size}
   end
else Result := 0; {if Size < 3}
end
else {Иначе выдаем фиксированную длинну пакета}
   begin
   ResSize := PacketLen;
   {Если пакет неполный, то выдадим 0}
   if ResSize <= Size then Result := ResSize
   else Result := 0; {if ResSize > Size}
   end;
end;


Digitman   (2005-05-20 11:31:00) [53]


> первые пару байт получатся правильными


и как ты интерпретируешь и используешь содержимое этих первых 2-х байт ?


Gek1   (2005-05-20 11:29:00) [54]

Digitman ©   (20.05.05 11:26) [49]
Именно. Иначе данные будут испорчены.
Но как я понял практически всегда первые пару байт получатся правильными.
К сожалению на это мне приходится расчитывать, потому как альтернативы не нашел.


Digitman   (2005-05-20 11:26:00) [55]

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


Gek1   (2005-05-20 11:24:00) [56]

PHuffDecompress как и FincomingBuf имееют размер 150к.


> но лучше выбрать его кратным 4к

Это да. не подумал. Сделаю все кратными 4к.


Digitman   (2005-05-20 11:12:00) [57]


> Могу. первый всмысле TempBuf


про TempBuf вообще речи не идет.
вот TempBuf как раз можно и от балды выбрать
но лучше выбрать его кратным 4к

речь же идет о PHuffDecompress, т.е. буфере, в который Хаффман-декомпрессор записывает результат ...


Gek1   (2005-05-20 10:35:00) [58]


> какой "первый" ? ты оперировать конкретными идентификаторами
> в своем коде можешь ?

Могу. первый всмысле TempBuf.


> т.е. от балды

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

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


Digitman   (2005-05-20 10:27:00) [59]


> Первый буфер


какой "первый" ? ты оперировать конкретными идентификаторами в своем коде можешь ?


> непроверенному факту, что длинна пакета не может быть больше
> 0х8000


т.е. от балды


> густонаселенной местности


что еще за "местность" ?
какое отношение имеет к алгоритму компрессии/декомпрессии ?


Gek1   (2005-05-20 09:37:00) [60]


> Digitman ©   (19.05.05 18:38) [43]

Я бы не сказал, что от фоноря...

Взяты были с запасом.

Первый буфер был выбран по непроверенному факту, что длинна пакета не может быть больше 0х8000.
Поэтому взял с запасом и сделал 50к.

Второй (собирающий) буфер (fIncomingBuf) был выбран из расчета густонаселенной местности. Было определен примерный обьем и расчитанно примерно максимально возможное его значение. Все эти подсчеты относительны, но взяты (на всякий пожарный) с запасом.


Страницы:   1 2 3 4 5 6

Наверх

Титульная страница Поиск, карта сайта Написать письмо