-
> где можно про это почитать
Дык в справке стандартной и почитай
-
> zSergey
Delphi автоматически позволяет создавать ActiveX только на основе потомков TWinControl кажется.
-
Привет знатокам! Написал сокет-сервер как сом-объект на WinApi (*.dll). Все хорошо, сокет открывается, слушается, данные из него читаются, но само приложение, использующее библиотеку, тормозит и дальше программа не может выполнить остальные свои функции, что не есть хорошо. Используются не блокирующие сокеты. Выкладываю весь исходник: unit Utestlib;
interface
uses
ComObj, ActiveX, testlib_TLB, StdVcl, Dialogs, WinSock, SysUtils, Windows;
type
TApp = class(TAutoObject, IApp)
protected
procedure OpenSocket; safecall;
end;
type
ThreadData = record
Socket: TSocket;
end;
PThreadData = ^ThreadData;
var
WSAData: TWSAData;
ListenSocket, ClientSocket: TSocket;
Info: PThreadData;
SockAddr: TSockAddr;
ThreadId: THandle;
hClientThread: Thandle;
Arg : u_long;
s: TSocket;
const
Port = word(5555);
implementation
uses ComServ;
procedure SocketThread(Info: PThreadData);
var
SockName: TSockAddr;
NameLen, OptLen: Integer;
buf : array[0..35] of byte;
RecvSize: integer;
BuffSize,k: integer;
str : string;
error : integer;
begin
s := Info^.Socket;
try
NameLen := SizeOf(TSockAddr);
if GetPeerName(s, SockName, NameLen) <> 0 then exit;
ShowMessage('Client accepted');
OptLen := SizeOf(BuffSize);
if GetSockOpt(s, SOL_SOCKET, SO_RCVBUF, pointer(@BuffSize), OptLen) <> 0 then exit;
try
repeat
RecvSize := recv(s, Buf[0], Length(Buf), 0);
if RecvSize = SOCKET_ERROR then
Begin
error := WSAGetLastError();
if (error<>WSAEWOULDBLOCK) then
continue
else RecvSize := 1
end
else
sleep(5);
if RecvSize > 1 then
ShowMessage('Received from client ...');
until RecvSize <= 0;
finally
end;
ShowMessage('Client disconnected ...');
finally
CloseSocket(s);
Dispose(Info);
end;
end;
procedure TApp.OpenSocket;
begin
ShowMessage('WSA Initialize ...');
if WSAStartup($101, WSAData) <> 0 then exit;
try
ListenSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
Arg:=1;
IOCtlSocket(ListenSocket,FIONBIO,Arg);
if ListenSocket = INVALID_SOCKET then exit;
try
FillChar(SockAddr, SizeOf(TSockAddr), 0);
SockAddr.sin_family := AF_INET;
SockAddr.sin_port := htons(Port);
SockAddr.sin_addr.S_addr := INADDR_ANY;
if Bind(ListenSocket, SockAddr, SizeOf(TSockAddr)) <> 0 then exit;
if listen(ListenSocket, SOMAXCONN) <> 0 then exit;
repeat
ClientSocket := accept(ListenSocket, nil, nil);
if ClientSocket <> INVALID_SOCKET then
begin
New(Info);
Info^.Socket := ClientSocket;
hClientThread := BeginThread(nil, 0, @SocketThread, Info, 0, ThreadId);
if hClientThread <> 0 then CloseHandle(hClientThread);
end;
until false;
finally
Arg:=0;
IOCtlSocket(ListenSocket,FIONBIO,Arg);
CloseSocket(ListenSocket);
end;
finally
WSACleanup;
end;
end;
initialization
TAutoObjectFactory.Create(ComServer, TApp, Class_App,
ciMultiInstance, tmApartment);
end. что я не так делал?
-
zsergey (16.01.08 19:53) [22] не блокирующие сокеты 1. и нафега BeginThreadв твоем не блокирующем сокете? 2. чтобы он стал полноценно неблокирующим нужно вызвать Select функцию и в нее передать дескриптор окна в которое будут приходить сообщения об событиях сокета 3. неблокирующий у тебя только слушающий сокет (listen), остальные (accept) блокирующие т.к. становится им неблокирующими ты им не сказал
-
ты понацеплял ComObj, ActiveX, testlib_TLB, StdVcl, Dialogs и тебе жалко 10кб на нормальный сокет? ты еще сом-объект руками сваяй... Slym © (17.01.08 4:28) [23]4. в com объекте с потоками огребеш не зная предметной области
-
> ShowMessage
Что еще за VCL-выкрутасы в дополнительном потоке ?
-
> 3. неблокирующий у тебя только слушающий сокет (listen), > остальные (accept) блокирующие т.к. становится им неблокирующими > ты им не сказал >
Угу. Но при этом в слушающем потоке, работающем с неблокирующим ListenSocket, почему-то нет ни намека на WSAEWOULDBLOCK, зато в клиентских потоках, работающих с блокирующими ClientSocket, WSAEWOULDBLOCK с какого-то перепугу фигурирует.
-
> zsergey (16.01.08 19:53) [22]
Лучше бы ты на асинхронных неблокирующих сокетах сделал. На сообщениях. Ей богу проще для твоего случая. И доп потоки не нужны.
-
> if GetSockOpt(s, SOL_SOCKET, SO_RCVBUF, pointer(@BuffSize), > OptLen) <> 0 then exit;
Совершенно непонятно, в каких случаях GetSockOpt может вернуть ошибку и для чего вообще запрашивается размер буфера приема, если этот полученный размер далее по ходу дела нигде не используется
-
> Slym © (17.01.08 04:28) [23]
Впрочем ни ты ни я не правы - ClientSocket у автора работает именно в неблок.режиме.
Цитата из справки по accept():
newly created socket has the same properties as s including asynchronous events registered with WSAAsyncSelect or with WSAEventSelect
-
Но этот факт не меняет сути - логика repeat-цикла в кл.потоке с учетом неблок.режима не верна в принципе
-
Сергей М. © (17.01.08 10:56) [29] Select я то как раз прав... ниодного select я не увидел... если бы и увидел то было выглядело бы так: WSAAsyncSelect(ListenSocket,hWnd,FD_ACCEPT,WM_USER); этого для слушателя достаточно, а для ацепнутого сокета никаких Эвентов не прописано...
-
> 2. чтобы он стал полноценно неблокирующим нужно вызвать > Select функцию
> ниодного select я не увидел... > если бы и увидел то было выглядело бы так: > WSAAsyncSelect(ListenSocket,hWnd,FD_ACCEPT,WM_USER);
Хорошо, вот переделанный сервер с селектом (не СОМ)
program Project1;
uses
SysUtils,
WinSock;
var Sockets:array of TSocket;
Addr:TSockAddr;
Data:TWSAData;
Len,I,J:Integer;
FDSet:TFDSet;
arrBuf : array [0..35] of byte;
Arg:u_long;
tv : Ttimeval;
begin
WSAStartup($101,Data);
SetLength(Sockets,1);
Sockets[0]:=Socket(AF_Inet,Sock_Stream,0);
Addr.sin_family:=AF_Inet;
Addr.sin_port:=HToNS(21001);
Addr.sin_addr.S_addr:=InAddr_Any;
FillChar(Addr.Sin_Zero,SizeOf(Addr.Sin_Zero),0);
Bind(Sockets[0],Addr,SizeOf(TSockAddr));
Listen(Sockets[0],SoMaxConn);
while True do
begin
FD_Zero(FDSet);
for I:=0 to High(Sockets) do
Arg:=1;
IOCtlSocket(Sockets[I],FIONBIO,Arg);
FD_Set(Sockets[I],FDSet);
tv.tv_sec := 5;
tv.tv_usec := 0;
Select(0,@FDSet,nil,nil,@tv);
if FDSet.fd_count=0 then Continue;
sleep(100);
writeln('wait');
I:=1;
while I<=High(Sockets) do
begin
if FD_IsSet(Sockets[I],FDSet) then
if Recv(Sockets[I],arrBuf, sizeOf(arrBuf),0) <=0 then
begin
writeln('disconnect, socket close');
CloseSocket(Sockets[I]);
for J:=I to High(Sockets)-1 do
Sockets[J]:=Sockets[J+1];
Dec(I);
SetLength(Sockets,Length(Sockets)-1)
end
else
begin
writeln('Receive data');
end;
Inc(I)
end;
if FD_IsSet(Sockets[0],FDSet) then
begin
writeln('new client connected');
SetLength(Sockets,Length(Sockets)+1);
Len:=SizeOf(TSockAddr);
Sockets[High(Sockets)]:=Accept(Sockets[0],@Addr,@Len)
end
end;
end. Но почему-то при тестировании select не возвращает сокеты для чтения, чего не должно быть. Где ошибка?
-
> Slym © (17.01.08 11:08) [31]
> ниодного select я не увидел
Причем здесь select ? Ну нет его и нет - и фиг с ним, я о другом сейчас.
Я о твоей ремарке
> остальные (accept) блокирующие т.к. становится им неблокирующими > ты им не сказал
и о своей
> в клиентских потоках, работающих с блокирующими ClientSocket
А это неверно, поскольку слушающий сокет перед вызовом accept() переведен в неблок.режим (т.е. для него явно установлено опция-"свойство" nonblocking), что, с учетом цитаты в [29], при успешном вызове accept() порождает новое неблокирующее гнездо.
-
> select не возвращает сокеты для чтения
И не возвратит.
Куда у тебя accept()-то пропал ? Без него select как мертвому припарка)
-
> И не возвратит.
нашел в чем ошибка: FD_Set(Sockets[I],FDSet); в цикле не в операторских скобках; для чего Arg:=1;
IOCtlSocket(Sockets[I],FIONBIO,Arg); перенес под Accept(); теперь заработало, НО само приложение в "замороженном" состоянии, будь там блокирующие сокеты или не блокирующие, будут там нити или нет. Я почти все перепробовал. Повторюсь, сервер должен не только обрабатывать подключения, но и одновременно сохранять другие свои ф-ции, короче, чтобы все контролы на форме были доступными. Вроде понятно объяснил. > Лучше бы ты на асинхронных неблокирующих сокетах сделал. > На сообщениях. Ей богу проще для твоего случая. И доп потоки > не нужны.
Господа, дайте пож-ста простой примерчик, на который мне можно было опереться в данном случае.
-
ты все-таки определись, тебя на WSAPI заклинило окончательно и бесповоротно или только блажь ?
-
> ты все-таки определись, тебя на WSAPI заклинило окончательно > и бесповоротно или только блажь ?
ага мне вот делать нехер, низнаю чем заняться, WSAPI или еще чем. Уважаемый Сергей М, напоминаю, для чего мне все это нужно:
Поясняю задачу: сам сервер пишется для связки с 1с v.8. Т.к. у 1с нет стандартных средств для работы с сокетами, приходиться прибегнуть с помошью внешних компонент (dll), где собственно будет находится сам функционал сервера. Далее, приняв данные, сервер должен в реальном времени передать их приложению-хозяину, приложение в это время не находиться в режиме ожидания, а продолжает выполнять весь свой функционал. Я понимаю, что это форум отношения к 1с не имеет, потому я пока рассматриваю вариант взаимодействия программы и сервера на delphi. Буду благодарен любым идеям.
ни внешних компонент, ни бесплатных ActiveX контролов у меня нет, чтоб решить эту проблему, потому я вынужден ковырять wsapi ибо другого выхода я не вижу :)
-
> мне вот делать нехер, низнаю чем заняться
Я вот тоже так думаю - тебе нечем более заняться)
ЧЕМ не устроили TServerSocket, TTCPServer, ICS-компоненты ? Вразумительно ты так и не объяснил)
-
> ЧЕМ не устроили TServerSocket, TTCPServer, ICS-компоненты > ? > Вразумительно ты так и не объяснил)
я не знаю как эти компоненты засунуть в dll :)
|