![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Проблема с использованием TServerSocket в ServiceApplication !!! [NT4, Win2k, WinXP]
AlexAn © (2002-08-19 09:42:00)Может кто-нибудь сталкивался с проблемой:
Серверный неблокирующий сокет работает в сервисе, при попытке клиента подключиться, соединение устанавливается от 10 до 20 сек., т.е. Accept срабатывет примерно после 15 сек. с момента вызова Connect у клиента. ?
Причем если весь этот алгоритм запихнуть не в TServiceApplication а в обычный TApplication, то проблем нет.
Еще есть один нюанс - с той же машины на которой стои сервис клиент соединяется моментально.
Если у кого есть какие-либо соображения по поводу, как решить эту проблему - мыльте плз.
AlexAn (2002-08-29 09:12:00) [1]Ну я вобщем как раз над этим и работаю, просто я хотел обойтись средствами Delphi не залезая в API.
Digitman (2002-08-28 11:49:00) [2]а зачем ему постоянно находиться в состоянии recv ? да и зачем ему обязательно в блок.режиме работать ? после создания гнезда и соотв трансп.потока в теле ClientExecute переведи гнездо в асинхронный неблок.режим вызовом WSAEventSelect и лови гнездовые нотификации в цикле MsgWaitForMultipleObjects ! При этом тр.поток большинство времени находится в состоянии ожидания имеет возможность мгновенно реагироватьь как на осн.трансп.события (FD_READ, FR_WRITE, FD_CLOSE), так и на внешние сообщения. А внешним соообщением ты можешь передать тр.потоку инф-цию о необходимости выполнить send с такими-то параметрами
AlexAn (2002-08-28 11:40:00) [3]хорошо, как отослать клиенту сообщение от сервера, когда сервер находится в состоянии recv если сокет блокирующий?
Digitman (2002-08-28 11:14:00) [4]а почему не воспользоваться штатным режимом TServerSocket.ServerType = stThreadBlocking ?
просто реализуй наследника TServerClientThread, тело ClientExecute которого будет полностью контролировать транспорт отдельного гнезда
кр.того, TServerWinSocket реализует кэширование транспотных кодовых потоков; твой же вариант совершенно не учитывает того факта, что всякий вызов CreateThread() имеет значительную время- и ресурсоемкость, и для системы это выглядит как старт некоего холодного двигателя в холодную же погоду - медленно и тяжело
см. события OnGetThread, OnGetSocket
AlexAn (2002-08-28 10:57:00) [5]дело в том, что я вынес цикл отсылки в отдельную нить:
unit thrd_wr;
interface
uses
Classes, ScktComp, MessageFrm, SysUtils, WinSock;
type
PSDstr = ^TSDstr;
TSDstr = record
r_str,
UserName: string;
NeedSend: Boolean;
Data: Pointer;
end;
TWriteThread = class(TThread)
public
FServerSocket: TServerSocket;
FSocket: TCustomWinSocket;
constructor Create(ServerSocket: TServerSocket; Socket: TCustomWinSocket);
procedure Execute; override;
end;
TWriteStrThread = class(TThread)
public
FSocket: TCustomWinSocket;
FText: string;
constructor Create(AText: string; Socket: TCustomWinSocket);
procedure Execute; override;
end;
implementation
{ TWriteThread }
constructor TWriteThread.Create(ServerSocket: TServerSocket; Socket: TCustomWinSocket);
begin
inherited Create(false);
FServerSocket := ServerSocket;
FSocket := Socket;
FreeOnTerminate := true;
end;
procedure TWriteThread.Execute;
var
i: Integer;
begin
FSocket.SendText("write data");
for i := 0 to FServerSocket.Socket.ActiveConnections - 1 do
begin
if FServerSocket.Socket.Connections[i].Handle <> FSocket.Handle then
begin
TWriteStrThread.Create("addcli: " + PSDstr(FSocket.Data).UserName + " " + FSocket.RemoteHost + " " + FSocket.RemoteAddress, FServerSocket.Socket.Connections[i]);
end;
end;
end;
{ TWriteStrThread }
constructor TWriteStrThread.Create(AText: string;
Socket: TCustomWinSocket);
begin
inherited Create(false);
//Priority := tpLowest;
FSocket := Socket;
FText := AText;
end;
procedure TWriteStrThread.Execute;
begin
send(FSocket.SocketHandle, Pointer(FText)^, Length(FText), 0);
end;
end.
Digitman (2002-08-28 10:39:00) [6]В смысле - события TCustomWinSocket ?
Возможны OnWrite (факт доступности буфера передачи гнезда)
и OnError (отказ транспорта по многим причинам, например, разрыв соединения по инициативе партнера в момент передачи)
AlexAn (2002-08-28 10:35:00) [7]Reindeer Moss Eater > какой вариант с блокирующим сокетом (в смысле реализации), если сервер должен рассылать события клиентам не по запросу, а в момент возникновения события на сервере (желательно в реальном времени)?
AlexAn (2002-08-28 10:27:00) [8]и еще вопрос: какие события возникают вместе с
send?
AlexAn (2002-08-28 10:21:00) [9]я об этом думал, только у меня сервисное приложение, с обычным никаких проблем
Digitman (2002-08-28 10:18:00) [10]Так ты же не даешь приложению возможности обрабатывать потенциальные сообщения, могущие возникнуть в момент выполнения цикла с вызовом Send-метода !
Тогда уж вот так примерно делай :
i := 0;
with ServerSocket.Socket do
while i < ActiveConnections do
begin
if Connections[i].Handle <> Socket.Handle then
try
while Connections[i].SendText(..) <> 0 do
Application.ProcessMessages;
except
end;
Inc(i);
end;
Но только не в OnAccept ! Как минимум - в OnClientConnect
Reindeer Moss Eater (2002-08-28 10:06:00) [11]А каков глубокий смысл неблокирующего сокета в сервисе???
AlexAn (2002-08-28 09:46:00) [12]а насчет "работает .. сокет" - я имел ввиду, что мы (на FD_ACCEPT) имеем:
- серверный-ждущий сокет
- серверный-клиентский сокет, полученный ранее (или несколько)
- серверный-клиентский сокет, полученный по событию FD_ACCEPTacSocket := accept(...)
так вот, участвуют только сокетыиз второго пункта (у всех у них было FD_WRITE)
именно это я и имел ввиду
AlexAn (2002-08-28 09:45:00) [13]а насчет "работает .. сокет" - я имел ввиду, что мы (на FD_ACCEPT) имеем:
- серверный-ждущий сокет
- серверный-клиентский сокет, полученный ранее (или несколько)
- серверный-клиентский сокет, полученный по событию FD_ACCEPTacSocket := accept(...)
так вот, участвуют только сокеты из второго пункта (у всех у них было FD_WRITE)
именно это я и имел ввиду
AlexAn (2002-08-28 09:39:00) [14]!!! я описался - в неблокирующем конечно !!!
AlexAn (2002-08-28 09:38:00) [15]в блокирующем (через WSAAsyncSelect) режиме если данные не отосланы возвращается WSAEWOULDBLOCK, и данные отсылаются из буфера системы, после чего вызывается оконная процедура с FD_WRITE.
если данные отослались, то возвращается кол-во отсосланных байт, что у меня и происходит, т.к. сообщения маленькие.
но задержка send на 10 сек при посылке на 100Мбит (длинна ~20байт) - это КРУТО!!!
Digitman (2002-08-27 12:18:00) [16]что значит, по твоему , "работает ... сокет" ?
ты считаешь, что send-метод работает мгновенно ?
прокомментируй ход своих мыслей для строчки
ServerSocket.Socket.Connections[i].SendText(...);
Как она работает, по-твоему ? Что происходит при выполнении send-метода в том или ином режиме сервера ?
AlexAn (2002-08-27 11:34:00) [17]дело в том, что работает не сокет полученный по accept, а сокеты, которые уже соединены, я честно говоря не пойму, каким они тут боком ?
Digitman (2002-08-27 10:54:00) [18]>AlexAn
Ну разумеется ! В Accept-обработчике такие вещи недопустимы !
Но почему это, по твоему мнению, "глюк" - вот это уже гораздо интереснее) ... Не "глюк" это вовсе, а нормальная логика работы send-методов
AlexAn (2002-08-27 10:51:00) [19]но это еще не все !!!
я перенес весь этот цикл в OnWrite и там он тоже тормозит, хотя во всех остальных случаях вроде все в порядке
буду разбираться !!!
AlexAn (2002-08-27 10:47:00) [20]короче, я нашел глюк
можно, конечно, меня за это и по рукам - но интересная ситуация:
проблема вся в send().
код вот такой:
procedure TsvcTcp.ServerSocketAccept(Sender: TObject;
Socket: TCustomWinSocket);
var
i: Integer;
r_str: PString;
begin
New(r_str);
Socket.Data := r_str;
for i := 0 to ServerSocket.Socket.ActiveConnections - 1 do
begin
if ServerSocket.Socket.Connections[i].Handle <> Socket.Handle then
begin
ServerSocket.Socket.Connections[i].SendText("addcli: " + Socket.RemoteHost + " " + Socket.RemoteAddress);
end;
end;
end;
если убрать
ServerSocket.Socket.Connections[i].SendText("addcli: " + Socket.RemoteHost + " " + Socket.RemoteAddress);
все работает