ReceiveBuf Delphi7
От: molinero  
Дата: 31.05.11 09:31
Оценка:
Блин, народ разъясните кому не лень. Ни как не пойму...


Клиент отправляет сразу после подключения:

type
TPrefix=record
CMD: Integer;
Size: Integer;
end;

procedure TfMain.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
var Prefix: TPrefix;
ClientNo: String;
begin
if Socket.Connected then
begin
Prefix.CMD:=CMD_LOGIN;
Prefix.Size:=6;
ClientSocket1.Socket.SendBuf(Prefix,SizeOf(Prefix));
ClientNo:=C_CLIENTNO;
ClientSocket1.Socket.SendBuf(ClientNo,Prefix.Size);
end;
end;

сервер принимает:

procedure TForm1.ServerClientRead(Sender: TObject; Socket: TCustomWinSocket);
var ClientNo: String;
SocketData: TclAssoc;
Prefix: TPrefix;
begin
Socket.ReceiveBuf(Prefix,Socket.ReceiveLength);
case Prefix.CMD of
{CMD_LOGIN} 0:begin
SetLength(ClientNo,Prefix.Size);
Socket.ReceiveBuf(ClientNo,Prefix.Size);
ShowMessage('CMD_LOGIN');
ShowMessage(ClientNo);
ShowMessage(IntTosTr(Length(ClientNo)));
end;
2:;
end;
end;

При вызове SendBuf(ClientNo...) доставляется все как задумано
При вызове SendBuf(ClientNo...) на сервере принимается какая то лабуда...
Re: ReceiveBuf Delphi7
От: Аноним  
Дата: 31.05.11 11:06
Оценка:
Здравствуйте, molinero, Вы писали:

M>Блин, народ разъясните кому не лень. Ни как не пойму...


M>Клиент отправляет сразу после подключения:


M>type

M> TPrefix=record
M> CMD: Integer;
M> Size: Integer;
M> end;

Во-первых,
PPrefxix = ^TPrefix;
TPrefix = packed record
CMD: Integer;
Size: Integer;
end;


Во-вторых,
кто обещал что Socket.ReceiveBuf(Prefix,Socket.ReceiveLength);
примет ровно SizeOf(TPrefix ) байт?
может придти и меньше и больше,
в случае меньше — накапливаем в буфер, пока не придет как минимум сколько нужно,
как дождались или в случае больше или равно
накладываем на буфер указатель (PPrefxix) структуры, читаем структуру,
продвигаемся по буферу (или откусываем голову)

M>При вызове SendBuf(ClientNo...) на сервере принимается какая то лабуда...
Re[2]: ReceiveBuf Delphi7
От: Аноним  
Дата: 01.06.11 04:59
Оценка:
А>в случае меньше — накапливаем в буфер, пока не придет как минимум сколько нужно,
А>как дождались или в случае больше или равно
А>накладываем на буфер указатель (PPrefxix) структуры, читаем структуру,
А>продвигаемся по буферу (или откусываем голову)

А как в буфер то напкопить?

var Buf: Pointer;
Prefix: TPrefix;
ReceiveLength: Integer;
begin
ReceiveLength:=0;
if Socket.ReceiveLength>=SizeOf(TPrefix)then
Socket.ReceiveBuf(Buf^,SizeOf(TPrefix))
else
begin
while ReceiveLength<=SizeOf(Tprefix)do
begin
Socket.ReceiveBuf(Buf^,Socket.ReceiveLength);
ReceiveLength:=+Socket.ReceiveLength;
end;
end;
Prefix:=PTPrefix(Buf)^;

Если длать так, то Buf будет перезаписваться и указывать на адрес новой порции данных?
Как мне добавитьв буфер?
В Prefix записывается то что я отправил, но в конце процедуры ошибка доступа к памяти.
Re[3]: ReceiveBuf Delphi7
От: Аноним  
Дата: 01.06.11 09:51
Оценка: 3 (1)
Здравствуйте, Аноним, Вы писали:


А>>в случае меньше — накапливаем в буфер, пока не придет как минимум сколько нужно,

А>>как дождались или в случае больше или равно
А>>накладываем на буфер указатель (PPrefxix) структуры, читаем структуру,
А>>продвигаемся по буферу (или откусываем голову)

А>А как в буфер то напкопить?


А>var Buf: Pointer;

А> Prefix: TPrefix;
А> ReceiveLength: Integer;
А>begin
А> ReceiveLength:=0;
А> if Socket.ReceiveLength>=SizeOf(TPrefix)then
А> Socket.ReceiveBuf(Buf^,SizeOf(TPrefix))
А> else
А> begin
А> while ReceiveLength<=SizeOf(Tprefix)do
А> begin
А> Socket.ReceiveBuf(Buf^,Socket.ReceiveLength);
А> ReceiveLength:=+Socket.ReceiveLength;
А> end;
А> end;
А> Prefix:=PTPrefix(Buf)^;

А>Если длать так, то Buf будет перезаписваться и указывать на адрес новой порции данных?

А>Как мне добавитьв буфер?
А>В Prefix записывается то что я отправил, но в конце процедуры ошибка доступа к памяти.

