Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2004.04.11;
Скачать: [xml.tar.bz2];

Вниз

Что-то "прицепляется" к сокет-сообщению по дороге?   Найти похожие ветки 

 
AkaSaint ©   (2004-03-15 23:02) [0]

Есть 2 приложения, клиент и сервер, обмениваются сообщениями произвольной длины. Сокеты реализованы на стандартных Delphi6-компонентах TServerSocket и TClientSocket. Сокеты асинхронные (non-blocking режим). Клиент по своей инициативе
посылает серверу сообщение длиной в 10 байт. Сервер пишет отладочное сообщение: Socket.ReceiveLength=10 и затем считывает сообщение, обрабатывает и отправляет ответ. Клиент больше ничего не посылает, но сразу за 10-байтным сообщением сервер получает 3 сообщения длиной 0 байт каждое! (Под получение сообщения я имею в виду срабатывание обработчика OnClientRead объекта TServerSocket). Почему это может происходить, подскажите, пожалуйста!


 
AkaSaint ©   (2004-03-15 23:02) [0]

Есть 2 приложения, клиент и сервер, обмениваются сообщениями произвольной длины. Сокеты реализованы на стандартных Delphi6-компонентах TServerSocket и TClientSocket. Сокеты асинхронные (non-blocking режим). Клиент по своей инициативе
посылает серверу сообщение длиной в 10 байт. Сервер пишет отладочное сообщение: Socket.ReceiveLength=10 и затем считывает сообщение, обрабатывает и отправляет ответ. Клиент больше ничего не посылает, но сразу за 10-байтным сообщением сервер получает 3 сообщения длиной 0 байт каждое! (Под получение сообщения я имею в виду срабатывание обработчика OnClientRead объекта TServerSocket). Почему это может происходить, подскажите, пожалуйста!


 
Cobalt ©   (2004-03-15 23:26) [1]

Могу только порекомендовать посмотреть исходники TServerSocket, и найти - когда происходит это событие.
Ну а дальше читать Win32.hlp


 
Cobalt ©   (2004-03-15 23:26) [1]

Могу только порекомендовать посмотреть исходники TServerSocket, и найти - когда происходит это событие.
Ну а дальше читать Win32.hlp


 
Rouse_ ©   (2004-03-15 23:42) [2]

Приведи код


 
Rouse_ ©   (2004-03-15 23:42) [2]

Приведи код


 
AkaSaint ©   (2004-03-17 11:53) [3]

2Cobalt: посмотрел, когда возникает это сообщение: оно приходит в WndProc объекта TCustomWinSocket, который живет в недрах TServerSocket. Как я понимаю, что это значит, что оно приходит от Windows-сокетов и мне нужно читать про них?

Еще, я обнаружил странную вещь. Собственно, посылаемое клиентом сообщение - это запрос на авторизацию (логин и пароль) (длина сообщения 10 байт - т.к. логин и пароль не передаются, а заданы жестко на сервере, в целях отладки). Так вот, при запуске клиента, он коннектится к серверу и выдает окно для ввода логина и пароля. Я в сервере поставил точку останова в WndProc объекта TCustomWinSocket. Так вот, какие-то сообщения приходят в этот WndProc серверного сокета, если я переключюсь с клиентского приложения на другое, а затем снова на клиентское! Что это такое, можете вы мне объяснить?

2Rouse_:
Вот как отсылается сообщение клиентом:
...
   MStream := TMemoryStream.Create;
   Msg.ToStream(MStream);
   MStream.Position := 0;
   //ShowMessage("MStream.Size=" + IntToStr(MStream.Size));
   //Если откомментировать, будет показывать "10"
   Result := FSocket.Socket.SendStream(MStream);
...

Прием сообщения сервером:

procedure TBSServerSocket.FOnClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
   i: Integer;
