Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.04.11;
Скачать: CL | DM;

Вниз

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

 
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


 
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;


 
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, например?


 
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 может не точно отражать положение дел для потоковых сокетов, то - что же делать? Предложи свой вариант, что делать в случае ошибки при приеме сообщения.

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

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


 
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:21) [10]

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


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


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


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


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


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

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

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



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

Текущий архив: 2004.04.11;
Скачать: CL | DM;

Наверх




Память: 0.52 MB
Время: 0.046 c
6-1079600116
Артут
2004-03-18 11:55
2004.04.11
компонента для HTTP


3-1079068666
Gerderic
2004-03-12 08:17
2004.04.11
SQL &amp; FireBird


7-1075519880
Прямой
2004-01-31 06:31
2004.04.11
Как прочитать сектор ?


6-1075699593
СергейМ
2004-02-02 08:26
2004.04.11
Определить сетевое имя пользователя


1-1080102128
IlyaP
2004-03-24 07:22
2004.04.11
Нехватка памяти