Форум: "Сети";
Текущий архив: 2010.10.31;
Скачать: [xml.tar.bz2];
Вниз
Как правильно написать socks-сервер? Найти похожие ветки
← →
zsergey (2008-01-13 21:01) [0]Привет господа! Есть такая задача: написать простой сокетный сервер, который должен обслуживать несколько подключений одновременно. Я только начал разбирать с этим вопросом, просьба, не ругать. Вот навоял небольшой работающий пример, правда без обработок ошибок:
procedure TForm1.startserverClick(Sender: TObject);
var S,AcceptedSock:TSocket;
Addr:TSockAddr;
Data:TWSAData;
Len:Integer;
buf : array[0..30] of byte;
begin
WSAStartup($101,Data);
S:=Socket(AF_Inet,Sock_Stream,0);
Addr.sin_family:=PF_Inet;
Addr.sin_port:=HToNS(5005);
Addr.sin_addr.S_addr:=InAddr_Any;
FillChar(Addr.Sin_Zero,SizeOf(Addr.Sin_Zero),0);
Bind(S,Addr,SizeOf(TSockAddr));
Listen(S,SoMaxConn);
Len:=SizeOf(TSockAddr);
AcceptedSock:=Accept(S,@Addr,@Len);
Recv(AcceptedSock,buf, sizeof(buf),0);
// дальше обрабатываю данные
end;
тут используется блокирующий сокет, который отрабатывает одно соединение. Подскажите пожалуйста, как здесь организовать обслуживание несколько подключений, в какую сторону хотя бы смотреть...
← →
DVM © (2008-01-13 21:06) [1]
> в какую сторону хотя бы смотреть...
в сторону либо потоков либо в сторону асинхронных сокетов на сообщениях.
Для блокирующих сокетов Accept крутить в цикле (в идеале цикл в отдельном потоке), при подключении клиента создавать новый поток - передавать туда сокет и работать с конкретным клиентом в этом потоке.
← →
DVM © (2008-01-13 21:08) [2]Вот как то так (в коде могут быть ошибки!):
program server;
{$APPTYPE CONSOLE}
uses
SysUtils,
Winsock,
Windows;
type
ThreadData = record
Socket: TSocket;
end;
PThreadData = ^ThreadData;
var
WSAData: TWSAData;
ListenSocket, ClientSocket: TSocket;
Info: PThreadData;
SockAddr: TSockAddr;
ThreadId: THandle;
hClientThread: Thandle;
const
Port = word(25);
//------------------------------------------------------------------------------
procedure SocketThread(Info: PThreadData);
var
SockName: TSockAddr;
NameLen, OptLen: Integer;
Buf: PChar;
RecvSize: integer;
s: TSocket;
BuffSize: integer;
begin
s := Info^.Socket;
try
NameLen := SizeOf(TSockAddr);
if GetPeerName(s, SockName, NameLen) <> 0 then exit;
Writeln(format("Client accepted, remote address [%s].", [inet_ntoa(SockName.sin_addr)]));
OptLen := SizeOf(BuffSize);
if GetSockOpt(s, SOL_SOCKET, SO_RCVBUF, pointer(@BuffSize), OptLen) <> 0 then exit;
writeln(format("Receive buffer size [%d]", [BuffSize]));
GetMem(Buf, BuffSize);
try
repeat
RecvSize := recv(s, Buf^, BuffSize, 0);
sleep(5);
if RecvSize > 0 then
writeln(format("Received from client: %s", [strpas(Buf)]));
until RecvSize <= 0;
finally
FreeMem(Buf, BuffSize);
end;
Writeln(format("Client disconnected, remote address [%s].",[inet_ntoa(SockName.sin_addr)]));
finally
CloseSocket(s);
Dispose(Info);
end;
end;
//------------------------------------------------------------------------------
begin
Writeln("WSA Initialize...");
if WSAStartup($101, WSAData) <> 0 then exit;
try
Writeln(format("Creating socket on port [%d].", [Port]));
ListenSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
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;
Writeln("Binding socket...");
if Bind(ListenSocket, SockAddr, SizeOf(TSockAddr)) <> 0 then exit;
if listen(ListenSocket, SOMAXCONN) <> 0 then exit;
Writeln("Socket status: listening...");
repeat
Writeln("wait");
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
CloseSocket(ListenSocket);
end;
finally
WSACleanup;
end;
//------------------------------------------------------------------------------
end.
← →
zsergey (2008-01-13 21:25) [3]Cпасибо за пример, буду разбираться.
← →
zSergey (2008-01-14 15:35) [4]теперь задача немного усложняется: сам сервер я написал в виде библиотеки (*.dll), увы такое требование. Сервер будет запускаться сторонним приложением. Здесь проблем нет. Вопрос в том, как сервер должен передавать данные в вызвавшую его программу?. Поясняю задачу: сам сервер пишется для связки с 1с v.8. Т.к. у 1с нет стандартных средств для работы с сокетами, приходиться прибегнуть с помошью внешних компонент (dll), где собственно будет находится сам функционал сервера. Далее, приняв данные, сервер должен в реальном времени передать их приложению-хозяину, приложение в это время не находиться в режиме ожидания, а продолжает выполнять весь свой функционал. Я понимаю, что это форум отношения к 1с не имеет, потому я пока рассматриваю вариант взаимодействия программы и сервера на delphi. Буду благодарен любым идеям.
← →
Сергей М. © (2008-01-14 16:16) [5]
> zSergey (14.01.08 15:35) [4]
Какого хрена ты вообще взялся за WinsockAPI ?
Тебе что, компонентов готовых мало ?)
← →
zSergey (2008-01-14 16:31) [6]> Сергей М.
ну зачем жет так резко, уважаемый? Ничего плохо в том, что я хочу разобрать с WinsockAPI нет. Да и сама библиотечка будет намного легче весить без компонент. Кстати, а как запихнуть компонент в Dll ? :)Да и вопрос ведь в том, какой должен быть механизм обмена между 2 приложениями, пусть даже с использованием компонент?
← →
zSergey (2008-01-14 16:40) [7]
> Тебе что, компонентов готовых мало ?)
Кстати, а о каких компонентах речь идет?
← →
DVM © (2008-01-14 22:59) [8]
> теперь задача немного усложняется: сам сервер я написал
> в виде библиотеки (*.dll), увы такое требование. Сервер
> будет запускаться сторонним приложением. Здесь проблем нет.
> Вопрос в том, как сервер должен передавать данные в вызвавшую
> его программу?.
А разве сервер запущенный из dll некой сторонней программой не находится в адресном пространстве процесса этой программы? Это передача не между процессами, а между частями одного и того же приложения (в данном случае между потоками).
Самый простой и надежный способ передачи через сообщения Windows.
← →
Сергей М. © (2008-01-15 08:21) [9]
> о каких компонентах речь идет?
Мало ли таковых существует !
TTCPServer, TServerSocket у тебя прямо под рукой имеются.
> как запихнуть компонент в Dll
Точно также как в exe
← →
ketmar © (2008-01-15 09:13) [10]>[6] zSergey (14.01.08 16:31)
>какой должен быть механизм обмена между 2 приложениями
рабочий он должен быть. далее конкретизируется под конкретную задачу.
вот с той же адынцэ — это тебе ActiveX писать. где, кстати, пофигу, компоненты али не компоненты — всё равно будет жирное (если ты, конечно, не хардкорщик, желающий ваять ActiveX «с нуля»).
← →
zSergey (2008-01-15 13:33) [11]
> вот с той же адынцэ — это тебе ActiveX писать. где, кстати,
> пофигу, компоненты али не компоненты — всё равно будет
> жирное (если ты, конечно, не хардкорщик, желающий ваять
> ActiveX «с нуля»).
Тоже хорошая идея, навоять собственный ActiveX контрол, чтобы было проще, на основе того же TServerSocket. Как это сделать? Полез я визард "ActiveX control wizard", там нет родителя для TServerSocket, как я понял, так можно замутить только для визуальный компонент. возможно ли в данном случае сделать свой ActiveX на основе TSocketServer, если да, то как? (просьба не смеяться и не злиться) :)
← →
Сергей М. © (2008-01-15 13:39) [12]Сначала поясни, зачем тебе понадобился именно AX-контрол, да еще и невизуальный ?
← →
zSergey (2008-01-15 13:48) [13]
> Сначала поясни, зачем тебе понадобился именно AX-контрол,
> да еще и невизуальный ?
потому что
> у 1с нет стандартных средств для работы с сокетами, приходиться
> прибегнуть с помошью внешних компонент (dll),
или еще как вариант - ActiveX :)
← →
Сергей М. © (2008-01-15 13:51) [14]
> zSergey (15.01.08 13:48) [13]
Зато у одноэсины есть механизм использования надстроек (1C Add-ins)
И она не имеет ни малейшего отношения к AX.
← →
zSergey (2008-01-15 13:56) [15]
> Зато у одноэсины есть механизм использования надстроек (1C
> Add-ins)
> И она не имеет ни малейшего отношения к AX.
это я понимаю! Просто мне интересно, а возжно и проще решить энту задачу через АХ. Итак, не посчитайте меня назойливым, как написать АХ на основе TSockerServer ?
← →
zSergey (2008-01-15 14:02) [16]> Сергей М. ©
Напиши сам, делов-то на 10 минут. - Здесь вы имели в виду с нуля написать?
← →
Сергей М. © (2008-01-15 14:03) [17]
> мне интересно, а возжно и проще решить энту задачу через АХ
Ходить в гости к соседу по двору через Китай тоже можно.
> как написать АХ на основе TSockerServer
Никак.
← →
Сергей М. © (2008-01-15 14:04) [18]
> с нуля написать?
Почему бы и нет ?
← →
zSergey (2008-01-15 14:11) [19]
> Почему бы и нет ?
Не посчитайте назойливым, может дадите сылочку, где можно про это почитать... В книжке Архангельского подобного ничего нет.
← →
Сергей М. © (2008-01-15 14:18) [20]
> где можно про это почитать
Дык в справке стандартной и почитай
← →
DVM © (2008-01-15 17:33) [21]
> zSergey
Delphi автоматически позволяет создавать ActiveX только на основе потомков TWinControl кажется.
← →
zsergey (2008-01-16 19:53) [22]Привет знатокам!
Написал сокет-сервер как сом-объект на WinApi (*.dll). Все хорошо, сокет открывается, слушается, данные из него читаются, но само приложение, использующее библиотеку, тормозит и дальше программа не может выполнить остальные свои функции, что не есть хорошо. Используются не блокирующие сокеты. Выкладываю весь исходник:unit Utestlib;
{$WARN SYMBOL_PLATFORM OFF}
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);
// non bloking
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.
что я не так делал?
← →
Slym © (2008-01-17 04:28) [23]zsergey (16.01.08 19:53) [22]
не блокирующие сокеты
1. и нафега BeginThreadв твоем не блокирующем сокете?
2. чтобы он стал полноценно неблокирующим нужно вызвать Select функцию и в нее передать дескриптор окна в которое будут приходить сообщения об событиях сокета
3. неблокирующий у тебя только слушающий сокет (listen), остальные (accept) блокирующие т.к. становится им неблокирующими ты им не сказал
← →
Slym © (2008-01-17 04:32) [24]ты понацеплял
ComObj, ActiveX, testlib_TLB, StdVcl, Dialogs
и тебе жалко 10кб на нормальный сокет? ты еще сом-объект руками сваяй...
Slym © (17.01.08 4:28) [23]
4. в com объекте с потоками огребеш не зная предметной области
← →
Сергей М. © (2008-01-17 10:11) [25]
> ShowMessage
Что еще за VCL-выкрутасы в дополнительном потоке ?
← →
Сергей М. © (2008-01-17 10:21) [26]
> 3. неблокирующий у тебя только слушающий сокет (listen),
> остальные (accept) блокирующие т.к. становится им неблокирующими
> ты им не сказал
>
Угу.
Но при этом в слушающем потоке, работающем с неблокирующим ListenSocket, почему-то нет ни намека на WSAEWOULDBLOCK, зато в клиентских потоках, работающих с блокирующими ClientSocket, WSAEWOULDBLOCK с какого-то перепугу фигурирует.
← →
DVM © (2008-01-17 10:34) [27]
> zsergey (16.01.08 19:53) [22]
Лучше бы ты на асинхронных неблокирующих сокетах сделал. На сообщениях. Ей богу проще для твоего случая. И доп потоки не нужны.
← →
Сергей М. © (2008-01-17 10:42) [28]
> if GetSockOpt(s, SOL_SOCKET, SO_RCVBUF, pointer(@BuffSize),
> OptLen) <> 0 then exit;
Совершенно непонятно, в каких случаях GetSockOpt может вернуть ошибку и для чего вообще запрашивается размер буфера приема, если этот полученный размер далее по ходу дела нигде не используется
← →
Сергей М. © (2008-01-17 10:56) [29]
> 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
← →
Сергей М. © (2008-01-17 10:59) [30]Но этот факт не меняет сути - логика repeat-цикла в кл.потоке с учетом неблок.режима не верна в принципе
← →
Slym © (2008-01-17 11:08) [31]Сергей М. © (17.01.08 10:56) [29]
Select
я то как раз прав...
ниодного select я не увидел...
если бы и увидел то было выглядело бы так:
WSAAsyncSelect(ListenSocket,hWnd,FD_ACCEPT,WM_USER);
этого для слушателя достаточно, а для ацепнутого сокета никаких Эвентов не прописано...
← →
zSergey (2008-01-17 11:38) [32]
> 2. чтобы он стал полноценно неблокирующим нужно вызвать
> Select функцию
> ниодного select я не увидел...
> если бы и увидел то было выглядело бы так:
> WSAAsyncSelect(ListenSocket,hWnd,FD_ACCEPT,WM_USER);
Хорошо, вот переделанный сервер с селектом (не СОМ)
program Project1;
uses
SysUtils,
WinSock;
{$APPTYPE CONSOLE}
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 не возвращает сокеты для чтения, чего не должно быть. Где ошибка?
← →
Сергей М. © (2008-01-17 11:41) [33]
> Slym © (17.01.08 11:08) [31]
> ниодного select я не увидел
Причем здесь select ?
Ну нет его и нет - и фиг с ним, я о другом сейчас.
Я о твоей ремарке
> остальные (accept) блокирующие т.к. становится им неблокирующими
> ты им не сказал
и о своей
> в клиентских потоках, работающих с блокирующими ClientSocket
А это неверно, поскольку слушающий сокет перед вызовом accept() переведен в неблок.режим (т.е. для него явно установлено опция-"свойство" nonblocking), что, с учетом цитаты в [29], при успешном вызове accept() порождает новое неблокирующее гнездо.
← →
Сергей М. © (2008-01-17 11:45) [34]
> select не возвращает сокеты для чтения
И не возвратит.
Куда у тебя accept()-то пропал ? Без него select как мертвому припарка)
← →
zSergey (2008-01-17 14:43) [35]
> И не возвратит.
нашел в чем ошибка:FD_Set(Sockets[I],FDSet);
в цикле не в операторских скобках;
для чегоArg:=1;
перенес под Accept(); теперь заработало, НО само приложение в "замороженном" состоянии, будь там блокирующие сокеты или не блокирующие, будут там нити или нет. Я почти все перепробовал. Повторюсь, сервер должен не только обрабатывать подключения, но и одновременно сохранять другие свои ф-ции, короче, чтобы все контролы на форме были доступными. Вроде понятно объяснил.
IOCtlSocket(Sockets[I],FIONBIO,Arg);
> Лучше бы ты на асинхронных неблокирующих сокетах сделал.
> На сообщениях. Ей богу проще для твоего случая. И доп потоки
> не нужны.
Господа, дайте пож-ста простой примерчик, на который мне можно было опереться в данном случае.
← →
Сергей М. © (2008-01-17 15:13) [36]ты все-таки определись, тебя на WSAPI заклинило окончательно и бесповоротно или только блажь ?
← →
zSergey (2008-01-17 15:32) [37]
> ты все-таки определись, тебя на WSAPI заклинило окончательно
> и бесповоротно или только блажь ?
ага мне вот делать нехер, низнаю чем заняться, WSAPI или еще чем. Уважаемый Сергей М, напоминаю, для чего мне все это нужно:
Поясняю задачу: сам сервер пишется для связки с 1с v.8. Т.к. у 1с нет стандартных средств для работы с сокетами, приходиться прибегнуть с помошью внешних компонент (dll), где собственно будет находится сам функционал сервера. Далее, приняв данные, сервер должен в реальном времени передать их приложению-хозяину, приложение в это время не находиться в режиме ожидания, а продолжает выполнять весь свой функционал. Я понимаю, что это форум отношения к 1с не имеет, потому я пока рассматриваю вариант взаимодействия программы и сервера на delphi. Буду благодарен любым идеям.
ни внешних компонент, ни бесплатных ActiveX контролов у меня нет, чтоб решить эту проблему, потому я вынужден ковырять wsapi ибо другого выхода я не вижу :)
← →
Сергей М. © (2008-01-17 15:34) [38]
> мне вот делать нехер, низнаю чем заняться
Я вот тоже так думаю - тебе нечем более заняться)
ЧЕМ не устроили TServerSocket, TTCPServer, ICS-компоненты ?
Вразумительно ты так и не объяснил)
← →
zSergey (2008-01-17 15:41) [39]
> ЧЕМ не устроили TServerSocket, TTCPServer, ICS-компоненты
> ?
> Вразумительно ты так и не объяснил)
я не знаю как эти компоненты засунуть в dll :)
← →
Сергей М. © (2008-01-17 15:47) [40]А в ехе знаешь ?)
рассказывай) ..
Страницы: 1 2 вся ветка
Форум: "Сети";
Текущий архив: 2010.10.31;
Скачать: [xml.tar.bz2];
Память: 0.58 MB
Время: 0.004 c