Форум: "Сети";
Текущий архив: 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.037 c