begin
   Assert(Socket.Data <> nil);
   if FReading then
       Exit;
   FReading := True;
   try
   FCurClientSession := TBSClientSession(Socket.Data);
   try
       if Assigned(OnDebugMessage) then
           OnDebugMessage(Self, "ReceiveLength=" +                                     IntToStr(Socket.ReceiveLength));
       //При первом проходе этого места покажет "10", затем
       //от 0 до 3-х раз, если обработчик срабатывает, то
       //показывает "0"
       if not FBSOMessage.AssignFromSocket(Socket,
                                       FHangupTimer) then
       begin
           DropSocketBuffer(Socket);
           Exit;
       end;
   except
       on E: Exception do
       begin
           FCurClientSession.SendFailedBlockE(0, E);
       end;
   end;
   finally
       FReading := False;
   end;
end;


 
AkaSaint ©   (2004-03-17 11:53) [3]

2Cobalt: посмотрел, когда возникает это сообщение: оно приходит в WndProc объекта TCustomWinSocket, который живет в недрах TServerSocket. Как я понимаю, что это значит, что оно приходит от Windows-сокетов и мне нужно читать про них?

Еще, я обнаружил странную вещь. Собственно, посылаемое клиентом сообщение - это запрос на авторизацию (логин и пароль) (длина сообщения 10 байт - т.к. логин и пароль не передаются, а заданы жестко на сервере, в целях отладки). Так вот, при запуске клиента, он коннектится к серверу и выдает окно для ввода логина и пароля. Я в сервере поставил точку останова в WndProc объекта TCustomWinSocket. Так вот, какие-то сообщения приходят в этот WndProc серверного сокета, если я переключюсь с клиентского приложения на другое, а затем снова на клиентское! Что это такое, можете вы мне объяснить?

2Rouse_:
Вот как отсылается сообщение клиентом:
...
   MStream := TMemoryStream.Create;
   Msg.ToStream(MStream);
   MStream.Position := 0;
   //ShowMessage("MStream.Size=" + IntToStr(MStream.Size));
   //Если откомментировать, будет показывать "10"
   Result := FSocket.Socket.SendStream(MStream);
...

Прием сообщения сервером:

procedure TBSServerSocket.FOnClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
   i: Integer;
begin
   Assert(Socket.Data <> nil);
   if FReading then
       Exit;
   FReading := True;
   try
   FCurClientSession := TBSClientSession(Socket.Data);
   try
       if Assigned(OnDebugMessage) then
           OnDebugMessage(Self, "ReceiveLength=" +                                     IntToStr(Socket.ReceiveLength));
       //При первом проходе этого места покажет "10", затем
       //от 0 до 3-х раз, если обработчик срабатывает, то
       //показывает "0"
       if not FBSOMessage.AssignFromSocket(Socket,
                                       FHangupTimer) then
       begin
           DropSocketBuffer(Socket);
           Exit;
       end;
   except
       on E: Exception do
       begin
           FCurClientSession.SendFailedBlockE(0, E);
       end;
   end;
   finally
       FReading := False;
   end;
end;


 
Digitman ©   (2004-03-17 12:03) [4]

что творится в недрах методов AssignFromSocket и DropSocketBuffer ?

почему ни на той ни на другой стороне соединения не фигурирует обработка On[Client]Write ? без этих обработчиков работать как положено вся эта пертрушка у тебя никогда не будет


 
Digitman ©   (2004-03-17 12:03) [4]

что творится в недрах методов AssignFromSocket и DropSocketBuffer ?

почему ни на той ни на другой стороне соединения не фигурирует обработка On[Client]Write ? без этих обработчиков работать как положено вся эта пертрушка у тебя никогда не будет


 
AkaSaint ©   (2004-03-17 12:54) [5]

В недрах AssignFromSocket сообщение считывается путем нескольких вызовов Socket.ReceiveBuf. DropSocketBuffer считывает в никуда Socket.ReceiveLength байт из Socket.

Я разобрался, откуда брались эти "левые" сообщения о том, что пора читать данные. Думаю, это может пригодиться другим участникам форума.
Из MSDN: "...an application need not read all available data in response to an FD_READ message—a single recv in response to each FD_READ message is appropriate. If an application issues multiple recv calls in response to a single FD_READ, it can receive multiple FD_READ messages. Such an application can need to disable FD_READ messages before starting the recv calls by calling WSAAsyncSelect with the FD_READ event not set." У меня как раз в ответе на первый FD_READ было несколько recv в ReceiveBuf, которые считывали все сообщение, но после каждого recv посылалось уведомление, что данные еще есть.

