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-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 пакеты или пакеты протокол обмена...
Сжимается каждый пакет отдельно, или весь поток?

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


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

Наверх

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