Конференция "Сети" » ReceiveBuf. не могу разобраться как приавильно принять данные [D7, WinXP]
 
  • molinero (31.05.11 09:57) [0]
    Клиент отправляет сразу после подключения:

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

    procedure TfMain.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
    var Prefix: TPrefix;
       ClientNo: String[6];
    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[6];
       SocketData: TclAssoc;
       Prefix: TPrefix;
    begin
     Socket.ReceiveBuf(Prefix,Socket.ReceiveLength);
     case Prefix.CMD of
    {CMD_LOGIN} 0:begin
                   Socket.ReceiveBuf(ClientNo,Prefix.Size+1);
                   ShowMessage('CMD_LOGIN');
                   ShowMessage(ClientNo);
                   ShowMessage(IntTosTr(Length(ClientNo)));
                 end;
               2:;
     end;
    end;

    При первовызове SendBuf(Prefix...) доставляется все как задумано, при втором вызове SendBuf(ClientNo...) принимается 0 байт
    и ClientNo=''. А если указать что принимать нужно 8 байт, то длина ClientNo в итоге получится 48.
    Заметил такую штуку. Если установить брекпоинт в том месте где принимается ClientNo то все принимается нормально...
    Помогите разобраться почему? Я в ступоре...
  • han_malign (31.05.11 10:18) [1]

    >  Socket.ReceiveBuf(Prefix,Socket.ReceiveLength);

    - тогда уж Socket.ReceiveBuf(Prefix,sizeof(Prefix));
    т.к. в Socket.ReceiveLength - может быть уже весь пакет(и только чудом у тебя не запоролся стек, при переполнении буфера
    Prefix

    - размещенного на стеке)...

    > Socket.ReceiveBuf(ClientNo,Prefix.Size+1);

    - отправляешь Prefix.Size,  а принимаешь почему то Prefix.Size+1...
    при этом принимаешь и отправляешь ShortString в которой первый(и похоже лишний) символ - длинна строки...
  • molinero (31.05.11 10:38) [2]

    > т.к. в Socket.ReceiveLength - может быть уже весь пакет(и
    > только чудом у тебя не запоролся стек, при переполнении
    > буфера Prefix - размещенного на стеке)...


    Видимых результатов не дало... Я не пойму почему при брекпоинте я вижу то что отправил??
  • FireMan_Alexey © (31.05.11 14:41) [3]
    If Socket.ReceiveLength>=SizeOf(Prefix) Then
     Socket.ReceiveBuf(Prefix,SizeOf(Prefix));
    If Socket.ReceiveLength>=Prefix.Size Then
       {...
       Считываем что надо!!!! Хотя если размер буфера приема меньше передаваемых данных (по умолчанию 4КБ), то надо просто сохранять все что пришло в свой буфер!!!! Иначе ничего не получиться....
       ...}
  • FireMan_Alexey © (31.05.11 14:51) [4]
    Не кидайте тапками... Обшибся :)
    Не 4КБ, а 8 КБ... Давно к сетям не обращался...

    >molinero
    TCP - потоковый протокол, это не значит, что если пришло сообщение/событие о приходе данных, то в буфере 100% будет все что ты послал!!!!
    Т.е., гипотетически, даже твой префикс в 8 байт может приходить по 1 байту!
    И твой код должен быть готовым к принятию фрагментированных/частичных данных!!! В том числе и то, что конец одного ТВОЕГО пакета может быть склеен с началом другого пакета!!!
  • FireMan_Alexey © (31.05.11 15:26) [5]
    Как вариант, если собираешься посылать/принимать пакеты свыше 4 КБ (не рекомендуется посылать больше чем MaxSize(8192) div 2 пакеты с данными, может постоянно выдавать ошибку переполнения буфера...), то стоит сделать примерно так...

    Type
      PClientData=^TClientData;
      TClientData=Record
          Prefix:TPrefix;
          ReadBytes:Integer;
          prefixReady:Boolean;
          <Какой-нибудь буфер на твое усмотрение (Stream/Динамический массив)>
      End;



    В процедуре коннекта:

    var
      CData:PClientData;
    Begin
    ...
     New(CData);
     CData^.PrefixReady:=False;
     CData^.ReadBytes:=0;
     Socket.Data:=CData;
    ...
    End;



    В процедуре приема данных:

    Begin
    ...
      If (not Socket.Data^.PrefixReady)and(Socket.ReceiveLength>=SizeOf(TPrefix)) Then
     <читаем префикс и устанавливаем PrefixReady=True>
    Else
     If Socket.Data^.ReadBytes<Socket.Data^.prefix.Size then
      <Вычисляем кол-во байт, которые необходимо считать, и если считали все необходимое, сбрасываем PrefixReady=False и ReadBytes=0!!!! В принципе УСЕ :), дальше делай с данными все что хочешь ;)>
    ...
    End;
  • FireMan_Alexey © (31.05.11 15:32) [6]
    Да забыл в процедуре дисконнекта подчищаем созданную динам. запись:

    ...
     Dispose(Socket.Data);
    ...

  • FireMan_Alexey © (31.05.11 15:46) [7]
    Еще раз оговорюсь, сам заметил ошибку... :)

    If (Socket.Data^.PrefixReady)and(Socket.Data^.ReadBytes<Socket.Data^.prefix.Size) then

  • molinero (01.06.11 09:07) [8]
    FireMan_Alexey благодарю за участие.
    Я сделал немного подругому. Принимаю в буфер, а потом накладываю на буфер указатель (PTPrefix).

    type
      PTPrefix=^TPrefix;
      TPrefix=Packed record
         CMD: Integer;
         Size: Integer;
      end;

    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)^;

      В Prefix записывается то что я отправил, но в конце процедуры ошибка доступа к памяти. Нужно освободить памаять (Buf)?

      И еще, Если длать так, то Buf будет перезаписваться и указывать на адрес новой порции данных?
    Как мне добавитьв буфер?
  • Anatoly Podgoretsky © (01.06.11 09:25) [9]
    > molinero  (01.06.2011 09:07:08)  [8]

    В конце ReceiveLength становится равным 0
  • molinero (01.06.11 10:09) [10]
    С ошибкой доступа к памяти разобрался.
    Сначала GetMem(Buf,SizeOf(TPrefix))
    Когда буфер больше ненужен FreeMem(Buf)

    Дальше опять непонятно...
    На форуме прочитал, что если данные приходят не все сразу, нужно принимать в цикле.

    Отправляю

    procedure TfMain.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
    var Prefix: PTPrefix;
       ClientNo: String[6];
    begin
     if Socket.Connected then
       begin
         New(Prefix);
         Prefix^.CMD:=CMD_LOGIN;
         Prefix^.Size:=6;
         ClientNo:=C_CLIENTNO;
         ClientSocket1.Socket.SendBuf(Prefix^,SizeOf(TPrefix));//Отправляем размер и команду
         ClientSocket1.Socket.SendBuf(ClientNo,Length(ClientNo));//Отправляем номер клиента
         Dispose(Prefix);
       end;
    end;

    //принимаю

    Prefix:=PTPrefix(Buf)^;
     FreeMem(Buf);
     case Prefix.CMD of
    {CMD_LOGIN} 0:begin
                            //здесь я уже принял Prefix и теперь хочу принять номер клиента
                            GetMem(Buf,Prefix.Size);
                            ReceiveLength:=0;
                            if Socket.ReceiveLength>=Prefix.Size then//здесь ReceiveLength=0
                               Socket.ReceiveBuf(Buf^,Prefix.Size)
                            else
                               while ReceiveLength<=Prefix.Size do//и вот здесь зацикливается
                                  begin
                                     Socket.ReceiveBuf(Buf^,Socket.ReceiveLength);
                                     ReceiveLength:=+Socket.ReceiveLength;
                                  end;
                            s:=PClientNo(Buf)^;
                         end;
                      2:;
     end;

    Собственно вопрос. Как принимать в цикле, если на клиенте был вызван два раза подряд метод отправки данных?
  • Anatoly Podgoretsky © (01.06.11 14:11) [11]
    > molinero  (01.06.2011 10:09:10)  [10]

    А если один раз нажали, уже умеешь и правильно?
  • FireMan_Alexey © (02.06.11 00:12) [12]
    >molinero

    Чего то я ничего не понял...!!! :-\

    Если у тебя в параметре Socket.ReceiveLength не хватает цифры до длины твоего префикса, то забей и не читай ничего до следующего события прихода данных!!!
    Я для чего тебе написал вот это и как заполнять это поле ??????:

    Type
     PClientData=^TClientData;
     TClientData=Record
         Prefix:TPrefix;
         ReadBytes:Integer;
         prefixReady:Boolean;
         <Какой-нибудь буфер на твое усмотрение (Stream/Динамический массив)>
     End;



    Еще если ты собираешься в цикле писать данные из сокета в буфер, то какого лешего ты пишешь в одно и тоже место:

    while ReceiveLength<=SizeOf(Tprefix)do
              begin
                 Socket.ReceiveBuf(Buf^,Socket.ReceiveLength);
                 ReceiveLength:=+Socket.ReceiveLength{вот здесь всегда будет ноль как написал Анатолий};
              end;


    ReceiveBuf и SendBuf - это функции!!!!! Они возвращают кол-во посланных байт!!!!!!
    Даже если ты посылаешь/принимаешь 1 байт это не значит, что эти функции отработают на 100%!!! А как показано в твоем коде, ты не разу даже не поинтересовался ушли ли все данные у тебя или нет!!!!
  • FireMan_Alexey © (02.06.11 00:17) [13]
    Вот правильное принятие данных!
    RecSize:=Socket.ReceiveLength;
    Size:= Socket.ReceiveBuf(Buf^,RecSize);



    В нормальном случае Size=RecSize, а может быть и не равны!!!!
  • FireMan_Alexey © (02.06.11 00:24) [14]
    И зачем тебе динамический префикс?????!!!!!!

    Я тебе вот это показал:

    PClientData=^TClientData;

    Для того, чтобы ты смог использовать свойство Socket.Data: pointer!

    В твоем случае, когда на сервере у тебя будет не 1 клиент, то возникнет проблема как привязать данные к каждому клиенту!!!!!!!! По сути расписал тебе всю схему доставки! Только осталось вписать в обработчики событий!!!
  • FireMan_Alexey © (02.06.11 00:31) [15]
    >molinero

    Не обижайся, но это полная охинея!!!!

    procedure TfMain.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
    var Prefix: PTPrefix;
      ClientNo: String[6];
    begin
    if Socket.Connected then - Он точно законнектился, проверять не надо!!!!
      begin
        New(Prefix);  - Зачем это, сделай свой TPrefix, он все равно умрет в стеке при выходе из процедуры!!!!
        Prefix^.CMD:=CMD_LOGIN;
        Prefix^.Size:=6;
        ClientNo:=C_CLIENTNO;
        ClientSocket1.Socket.SendBuf(Prefix^,SizeOf(TPrefix)); - Где проверка, что данные ушли и сколько их ушло???
        ClientSocket1.Socket.SendBuf(ClientNo,Length(ClientNo));- здесь аналогично!!!
        Dispose(Prefix); - Убрать!!!!! Как и New выше!!!!
      end;
    end;

  • FireMan_Alexey © (02.06.11 00:33) [16]
    Да! Работай вот так!!!!

    Socket.SendBuf(Prefix^,SizeOf(TPrefix));



    а не

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

  • FireMan_Alexey © (02.06.11 00:43) [17]
    Далее на сервере:

    procedure TForm1.ServerSocket1ClientConnect(Sender: TObject;
     Socket: TCustomWinSocket);
    var
     CData:PClientData;
    Begin
    ...
    New(CData);
    CData^.PrefixReady:=False;
    CData^.ReadBytes:=0;
    Socket.Data:=CData;
    ...
    End;

    procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
     Socket: TCustomWinSocket);
    Begin
    ...
     If (not Socket.Data^.PrefixReady)and(Socket.ReceiveLength>=SizeOf(TPrefix)) Then
    <читаем префикс и устанавливаем PrefixReady=True>
    Else
    If Socket.Data^.ReadBytes<Socket.Data^.prefix.Size then
     <Вычисляем кол-во байт, которые необходимо считать, и если считали все необходимое, сбрасываем PrefixReady=False и ReadBytes=0!!!! В принципе УСЕ :), дальше делай с данными все что хочешь ;)>
    ...
    End;




    Повторился, но это  только для понимания!!!
  • FireMan_Alexey © (02.06.11 00:48) [18]
    procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject;
     Socket: TCustomWinSocket);
    begin
     Dispose(Socket.Data);
    end;

  • FireMan_Alexey © (02.06.11 01:32) [19]
    Совет! Почитай про динамические массивы!
    Функцию SetLength!
    Потому что, судя из выше написанного тобою, ты слабо разбираешься/понимаешь в работе указателей и адресации!

    Без обид! ;)
 
Конференция "Сети" » ReceiveBuf. не могу разобраться как приавильно принять данные [D7, WinXP]
Есть новые Нет новых   [134435   +14][b:0][p:0.003]