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

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

Сокеты "склеивают" сообщения! [D7, WinXP]


Galiaf ©   (2005-09-20 20:31:00) 

Драсти. Проблема такая: если очень быстро послать сокетом текст 'привет' то придёт он в таком виде 'приветпривет' :(.
Не катит!
Помогите с проблемой разобраться, как отправлять что бы не склеивало, потому что отправлять придётся много и быстро, это будет использоваться в сетевой игре и отсылать буду очень важные данные.


kami   (2005-09-21 01:09:00) [1]

Упс...
 Сразу послеrepeat :
MyStream.Position:=0;


kami   (2005-09-21 01:04:00) [2]

> но страшно

А чего тут страшного ? Сам пару месяцев назад так думал.

Пример...
Предполагаю, что соединение ClientSocket - ServerSocket уже установлено, отправка производится с клиента на сервер, тогда :

type
 TMyData=record
   Length:Word;// здесь будет храниться длина пересылаемой строки
   //---------- другая необходимая информация
   myText:string;
 end;
 //-----------------------------------
 MyStream:TMemoryStream;
 procedure MySendText(MyText:string);

// где создать MyStream - без понятия, например, в FormCreate, тогда уничтожить - в FormDestroy

procedure MySendText(MyText:string);
var
 MyData:TMyData;
begin
 MyData.Length:=Length(MyText);
 MyData.myText:=MyText;{в принципе,это все лишнее,если структура TMyData ограничится только этими полями}
 ClientSocket.Socket.SendBuf(MyData.Length);
 ClientSocket.Socket.SendBuf(MyData.myText[1],MyData.Length);
end;

{ пересылать вот так, непосредственно буфером тоже не совсем верно - при заполнении буфера самого сокета может не отослаться, реализация такой защиты в http://www.delphimaster.ru/cgi-bin/forum.pl?id=1122136315&n=4}

на сервере в OnClientRead:
var
 Buf:Array of byte;// временный массив, куда "запихнем" всё, что прислали с клиента
 Size:integer;// размер принятой информации
 MyData:TMyData;// сюда "складываем" принятые строки (по очереди)
 TempStream:TMemoryStream;//совсем временный поток :)
begin
 Size:=Socket.ReceiveLength;
 SetLength(Buf,Size);
 Socket.ReceiveBuf(Buf[0],Size);

 MyStream.Seek(0,soFromEnd)//добавляем данные в буфер FIFO, которым является MyStream
 MyStream.Write(Buf[0],Size);
 { теперь в MyStream содержатся все данные принятые прежде и не выбранные (по причине того, что приняты не полностью) + принятые сейчас в формате: длина строки | содержание строки}

 // начинаем выборку
 repeat
   if MyStream.Size=MyData.Length then
   { если запись, которую пытаемся выбрать, пришла полностью, то можно продолжить выборку текущей записи}
     begin
       SetLength(MyData.myText,MyData.Length);
       MyStream.Read(MyData.myText[1],MyData.Length);
       { запись считана, теперь нужно удалить ее из потока}
       { следующий код - далеко не лучшее решение, но сейчас в голову ничего не приходит}
       TempStream:=TMemoryStream.Create;
       TempStream.CopyFrom(MyStream,MyStream.Size-MyStream.Position);//скопируем все, кроме уже считанного
       MyStream.Clear;
       MyStream.LoadFromStream(TempStream);// и занесем обратно
       TempStream.Free;
       DoSomethingWithString(MyData.myText);// вместо этого - реальный обработчик пришедшей строки
     end
   else
     break;//строка, до которой дошли, пришла не полностью, будем ждать следующего пакета, чтобы дополнить недостающее
 until False;
end;


ЗЫ. Писалось по памяти, без Delphi, так что могут быть некоторые ошибки. Но общий принцип - примерно такой.


Delphi_is_cool   (2005-09-21 00:53:00) [3]

Ставь между сообщениями пробел ? )


Galiaf   (2005-09-20 22:06:00) [4]

kami ©   (20.09.05 21:29) [3]
Всё это звучит весьма достойно но страшно, я не очень разбираюсь в сетевых вопросах прошу доступный для понимания пример(желательно с пояснениями), спасибо.


XProger   (2005-09-20 22:01:00) [5]

http://xproger.mirgames.ru/files/lib/l_net.rar

Библиотека готового сетевого протокола основанного на UDP. Имеется реализация гарантированной доставки пакетов, а также широковещательные рассылки. Прилагается пример чата. Прирост к исполняемому файлу составляет 5.5 кб!


kami   (2005-09-20 21:29:00) [6]


> что бы не склеивало

1.Посылать перед каждым текстом его длину [ другую служебную информацию ]
2.Сохранять всёпринятое в конецMemoryStream
3.Просматривать с начала MemoryStream после каждого приема, выбирая пришедшие полностью сообщения и, соответственно, удаляя их из потока.

Это защитит не только от склеивания, но и от разбиения по пакетам.


Galiaf   (2005-09-20 21:22:00) [7]

Отправлял я по нажатию на Enter но без строки "ActiveControl := Edit13;", а что она делает?


KLAUS   (2005-09-20 20:47:00) [8]

А ты как вообще отправляешь?

procedure TForm1.Button1Click(Sender: TObject);
 begin

   ClientSocket1.Socket.SendText(Edit1.Text);
      Edit13.Text := '';
   ActiveControl := Edit13;
end;


Наверх

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