Правильно, Вы ведь буфер не аллоцируете нигде.
Пользуясь для простоты строкой как буфером (не самое плохое решение, между прочим), делаем примерно так:



type
  TclAssoc = ... ваша структура или объект, ассоциированный с сокетом
     ...
    sInputBuffer : ansistring;
     ...
  end;

type
  PPrefix=^TPrefix;
  TPrefix = packed record
    CMD   : Integer;
    Size  : Integer;  // я правильно понимаю, что это размер хвоста посылки после префикса?
  end;

procedure TForm1.ServerClientRead(Sender: TObject; Socket: TCustomWinSocket);
var 
  SocketData: TclAssoc;
  ptrPrefix: PPrefix;
  totalPackSize : cardinal;
  sReadPart: ansistring;
  sClientNo: ansistring;
begin
  // получаем ассоциированную с сокетом структуру данных
  SocketData := GetSocketDataBySocket(Socket);  <= тут я не знаю как у вас связь сделана

  // читаем кусок данных
  SetLength(sReadPart, Socket.ReceiveLength);
  Socket.ReceiveBuf(sReadPart[1], Length(sReadPart));

  // добавляем прочитанный кусок к общему буферу
  SocketData.sInputBuffer := SocketData.sInputBuffer + sReadPart;

  // разбираем принятые пакеты
  while (Length(SocketData.sInputBuffer) > 0) do begin

    // если данных совсем мало, даже на хидер не хватает, то перестанем 
    // тут крутиться и пойдем нафик, ждем когда дошлют еще данных
    if (Length(SocketData.sInputBuffer) < SizeOf(TPrefix)) then Break; 
    
    // заголовок пришел, проверим, весь-ли хвост имеем.
    // в принципе, можно обработать и только заголовок, но сейчас для
    // простоты будем ждать посылку целиком
    ptrPrefix := @SocketData.sInputBuffer[1];
    totalPackSize := (SizeOf(TPrefix) + ptrPrefix^.Size);
    if (Length(SocketData.sInputBuffer) < totalPackSize) then Break; 

    // если мы еще здесь, то как минимум одна посылка уже имеется целиком, обработаем
    case Prefix.CMD of
      {CMD_LOGIN} 0: begin
        sClientNo := Copy(SocketData.sInputBuffer, SizeOf(TPrefix) + 1, ptrPrefix^.Size);
        ShowMessage(Format('CMD_LOGIN %s %d', [sClientNo, Length(sClientNo)])); // не очень хорошо тут мессаджи показывать...
      end;
      2: ;
  
    end;

    // убрать обработанную посылку из буфера
    Delete(SocketData.sInputBuffer, 1, totalPackSize); 

    // тут возвращаемся к началу цикла - возможно в буфере есть еще посылки
  end;

end;
Re[4]: ReceiveBuf Delphi7
От: molinero  
Дата: 01.06.11 10:11
Оценка:
А> ShowMessage(Format('CMD_LOGIN %s %d', [sClientNo, Length(sClientNo)])); // не очень хорошо тут мессаджи показывать...

Такие сообщения я кое где вставляю для проверки результатов... А чем это чревато?

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

ClientSocket1.Socket.SendBuf(Prefix^,SizeOf(TPrefix));
ClientSocket1.Socket.SendBuf(ClientNo,Length(ClientNo));


То на приемнике возникнет два события OnClientRead? Или все же одно?
Re[4]: ReceiveBuf Delphi7
От: molinero  
Дата: 01.06.11 10:25
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Пользуясь для простоты строкой как буфером (не самое плохое решение, между прочим), делаем примерно так:


В качестве буфера можно использовать TMemoryStream?
Re[5]: ReceiveBuf Delphi7
От: Аноним  
Дата: 01.06.11 11:10
Оценка:
Здравствуйте, molinero, Вы писали:

А>> ShowMessage(Format('CMD_LOGIN %s %d', [sClientNo, Length(sClientNo)])); // не очень хорошо тут мессаджи показывать...


M>Такие сообщения я кое где вставляю для проверки результатов... А чем это чревато?


M>Я правильно понимаю, что если отправить два сообщения подряд, к примеру,


M>
M>ClientSocket1.Socket.SendBuf(Prefix^,SizeOf(TPrefix));
M>ClientSocket1.Socket.SendBuf(ClientNo,Length(ClientNo));
M>


M>То на приемнике возникнет два события OnClientRead? Или все же одно?


Это вам теорию почитать про ТСР протокол нужно.
Запись в поток, чтение из потока, фрагментация и там и там — вам практически неподвластна,
думайте о потоке байтов а не о пакетах, каждый сетевой уровень может разбивать-собирать
фрагменты по своим правилам, ваш вызов recv не обязательно даст вам столько сколько
запрошено, может и меньше.

TMemoryStream использовать можно, но будет немного затруднительно откусывать обработанную голову
вариант со строками вполне приемлим на небольших (до 4к) посылках, если больше и нужна
хорошая производительность — нужно разрабатывать альтернативные методы.
Например, кольцевой зеркальный, или массив или дерево буферов, но тут усложняется
манипулирование и доступ. Или комбинированный — строка для заголовков и стрим для хвоста.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.