Конференция "Сети" » winsock, о приеме данных [D7, WinXP]
 
  • Очень злой (24.10.11 23:42) [0]
    1.Если я установлю требование генерации сообщений с помощью функции WSAAsyncSelect, то в какие моменты они будут происходить?
    Только при приходе от сервера новой порции данных или не только?

    2. нужно организовать прием и обработку данных от сервера, который посылает их в виде пакетов такого формата:
    первые 2 байта - длина пакета, остальное - собственно данные.

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


    type TPacket: packed record
     case boolean of
     true: (buf: array[0..8191] of byte;);
     false: (len:word;
             typepack:byte;);
     end;

    var
     Packet:TPacket;

    ...
    begin
     while true do
       begin
         if recv(FSocket,Packet,2,MSG_PEEK)<2 then Exit;
         if recv(FSocket,Packet,Packet.len,MSG_PEEK)<packet.len then Exit;
         recv(FSocket,Packet,Packet.len,0);
         Obrabotka(Packet); // функция обработки пакета
       end;
    end;



    Если так делать, то получается как-то "некрасиво" что-ли, из-за многократного дергания функции recv. Это нормально или лучше переписать по другому, если да, то как?
  • Rouse_ © (25.10.11 00:00) [1]
    1. Не только, т.к. будут приходить оконные сообщения, может произойти ситуация что сообщение посланное через HWND_BROADCAST может пойматься твоим ЦВС и если оно будет идентично тем, которые ты обрабатываешь, то...
    2. Такой сервер можно легко "положить" отправив FFFF первым словом. Для разделения пакетов? в случае бинарных данных? можно использовать Base64? используя в качестве разделителя байт, не входящий в последовательность BASE64
  • Очень злой (25.10.11 00:54) [2]
    Не, я пишу не сервер. Это у меня клиент. Т.е. тут я не изобретаю свой протокол обмена, а придерживаюсь того, с которым работает сервер.
    Просто протокол обмена большей частью описан в некоторых статьях в интернете, ну и частично я могу просниферить то, как с сервером общается стандартный клиент.
    Что касается буфера [0..8191], то этого более чем достаточно для приема любого из реальных пакетов посланных сервером. Защищать мой клиент от  FFFF или нечто подобного можно разве что на случай какого-нить сбоя, что вроде бы и невозможно,  но если такой сбой произойдет, то весь дальнейший обмен трафиком потеряет смысл, и можно будет дисконектить соединение. А защитить клиент от этого можно добавивши еще одну проверку.
  • Омлет © (25.10.11 08:03) [3]
    > Если так делать, то получается как-то "некрасиво" что-ли, из-за многократного дергания функции recv.

    Так делать неправильно.
    recv читает данные в буфер и возвращает размер прочитанных данных, либо число меньше нуля (тогда надо вызвать WSAGetLastError, чтобы обработать ошибку). Если recv вернул размер, равный размеру буфера, то надо читать дальше, пока не вернет размер меньше размера буфера или 0. Всё прочитанное последовательно складывается в буфер (а лучше сразу в него и считывать, передавая указатель на текущую позицию), затем там ищутся пакеты. По-хорошему, у пакетов должна быть сигнатура и контрольная сумма.

    > if recv(FSocket,Packet,Packet.len,MSG_PEEK)<packet.len then Exit;

    Прийти может 1 байт - и ты просто потеряешь его.
  • Омлет © (25.10.11 08:09) [4]
    > Омлет ©   (25.10.11 08:03) [3]

    А, пардно, кое в чем не прав. Не заметил флаг MSG_PEEK ))
    Но, всё равно, незачем лишний раз вызывать recv, если можно сразу считать данные.
  • Slym © (25.10.11 09:16) [5]
    я делаю по такому принципу

    function RecvBufFully(Peer:TSocket;var Buf; Count: Integer):boolean;
    var pBuf:PByte;
      s:integer;
    begin
      result:=false;
      pBuf:=@Buf;
      while count>0 do
      begin
        s:=recv(Peer,pBuf,Count,0);
        if s=0 then exit;
        inc(pBuf,s);
        dec(Count,s);
      end;
      result:=true;
    end;

    if RecvBufFully(FSocket,Packet.len,SizeOf(Packet.len)) then
    if not RecvBufFully(FSocket,Packet.typepack,Packet.len) then БитыйПакет;

  • Омлет © (25.10.11 09:38) [6]
    > Slym ©   (25.10.11 09:16) [5]

    У тебя нет проверки на s < 0, огребешь ведь..
    И что будет, если еще не все данные пришли? recv вернет 0, а через секунду придет остаток данных - ты же пакет потеряешь. Или, к примеру, придет сначала часть пакета..
  • Очень Злой (25.10.11 10:48) [7]

    > Омлет ©   (25.10.11 08:09) [4]
    >
    > > Омлет ©   (25.10.11 08:03) [3]
    >
    > А, пардно, кое в чем не прав. Не заметил флаг MSG_PEEK ))
    > Но, всё равно, незачем лишний раз вызывать recv, если можно
    > сразу считать данные.


    Вот и интересует как можно по-другому считать данные, если:

    // нам нужно считать только один пакет, чтобы обработать его,
    // но мы не знаем имеется ли в принятых данных целый пакет.
    // для начала пытаемся считать первых 2 байта (которые хранят длину пакета, включая и эти 2 байта)
    if recv(FSocket,Packet,2,MSG_PEEK)<2 then Exit; // если данных меньше 2-х байт - дальше не читаем.

    // длина пакета известна, проверяем или есть весь пакет целиком, если нет, то прекращаем обработку. будем ждать...
    if recv(FSocket,Packet,Packet.len,MSG_PEEK)<packet.len then Exit;

    // пакет есть целиком и мы его прочитали, однако нужно удалить эти данные в буфере сокета
    recv(FSocket,Packet,Packet.len,0);

    //вроде бы и лишние операции, но и без них не могу обойтись...

  • Сергей М. © (25.10.11 12:36) [8]

    > как можно по-другому считать данные


    Не использовать MSG_PEEK.
  • Очень Злой (25.10.11 12:43) [9]

    > Не использовать MSG_PEEK.


    Т.е. читать в буфер все что получили, а потом работать с буфером и пакеты выделять из буфера? насколько это будет "лучше"?
  • Сергей М. © (25.10.11 13:29) [10]
    Не "все что получили", а не более того сколько ожидается до окончания приема текущего принимаемого пакета.

    > насколько это будет "лучше"?

    Ну ты же сам сказал что

    > "некрасиво" ..из-за многократного дергания функции recv


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


    Да.

    > насколько это будет "лучше"?

    В чем тебе измерить? ))
  • Slym © (26.10.11 06:44) [12]
    Омлет ©   (25.10.11 9:38) [6]
    У тебя нет проверки на s < 0, огребешь ведь..

    уговорил

    uses RTLConsts;

    function RecvBufFully(Peer:TSocket;var Buf; Count: Integer):boolean;
    var pBuf:PByte;
     s,err:integer;
    begin
     result:=false;
     pBuf:=@Buf;
     while count>0 do
     begin
       s:=recv(Peer,pBuf,Count,0);
       if s=SOCKET_ERROR then
       begin
         err:=WSAGetLastError;
         raise Exception.CreateResFmt(@sWindowsSocketError, [SysErrorMessage(err), err, 'recv']);
       end else
         if s=0 then exit;// сокет клозед грацефули
       inc(pBuf,s);
       dec(Count,s);
     end;
     result:=true;
    end;

    if RecvBufFully(FSocket,Packet.len,SizeOf(Packet.len)) then
    if not RecvBufFully(FSocket,Packet.typepack,Packet.len) then БитыйПакет;

  • Омлет © (26.10.11 08:25) [13]
    > Slym ©   (26.10.11 06:44) [12]

    Так тоже нехорошо делать, т.к. ошибки-то разные бывают.
    Например, WSAEWOULDBLOCK.
  • Сергей М. © (26.10.11 09:06) [14]

    > Например, WSAEWOULDBLOCK


    WSAEWOULDBLOCK никогда не возникнет при вызове recv()
  • han_malign (26.10.11 09:46) [15]

    > WSAEWOULDBLOCK никогда не возникнет при вызове recv()

    - не надо обманывать - в неблокирующем режиме WSAEWOULDBLOCK - это обычное состояние, если перед recv не дожидаться данных с помощью select(0, readfds,...)

    If no incoming data is available at the socket, the recv call blocks and waits for data to arrive according to the blocking rules defined for WSARecv with the MSG_PARTIAL flag not set unless the socket is nonblocking. In this case, a value of SOCKET_ERROR is returned with the error code set to WSAEWOULDBLOCK. The select, WSAAsyncSelect, or WSAEventSelect functions can be used to determine when more data arrives.

    З.Ы. Если что
    recv() = 0

    - означает "элегантное закрытие" соединения...
  • 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.

    Если пакет разобьется на несколько датаграмм(а это нормальная ситуация на потоковых сокетах) - то "аля-улю - гони госей"...
  • Омлет © (26.10.11 10:00) [17]
    > Сергей М. ©   (26.10.11 09:06) [14]

    олала
    http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
  • Slym © (26.10.11 13:07) [18]
    han_malign   (26.10.11 9:46) [15]
    вот нафега новички лезут в неблокирующий режим?
    в блоке все по порядку никаких сообщений все линейно
  • Сергей М. © (26.10.11 13:19) [19]

    > han_malign   (26.10.11 09:46) [15]


    Согласен.
    Ляпнул не освежив в памяти сабж и поленившись приоткрыть справку)
  • Очень Злой (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 года, да и то в условиях ужасно паршивой связи (провайдера колбасило).
    была такая ситуация когда сервер не реагировал ни на какие команды клиента, но информация с сервера продолжала приходить и успешно обрабатывалась клиентом. Я только недавно понял из-за чего такой прикол вышел. Оказалось что принимаемый трафик и отправляемый шифруются разными ключами, и друг от друга не зависят. Т.е. непонятно как возникшее искажение данных передаваемых клиентом сделало полностью неработоспособным дальнейшую обработку этих данных на сервере, в то время как данные в обратном направлении продолжали нормально приходить и обрабатываться.
    Но такие ситуации чрезвычайно редки...
  • Сергей М. © (27.10.11 15:45) [40]

    > При обрыве связи, к примеру


    В TCP при обрыве ничего потеряться не может - все что успешно отправлено до момента обрыва, успешно и будет доставлено в той же последовательности.
  • Сергей М. © (27.10.11 15:50) [41]

    > такая кака не смотря ни на что все-таки возникала, при работе
    > со стандартным клиентом


    Эту каку подложил себе сам стандартный клиент, и TCP здесь совершенно ни причем.
    В поточном транспортном протоколе с квитированием доставки потери дейтаграмм, их искажения или нарушения последовательности их доставки попросту исключены.
  • Очень Злой (27.10.11 15:56) [42]

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


    ну не знаю... Насчет потери - согласен, а вот насчет искажения... даже не знаю...
    А чем определяется искажены данные или нет?
    Если контрольной суммой, то она также может быть искажена вместе с данными и теоретически возможны случаи, когда искаженная контрольная сумма будет соответствовать искаженным данным...
  • Сергей М. © (27.10.11 16:04) [43]

    > а вот насчет искажения


    Искажения на уровнях ниже прикладного отсечет сам стек TCP/IP.
    А за контроль искажений на прикладном уровне ответственны сами обменивающиеся прикладные стороны.
  • Anatoly Podgoretsky © (27.10.11 16:06) [44]
    > Очень Злой  (27.10.2011 15:56:42)  [42]

    Зачем контрольная сумма, MD5 хеш, CRC64, коды хеминга
  • Сергей М. © (27.10.11 16:07) [45]

    > чем определяется искажены данные или нет?


    Ну как чем ?
    Если принятые данные не соответствуют прикладному протоколу, значит они искажены.
    При идеальном прикл.следовании протоколу сос тороны приемника винить в искажении можно только либо передатчик либо "человека посередине". Либо и того и другого одновременно)
  • Сергей М. © (27.10.11 16:07) [46]

    > чем определяется искажены данные или нет?


    Ну как чем ?
    Если принятые данные не соответствуют прикладному протоколу, значит они искажены.
    При идеальном прикл.следовании протоколу сос тороны приемника винить в искажении можно только либо передатчик либо "человека посередине". Либо и того и другого одновременно)
  • han_malign (27.10.11 17:06) [47]

    > контрольная сумма, MD5 хеш, CRC64, коды хеминга

    - обычно достаточно контроля несущей(CSMA/CD), сбои ЦАП/АЦП относятся уже к условиям ядерной войны(либо феноменальной жадности при прокладке сегмента) - если не хватает 32-бит CRC Ethernet-пакета и 16-бит контрольной суммы TCP(не считая приличного количества контекстно-фиксированных бит в заголовках) - то спасет только тройное дублирование...
  • Slym © (28.10.11 08:25) [48]
    забудь про пакеты! для пользователя TCP-это поток данных, контроль над порядком и целостностью лежит на системе, потеряться или измениться порядок без злоумышленника не может
  • Slym © (28.10.11 08:28) [49]
    Очень Злой   (27.10.11 12:29) [29]
    В блоке я даже и не представляю пока как это сделать...

    как раз твоя писанина и напоминает блочную работу, но сокет установлен в неблок - и это не правильно
    в неблоке никаких ReceivePackets не должно быть, а должно быть OnRead и OnWrite
  • Slym © (28.10.11 09:21) [50]
    примерно так
    type
     TMyProtoReader=class
     private
       Socket:TClientSocket;
       Packet:Pointer;
     public
       constructor Create;
       destructor Destroy;override;
       function Connect:boolean;
       function GetPacket:Pointer;
     end;

    { TMyProtoReader }

    constructor TMyProtoReader.Create;
    begin
     Socket:=TClientSocket.Create(nil);
     Socket.ClientType:=ctBlocking;
    end;

    destructor TMyProtoReader.Destroy;
    begin
     Socket.Free;
     FreeMem(Packet);
     inherited;
    end;

    function TMyProtoReader.Connect: boolean;
    begin
     Socket.Address:='127.0.0.1';
     Socket.Port:=12345;
     Socket.Active:=true;
     result:=Socket.Active;
    end;

    function TMyProtoReader.GetPacket: Pointer;
    type
     PPacket=^TPacket;
     TPacket=packed record
       Len:word;
       data:array[0..0] of byte;
     end;

     function RecvBufFully(Peer:TCustomWinSocket;var Buf; Count: Integer):boolean;
     var pBuf:PByte;
       s:integer;
     begin
       result:=false;
       pBuf:=@Buf;
       while count>0 do
       begin
         s:=Peer.ReceiveBuf(pBuf^,Count);
         if s=0 then exit;
         inc(pBuf,s);
         dec(Count,s);
       end;
       result:=true;
     end;

    var len:word;
    begin
     if not Socket.Active then
       raise Exception.Create('Not connected');

     if not RecvBufFully(Socket.Socket,len,SizeOf(len)) then
     begin
       Socket.Active:=false;
       raise Exception.Create('Bad packet');
     end;

     ReallocMem(Packet,len);
     PPacket(Packet).Len:=len;

     if not RecvBufFully(Socket.Socket,PPacket(Packet).Data,PPacket(Packet).len) then
     begin
       Socket.Active:=false;
       raise Exception.Create('Bad packet');
     end;

     result:=Packet;
    end;

    procedure TForm1.Button1Click(Sender: TObject);
    var
     reader:TMyProtoReader;
     Packet:pointer;
    begin
     reader:=TMyProtoReader.Create;
     try
       reader.Connect;
       while true do
       begin
         Packet:=reader.GetPacket;
         ProcessPacket(Packet);
         Application.ProcessMessages;
       end;
     finally
       reader.Free;
     end;

    end;

  • han_malign (28.10.11 11:16) [51]

    > напоминает блочную работу, но сокет установлен в неблок - и это не правильно

    - приберегите свой свою категоричность(и откровенный говнокод) до времен - когда поймете что (non)blocking - означает (не)блокирующий, а не "блочный"...

    Блокирующий режим может быть использован в крайне ограниченном количестве сценариев - когда до получения определенного набора данных дальнейшее функционирование приложения невозможно...
    И при этом циклами не заморачиваются, а используют асинхронный ReadFile или асинхронный WSARecv...
  • Slym © (28.10.11 14:24) [52]
    han_malign   (28.10.11 11:16) [51]
    покажи пример неблочного режима и сравни с тем что Очень злой господин пишет...
    а пишет он неблокирующий сокет в блокирующей манере - где хоть одно сообщение OnRead?
    han_malign   (28.10.11 11:16) [51]
    Блокирующий режим может быть использован в крайне ограниченном количестве сценариев

    кто тебе сказал? Сокеты Беркли - блокирующие, весь линух на них, а микрософт к ним прицепил нативную для винды асинхронную модель сообщений
    есть один сценарий неприменимый для влоб блочного режима - высоконагруженный многоклиентский сервер где ограничение в колве потоков
    клиентскому сокету побарабану блок/неблок монописуально 1 поток
  • Slym © (28.10.11 14:27) [53]
    han_malign   (28.10.11 11:16) [51]
    приберегите свой свою категоричность(и откровенный говнокод) до времен - когда поймете что (non)blocking - означает (не)блокирующий, а не "блочный"...

    напиши на данную тему в неблокирующей манере свой пример и посмотрим чей код говнее
  • han_malign (28.10.11 17:15) [54]

    > где хоть одно сообщение OnRead?
    ...
    > Сокеты Беркли - блокирующие, весь линух на них

    - ссылку пожалуйста, на отсутствие неблокирующего режима в сокетах Беркли...

    > и посмотрим чей код говнее

    - Вы будете спорить, что код вешающий приложение до следующего входящего пакета и вываливающий исключение, если оно не пришло в течение getsockopt(SO_RCVTIMEO) - плохо пахнет?
  • Сергей М. © (28.10.11 19:56) [55]

    > микрософт к ним прицепил нативную для винды асинхронную
    > модель сообщений


    Он ее прицепил опираясь именно на неблокирующий режим как один из классических режимов гнезд Беркли)

    В Пердивикии по этому поводу есть, кстати, заметка:

    http://en.wikipedia.org/wiki/Berkeley_sockets#Blocking_vs._non-blocking_mode
  • Slym © (31.10.11 05:18) [56]
    han_malign   (28.10.11 17:15) [54]
    - Вы будете спорить, что код вешающий приложение до следующего входящего пакета и вываливающий исключение, если оно не пришло в течение getsockopt(SO_RCVTIMEO) - плохо пахнет?

    продолжаем обсирать... на этот раз обделан код никак не связаный с сабжом сокетами...
    СЛИВ ЗАСЧИТАН...
  • Slym © (31.10.11 05:24) [57]
    Сергей М. ©   (28.10.11 19:56) [55]
    тут я погорячился... примеров unix+nonblock я не встречал вот и сделал неправильный вывод
  • Очень Злой (31.10.11 11:11) [58]

    > в неблоке никаких ReceivePackets не должно быть, а должно
    > быть OnRead и OnWrite



    > а пишет он неблокирующий сокет в блокирующей манере - где
    > хоть одно сообщение OnRead?


    Почему?

    ...
    procedure SocketEvent(var M:TMessage); message WM_SOCKET_AUTH_EVENT;
    ...

      if  WSAAsyncSelect(FSocket,FWindowHandle,WM_SOCKET_AUTH_EVENT, FD_CONNECT or FD_READ or FD_WRITE or FD_CLOSE)= SOCKET_ERROR then

    ...

    procedure TXXX.SocketEvent(var M:TMessage);
    begin
     case M.LParam of
     FD_CONNECT:;
     FD_READ:     ReceivePackets;
     FD_WRITE:    SendPackets;
     FD_CLOSE:    CloseConnection;
     end;
    end;




    разве это в "блокирующей манере"?
  • Slym © (31.10.11 12:03) [59]
    Очень Злой   (31.10.11 11:11) [58]
    WSAAsyncSelect

    первое упоминание AsyncSelect...
  • Очень Злой (31.10.11 12:21) [60]

    > первое упоминание AsyncSelect...


    первое упоминание было в [0], но чуть детальнее в:

    > Очень Злой   (27.10.11 11:26) [23]


    Ну не привел я с самого начала весь код, ибо меня больше интересовала методика прием данных с сокетов  (с точки зрения транспортного уровня) и обработка пакетов (с точки зрения протокола прикладного уровня). Т.е. оптимальная организация такого процесса...
  • Очень Злой (02.11.11 12:47) [61]
    Вобщем написал я класс, который инкапсулирует в себе соединение с сервером, прохождение довольно сложной авторизации (на уровне прикладного протокола), после чего позволяет пользователю работать с сервером выполняя криптование исодящих пакетов и декриптование входящих.

    Но, если мне нужно создавать несколько соединений с сервером, то я должен создавать столько же объектов - экземпляров этого класса.
    На мой взгляд это расточительно с точки зрения использования ресурсов. Кроме того есть другие причины, по которым желательно было бы чтобы данный класс поддерживал не 1 соединение с сервером а несколько.
    Пока возникла проблема такого рода:
    Кроме дескриптора сокета, имеются другие данные привязанные к этому сокету (ключи шифрования, буфер для временного хранения неполных пакетов, и т.д.).
    Но при использовании WSAAsyncSelect оконная функция получает сообщение, в котором указывается дескриптор сокета и прочие данные, а вот как мне при этом передать и получить ссылку на остальные данные, привязанные к сокету?
  • Сергей М. © (02.11.11 19:29) [62]
    У тебя один-единственный поток обслуживает туеву хучу сокетов ?
  • Очень злой (02.11.11 20:00) [63]

    > Сергей М. ©   (02.11.11 19:29) [62]
    >
    > У тебя один-единственный поток обслуживает туеву хучу сокетов
    > ?


    Да. Ну не то чтобы туеву хучу, но сокетов 10-15
  • Сергей М. © (02.11.11 20:25) [64]
    > сокетов 10-15

    Смешная же цифирь)..

    И чего ж там такого "расточительного" ?
  • Очень злой (02.11.11 22:32) [65]

    > Смешная же цифирь)..


    Ну в принципе да.

    Я понимаю, что такие оптимизации в наше время не в моде, но все равно хочется сделать оптимальнее. Хотя бы не ради самого класса, а для усовершенствования своих знаний.
    Гуглить пробовал, но пока ничего не нашел, возможно потому, что не знаю как правильнее сформулировать поисковый запрос...
  • Сергей М. © (03.11.11 08:29) [66]
    imho, ради десятка-другого коннектов нет резона всерьез задумываться над оптимальностью.

    Пройтись в цикле по списку структур, ассоциированных с коннектами, в поисках нужного коннекта - это займет смехотворное время.

    Даже в TServerSocket над этим Борланд не задумывалась (хотя это вовсе и не показатель оптимальности) - в неблок.режиме там для каждого сокета, включая слушающий, создается отдельное окно (читай - выделяется немалый общесистемный ресурс). Таким образом в ущерб объему занимаемых ресурсов там упрощается диспетчеризация асинхронных сокетных событий, хотя можно было бы обойтись всего одним окном и беготней по списку Connections[].
 
Конференция "Сети" » winsock, о приеме данных [D7, WinXP]
Есть новые Нет новых   [134436   +21][b:0.001][p:0.007]