Почему обязательно должны быть On[Client]Write? Почему просто нельзя вызвать ClientSocket.SendStream, например?


 
AkaSaint ©   (2004-03-17 12:54) [5]

В недрах AssignFromSocket сообщение считывается путем нескольких вызовов Socket.ReceiveBuf. DropSocketBuffer считывает в никуда Socket.ReceiveLength байт из Socket.

Я разобрался, откуда брались эти "левые" сообщения о том, что пора читать данные. Думаю, это может пригодиться другим участникам форума.
Из MSDN: "...an application need not read all available data in response to an FD_READ message—a single recv in response to each FD_READ message is appropriate. If an application issues multiple recv calls in response to a single FD_READ, it can receive multiple FD_READ messages. Such an application can need to disable FD_READ messages before starting the recv calls by calling WSAAsyncSelect with the FD_READ event not set." У меня как раз в ответе на первый FD_READ было несколько recv в ReceiveBuf, которые считывали все сообщение, но после каждого recv посылалось уведомление, что данные еще есть.

Почему обязательно должны быть On[Client]Write? Почему просто нельзя вызвать ClientSocket.SendStream, например?


 
Digitman ©   (2004-03-17 13:57) [6]


> В недрах AssignFromSocket сообщение считывается путем нескольких
> вызовов Socket.ReceiveBuf


на основании чего ты уверен, что факт возбуждения события On[Client]Read есть факт доступности целостного сообщения ? в этот момент в буфере приема может находиться и пол-сообщения, и одно целое сообщение, и полтора сообщения, и ...


> DropSocketBuffer считывает в никуда Socket.ReceiveLength
> байт из Socket


это еще зачем ? на основании чего ты уверен в такой необходимости ?

ты вообще-то читал комментарий к ReceiveLength в хэлпе ?


> Почему обязательно должны быть On[Client]Write? Почему просто
> нельзя вызвать ClientSocket.SendStream, например?


потому что эти события извещают тебя о готовности внутреннего передающего буфера к записи в него тобой данных


 
Digitman ©   (2004-03-17 13:57) [6]


> В недрах AssignFromSocket сообщение считывается путем нескольких
> вызовов Socket.ReceiveBuf


на основании чего ты уверен, что факт возбуждения события On[Client]Read есть факт доступности целостного сообщения ? в этот момент в буфере приема может находиться и пол-сообщения, и одно целое сообщение, и полтора сообщения, и ...


> DropSocketBuffer считывает в никуда Socket.ReceiveLength
> байт из Socket


это еще зачем ? на основании чего ты уверен в такой необходимости ?

ты вообще-то читал комментарий к ReceiveLength в хэлпе ?


> Почему обязательно должны быть On[Client]Write? Почему просто
> нельзя вызвать ClientSocket.SendStream, например?


потому что эти события извещают тебя о готовности внутреннего передающего буфера к записи в него тобой данных


 
AkaSaint ©   (2004-03-17 22:35) [7]

>на основании чего ты уверен, что факт возбуждения события >On[Client]Read есть факт доступности целостного сообщения ? в >этот момент в буфере приема может находиться и пол-сообщения, и >одно целое сообщение, и полтора сообщения, и ...

Я понимаю,что это может быть не так. Поэтому AssignFromSocket получает параметр FHangupTimer. В этом методе - цикл, который пытается считать нужное число байт так, чтобы между поступлением байтов проходилос не больше мс, чем FHangupTimer.Interval. Если время истекло, считается, что произошел сбой и метод возвращает False. Если там более чем одно сообщение, то оно будет считано при следующем срабатывании OnClientRead.

>это еще зачем ? на основании чего ты уверен в такой >необходимости ?
>
>ты вообще-то читал комментарий к ReceiveLength в хэлпе ?


