Конференция "Сети" » winsock, о приеме данных [D7, WinXP]
 
  • Очень Злой (27.10.11 10:55) [20]

    > han_malign   (26.10.11 09:55) [16]
    >
    > а вот MSG_PEEK - это плохо:
    > MSG_PEEK:
    > Peeks at the incoming data. The data is copied into the
    > buffer, but is not removed from the input queue. The function
    > subsequently returns the amount of data that can be read
    > in a single call to the recv (or recvfrom) function, which
    > may not be the same as the total amount of data queued on
    > the socket. The amount of data that can actually be read
    > in a single call to the recv (or recvfrom) function is limited
    > to the data size written in the send or sendto function
    > call.
    >
    > Если пакет разобьется на несколько датаграмм(а это нормальная
    > ситуация на потоковых сокетах) - то "аля-улю - гони госей".
    > ..


    Или я неправильно перевел или хз, ибо получается что функция возвращает кол-во прочитанных данных и оно не может быть такое как общее кол-во данных в сокете?
    Какой тогда вообще смысл в MSG_PEEK ?

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


    > Омлет ©   (25.10.11 15:37) [11]
    >
    > > Очень Злой   (25.10.11 12:43) [9]
    > > Т.е. читать в буфер все что получили, а потом работать
    > с буфером и пакеты выделять из буфера?
    >
    > Да.
    >
    > > насколько это будет "лучше"?
    >
    > В чем тебе измерить? ))


    Ну я пока как новичек хочу научиться изначально делать "правильно",  и совета более опытных программистов вроде как достаточно.
  • Омлет © (27.10.11 11:01) [21]
    > Но посчитал что лишние операции и дополнительные буферы это не есть хорошо...

    Вызывать recv и ждать, пока там накапает на целый пакет - это и есть лишние операции. А считывать сразу всё, что пришло, в свой буфер - нормально. Главное, чтобы буфер был больше, чем максимальные размер пакета.
  • Сергей М. © (27.10.11 11:08) [22]

    > и оно не может быть такое как общее кол-во данных в сокете?


    returns an amount of data which can be read in a single recv; this may or may not be the same as the total amount of data queued on the socket.

    Это из справке к ioctlsocket/
  • Очень Злой (27.10.11 11:26) [23]

    > Омлет ©   (27.10.11 11:01) [21]
    >
    > > Но посчитал что лишние операции и дополнительные буферы
    > это не есть хорошо...
    >
    > Вызывать recv и ждать, пока там накапает на целый пакет
    > - это и есть лишние операции. А считывать сразу всё, что
    > пришло, в свой буфер - нормально. Главное, чтобы буфер был
    > больше, чем максимальные размер пакета.


    А разве я сказал, что я жду пока накапает пакет?

    у меня:
    ...
    if  WSAAsyncSelect(FSocket,FWindowHandle,WM_SOCKET_AUTH_EVENT, FD_CONNECT or FD_READ or FD_WRITE or FD_CLOSE)= SOCKET_ERROR then
    ...



    и если пришло сообщение с FD_READ, то я читаю из сокета данные целыми пакетами, и обрабатываю, и если при очередном чтении там нет целого пакета, то эти данные оставляю в сокете, чтобы прочитать в следующий раз...

    Или Вы хотите сказать, что если в сокете останутся данные, то даже при отсутствии новых данных все равно будет генерироваться сообщение для чтения?
  • Омлет © (27.10.11 11:38) [24]
    > Очень Злой   (27.10.11 11:26) [23]
    > Или Вы хотите сказать, что если в сокете останутся данные, то даже при отсутствии новых данных все равно будет генерироваться сообщение для чтения?


    Не будет.

    > данные оставляю в сокете, чтобы прочитать в следующий раз

    Какой в этом смысл, если тем же вызовом recv можно сразу забрать данные? Их ведь всё равно придется забирать, только уже не небольшими порциями.
  • Очень Злой (27.10.11 11:42) [25]

    > Какой в этом смысл, если тем же вызовом recv можно сразу
    > забрать данные? Их ведь всё равно придется забирать, только
    > уже не небольшими порциями.


    Ну просто я считал, что таким образом можно обойтись без дополнительного буфера и уменьшить кол-во операций по перемещению данных в памяти:
    т.е. кроме чтения данных из сокета в буфер придется еще переносить данные из буфера в другой буфер (предназначенный для хранения одного пакета), и кроме того перемещать данные в буфере влево по мере того, как они будут забираться оттуда...
  • Омлет © (27.10.11 11:47) [26]
    > Очень Злой   (27.10.11 11:42) [25]

    Зачем нужен буфер для одного пакета?
    Пришли данные - положили в буфер.
    Если накапало на пакет - весь пакет уже в буфере - его можно обрабатывать из этого же буфера, никуда не перемещая больше.
  • Омлет © (27.10.11 11:49) [27]
    Если обработка в отдельном потоке - блокируй запись в буфер, пока пакет из него не обработается.
  • Slym © (27.10.11 12:10) [28]
    Очень Злой   (27.10.11 11:42) [25]
    либо что-то плохое читал либо плохо читал
    1. recv(FSocket,Packet,Packet.len,MSG_PEEK) - копирует данные из буфера сокета в пользовательский буфер, т.е. ты не избежал операции чтения
    2. игнорировать сообщение плохо. сказано читай - а ты не буду
    3. есть другой способ узнать размер данных ioctlsocket(FSocket, FIONREAD, Longint(Len))
    4. делай в блоке и не парься
  • Очень Злой (27.10.11 12:29) [29]

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


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


    > 4. делай в блоке и не парься


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


    > 2. игнорировать сообщение плохо. сказано читай - а ты не
    > буду


    В смысле? Что я пропустил?

    Пока переписал так (еще не тестировал):

    ...
    private
       FBuffer: packed array[0..8191] of byte;
       FStartBuffer:integer;
       FEndBuffer:integer;
    ...

    procedure TMyClass.ReceivePackets;
    var
     s:Integer;
    begin
     s:=recv(FSocket,FBuffer[FEndBuffer],length(FBuffer)-FEndBuffer,0);

    // тут нужно обработать возможные ошибки.

     inc(FEndBuffer,s);
     while true do
       begin
         if FEndBuffer-FStartBuffer<2 then Break;
         move(FBuffer[FStartBuffer],Fpacket,2);
         if FPacket.len>sizeof(FPacket) then
           begin
             // Тут нужно закрыть сокет, и сгенерировать исключение
             // так как прием таких пакетов не планируется
             Exit;
           end;
         if FEndBuffer-FStartBuffer<Fpacket.len then Break;
         move(FBuffer[FStartBuffer],FPacket,Fpacket.len);
         inc(FstartBuffer,Fpacket.len);  // увеличиваем значение стартовой позиции буфера на величину забранных данных
         if FGame then ProcessingGame else ProcessingAuth;  // обработка выделенного пакета
        end;

      // Оптимизация буфера (чтобы обойтись без постоянных сдвигов буфера
      // сдвигать будем только при крайней необходимости)

      // Если буфер пустой, устанавливаем позиции начала и конца на 0
      if FStartBuffer>=FEndBuffer then
      begin
        FStartBuffer:=0;
        FEndBuffer:=0;
      end;
      // Оцениваем длину свободного хвоста буфера, если он менее 2 кб, то сдвигаем данные к началу буфера
      if length(FBuffer)-FEndBuffer<2048 then
      begin
        move(FBuffer[FStartBuffer],FBuffer[0], FEndBuffer-FStartBuffer);
        FEndBuffer:=FEndBuffer-FStartBuffer;
        FStartBuffer:=0;
      end;
    end;

  • Очень Злой (27.10.11 12:39) [30]
    Вобщем суть в том, что мне нужно последовательно принимать и обрабатывать некие пакеты данных. пакет представляет собой последовательность данных, где первые 2 байта содержат размер пакета (включительно с этими двумя байтами).
    Сервер не мой. И я не уверен что 1 дейтаграмма = 1 пакет. Поэтому исхожу из предположения что в 1 дейтаграмме может быть как 1 пакет так и несколько, а также что 1 пакет может содердаться в двух дейтаграммах,начало в одном, конец в другом.
  • han_malign (27.10.11 12:57) [31]

    > переносить данные из буфера в другой буфер (предназначенный для хранения одного пакета)

    - а зачем спрашивается хранить один пакет?
    res:= recv(FSocket,F_rgbBuf[F_cbDepth],sizeof(F_rgbBuf) - F_cbDepth,0);
    if( res > 0 )then begin
       inc(F_cbDepth, res);
       offs:= 0;
       while( (F_cbDepth >= sizeof(PPacket(@F_rgbBuf[offs]).len))  and
                (F_cbDepth >= PPacket(@F_rgbBuf[offs]).len)
       ) do begin
           _process(PPacket(@F_rgbBuf[offs])^);
           inc(offs, PPacket(@F_rgbBuf[offs]).len);
           dec(F_cbDepth, PPacket(@F_rgbBuf[offs]).len);
       end;
       if( (offs > 0) and (F_cbDepth > 0) )then
          move(F_rgbBuf[offs], F_cbDepth[0], F_cbDepth);
    end else
       ...


    - перемещаемые огрызки обычно достаточно малы, либо редки - и суммарный объем перемещаемой памяти заведомо меньше чем если читать два раза(либо бесконечное число раз, поскольку успешный MSG_PEEK - может(в зависимости от реализации winsock) каждый раз возвращать один и тот же размер(датаграммы) независимо от количества принятых данных)...
    При этом - при интенсивном обмене данными - за один
    recv

    может быть обработано несколько пакетов...
  • Очень Злой (27.10.11 13:08) [32]

    > - а зачем спрашивается хранить один пакет?

    Чтобы передать его в другую процедуру для обработки.


    >        _process(PPacket(@F_rgbBuf[offs])^);


    Насчет этого согласен. Как-то не подумал. Спасибо...


    > - перемещаемые огрызки обычно достаточно малы, либо редки
    > - и суммарный объем перемещаемой памяти заведомо меньше
    > чем если читать два раза.


    Похоже что да. Вы меня убедили.. :)
  • Сергей М. © (27.10.11 13:21) [33]
    function ReceiveDataBlock(hSocket: THandle; DataBlockSize: Integer; out DataBlock; out ErrorCode: Integer): Boolean;
    var
     i: Integer;
     BytesToRead, BytesRead: Integer;
    begin
     Result := False;
     i := 0;
     while i < DataBlockSize do begin
       BytesToRead := DataBlockSize - i;
       BytesRead := Recv(hSocket, PByteArray(@DataBlock)[i], BytesToRead, 0);
       Inc(i, BytesRead);
       if BytesRead > 0 then Continue;
       case BytesRead of
         0: ErrorCode := 0;
         SOCKET_ERROR: ErrorCode := WSAGetLastError;
       end;
       Exit;
     end;
     Result := True;
    end;

  • han_malign (27.10.11 13:58) [34]

    > Сервер не мой. И я не уверен что 1 дейтаграмма = 1 пакет.

    - размер датаграммы потокового сокета определяется сетевым окружением(включая шлюзы, маршрутизаторы и L3-комутаторы) и системными настройками(например - алгоритм Нага, QoS) , а не реализацией сервера...

    Размер датаграммы(для потокового сокета) обычно определяется максимальным размером Ethernet II пакета(1514-14{MAC}-20{IP}-20{TCP} = 1460) - во избежание лишних накладных расходов на обработку IP-фрагментов...
  • Омлет © (27.10.11 15:15) [35]
    > Очень Злой   (27.10.11 12:39) [30]

    Формат пакетов у вас не безопасный - при потере tcp-пакетов случится кака. Про udp вообще молчу )
  • Сергей М. © (27.10.11 15:29) [36]

    > Омлет ©   (27.10.11 15:15) [35]


    > при потере tcp-пакетов


    Стесняюсь спросить, и какая же "кака" в подлунном мире должна случиться, чтобы из трех подряд успешно отправленных по TCP прикладных пакетов A, B и С до получателя дошли только, скажем, A и C ?)
  • Очень Злой (27.10.11 15:35) [37]

    > Формат пакетов у вас не безопасный - при потере tcp-пакетов
    > случится кака. Про udp вообще молчу )


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

    Хотя о какой потере может идти речь в TCP-протоколе ?
  • Омлет © (27.10.11 15:40) [38]
    При обрыве связи, к примеру. Но это, конечно, смотря как логика восстановления соединения прописана.
  • Очень Злой (27.10.11 15:44) [39]

    > Стесняюсь спросить, и какая же "кака" в подлунном мире должна
    > случиться, чтобы из трех подряд успешно отправленных по
    > TCP прикладных пакетов A, B и С до получателя дошли только,
    >  скажем, A и C ?)


    Вобще-то такая кака не смотря ни на что все-таки возникала, при работе со стандартным клиентом. Но такое я замечал только 2 раза за последние 1,5 года, да и то в условиях ужасно паршивой связи (провайдера колбасило).
    была такая ситуация когда сервер не реагировал ни на какие команды клиента, но информация с сервера продолжала приходить и успешно обрабатывалась клиентом. Я только недавно понял из-за чего такой прикол вышел. Оказалось что принимаемый трафик и отправляемый шифруются разными ключами, и друг от друга не зависят. Т.е. непонятно как возникшее искажение данных передаваемых клиентом сделало полностью неработоспособным дальнейшую обработку этих данных на сервере, в то время как данные в обратном направлении продолжали нормально приходить и обрабатываться.
    Но такие ситуации чрезвычайно редки...
 
Конференция "Сети" » winsock, о приеме данных [D7, WinXP]
Есть новые Нет новых   [134435   +20][b:0][p:0.003]