Это мои интуитивные соображения. Если произошел сбой при приеме сообщения, я предполагаю, что в буфере может оставаться какая-то часть сообщения, которое не удалось принять. Понятно, что эту часть нужно выбросить. Также я опираюсь на то, что, клиент ожидает ответа на свой запрос довольно значительное время (в размере нескольких секунд), поэтому, очищая таким образом буфер, я не затрону повторные сообщения. Что касается комментария о том, что ReceiveLength может не точно отражать положение дел для потоковых сокетов, то - что же делать? Предложи свой вариант, что делать в случае ошибки при приеме сообщения.

>потому что эти события извещают тебя о готовности внутреннего передающего буфера к записи в него тобой данных

Справедливое замечание, я невнимательно читал хелпы к ним.


 
AkaSaint ©   (2004-03-17 22:35) [7]

>на основании чего ты уверен, что факт возбуждения события >On[Client]Read есть факт доступности целостного сообщения ? в >этот момент в буфере приема может находиться и пол-сообщения, и >одно целое сообщение, и полтора сообщения, и ...

Я понимаю,что это может быть не так. Поэтому AssignFromSocket получает параметр FHangupTimer. В этом методе - цикл, который пытается считать нужное число байт так, чтобы между поступлением байтов проходилос не больше мс, чем FHangupTimer.Interval. Если время истекло, считается, что произошел сбой и метод возвращает False. Если там более чем одно сообщение, то оно будет считано при следующем срабатывании OnClientRead.

>это еще зачем ? на основании чего ты уверен в такой >необходимости ?
>
>ты вообще-то читал комментарий к ReceiveLength в хэлпе ?


Это мои интуитивные соображения. Если произошел сбой при приеме сообщения, я предполагаю, что в буфере может оставаться какая-то часть сообщения, которое не удалось принять. Понятно, что эту часть нужно выбросить. Также я опираюсь на то, что, клиент ожидает ответа на свой запрос довольно значительное время (в размере нескольких секунд), поэтому, очищая таким образом буфер, я не затрону повторные сообщения. Что касается комментария о том, что ReceiveLength может не точно отражать положение дел для потоковых сокетов, то - что же делать? Предложи свой вариант, что делать в случае ошибки при приеме сообщения.

>потому что эти события извещают тебя о готовности внутреннего передающего буфера к записи в него тобой данных

Справедливое замечание, я невнимательно читал хелпы к ним.


 
Digitman ©   (2004-03-18 08:57) [8]


> Если время истекло, считается, что произошел сбой


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

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


> что же делать? Предложи свой вариант, что делать в случае
> ошибки при приеме сообщения


ошибкой следует считать два случая :

1. при вызове любого recv-метода возникла искл.ситуация ESocketConnectionError
2. после приема целостного сообщения проверка его на корректность в соответствии с ПИО дала отриц.результат

в обоих случаех следует немедленно закрыть свою сторону соединения с освобождением всех задействованных в нем ресурсах

следует организовать буферизацию вх.потока данных
событии же On[Client]Read следует считывать из буфера приема ровно то число байт, сколько ожидается в соответствии с ПИО для приема оставшихся байт очередного ожидаемого целостного сообщения.. прием целостного сообщения может быть осуществлен и за более чем одно событие On[Client]Read, для того и нужна буферизация вх.потока ..

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

следует реализовать KeepAlive-механизм, например, на основе таймера.. в каждом событии On[Client]Read следует обнулять сч-к таймера, в каждом событии таймера посылать партнеру сообщение-"щуп" и инкрементировать сч-к .. при возникновении исключения при посылке "щупа" следует немедленно закрыть соединение и освободить ресурсы.. при превышении сч-ком заданного значения можно также закрыть соединение, если за весь период "прощупывания" от партнера не было ни одного ответного сообщения


 
Digitman ©   (2004-03-18 08:57) [8]


> Если время истекло, считается, что произошел сбой


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

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


> что же делать? Предложи свой вариант, что делать в случае
> ошибки при приеме сообщения


ошибкой следует считать два случая :

1. при вызове любого recv-метода возникла искл.ситуация ESocketConnectionError
2. после приема целостного сообщения проверка его на корректность в соответствии с ПИО дала отриц.результат

в обоих случаех следует немедленно закрыть свою сторону соединения с освобождением всех задействованных в нем ресурсах

следует организовать буферизацию вх.потока данных
событии же On[Client]Read следует считывать из буфера приема ровно то число байт, сколько ожидается в соответствии с ПИО для приема оставшихся байт очередного ожидаемого целостного сообщения.. прием целостного сообщения может быть осуществлен и за более чем одно событие On[Client]Read, для того и нужна буферизация вх.потока ..

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

следует реализовать KeepAlive-механизм, например, на основе таймера.. в каждом событии On[Client]Read следует обнулять сч-к таймера, в каждом событии таймера посылать партнеру сообщение-"щуп" и инкрементировать сч-к .. при возникновении исключения при посылке "щупа" следует немедленно закрыть соединение и освободить ресурсы.. при превышении сч-ком заданного значения можно также закрыть соединение, если за весь период "прощупывания" от партнера не было ни одного ответного сообщения


 
AkaSaint ©   (2004-03-20 12:02) [9]

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


Спасибо, принял к сведенияю. Про KeepAlive тоже.

Что такое ПИО, объясни, пожалуйста.

Еще такой вопрос (в MSDN ответа я не нашел): когда посылается сообщение On[Client]Write неблокируемому сокету? Точнее, после каких действий в моей программе требуется ожидать On[Client]Write, чтобы с максимальной вероятностью рассчитывать на успешную передачу информации другой стороне?

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


 
AkaSaint ©   (2004-03-20 12:02) [9]

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


Спасибо, принял к сведенияю. Про KeepAlive тоже.

Что такое ПИО, объясни, пожалуйста.

Еще такой вопрос (в MSDN ответа я не нашел): когда посылается сообщение On[Client]Write неблокируемому сокету? Точнее, после каких действий в моей программе требуется ожидать On[Client]Write, чтобы с максимальной вероятностью рассчитывать на успешную передачу информации другой стороне?

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


 
AkaSaint ©   (2004-03-20 12:21) [10]

Этот пост - против глюка форума.


 
AkaSaint ©   (2004-03-20 12:21) [10]

Этот пост - против глюка форума.


 
Digitman ©   (2004-03-22 09:34) [11]


> Что такое ПИО


Протокол Информационного Обмена (в дан.случае - прикладного уровня)


> когда посылается сообщение On[Client]Write неблокируемому
> сокету?


минимум - однократно, сразу после установления вирт.петли соединения с партером.. означает готовность к передаче данных

впоследствии - всякий раз, когда буфер передачи освободился и готов к записи в него очер.блока данных .. событие тесно связано с получением WSAEWOULDBLOCK в рез-те попытке выполнить очередной send данных (непосредственно перед этим), размер которых превышает размер своб.области в буфере передачи

http://book.itep.ru, см.раздел "Winsock"


 
Digitman ©   (2004-03-22 09:34) [11]


> Что такое ПИО


Протокол Информационного Обмена (в дан.случае - прикладного уровня)


> когда посылается сообщение On[Client]Write неблокируемому
> сокету?


минимум - однократно, сразу после установления вирт.петли соединения с партером.. означает готовность к передаче данных

впоследствии - всякий раз, когда буфер передачи освободился и готов к записи в него очер.блока данных .. событие тесно связано с получением WSAEWOULDBLOCK в рез-те попытке выполнить очередной send данных (непосредственно перед этим), размер которых превышает размер своб.области в буфере передачи

http://book.itep.ru, см.раздел "Winsock"



Страницы: 1 вся ветка

Форум: "Сети";
Текущий архив: 2004.04.11;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.56 MB
Время: 0.054 c
8-1075957443
Настенька
2004-02-05 08:04
2004.05.09
mp3


4-1079637405
3APA3A
2004-03-18 22:16
2004.05.09
Найти процесс...


6-1079884056
Asail
2004-03-21 18:47
2004.05.09
Как выполнить удаленный Restart/Shutdown в Delphi?


14-1081947518
bigsnake
2004-04-14 16:58
2004.05.09
Windows работает в другом измерении?


8-1076746407
iudjen
2004-02-14 11:13
2004.05.09
только height или witdth на TImage





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский