-
Доброго времени суток, уважаемые! Вопрос у меня немного поверхностный, но важный (по крайней мере, для меня :)), больше похоже на "посоветуйте" -).
Есть задача, написать аналог HTTP(S) прокси-сервера, но с небольшой спецификой: запросы некоторых подключенных клиентов будут обрабатываться по-другому и обмен с такими клиентами будет происходить маленькими пакетами. Остальным клиентам ("не особо одаренным") должно быть также хорошо, как и с обычным прокси :). Основные требования, предъявляемые к такому серверу - быть устойчивым к капризным клиентам (это те, кто маленькими пакетами будет пуляться) и всем остальным клиентам, и держать максимально возможное (насколько позволят ОС+hardware и др.) количество одновременных соединений (например, 100 :)). В то же время, сервер не должен получиться громоздким и слишком сложным.
Из арсенала имеются только стандартные средства Delphi (Indy и другие библиотеки отбрасываем).
Собственно вопрос заключается в том, что использовать в качестве базы и какая модель приложения наиболее пригодна (по вашему мнению) для описанной выше задачи.
Первое, что приходит в голову - TTCP[Server\Client], T[Server\Client]Socket или голое API of WinSock.
С T[Server\Client]Socket работал давно (последний раз лет 6-7 назад) - в то время очень не понравилось. Некоторые пакеты пропускались (хотя TCP считают "гарантированным"). Когда тут на форуме спросил в чем дело, мне тогда намекнули, если мне не изменяет память, на метод Nagle. Но проблему так и не решил (в те времена).
Вчера "тест-драйв" попытался устроить TCPServer`у. НаписАл на базе него простенький прокси для обработки GET запросов, который, в общем-то, даже работал, но мееееедленно. Реагировал на событие OnAccept, режим блокировки - bmThreadBlocking, читал и писАл из\в сокеты используя ReceiveBuf и SendBuf.
В общем, уважаемые, каким образом лучше организовать работу сервера (прием соединений на сервере и создание временных подключений внутри потока для получения данных с другого сервера и отдачи их обратно клиенту) и с помощью чего: * ПисАть на голом WinSock API, потоки создавать самому, работать в блокирующем режиме * ПисАть на голом WinSock API, работать в неблокирующем режиме, тогда, как я понимаю, потоки создавать не нужно будет * Аналогично описанному выше, только с использванием в качестве опорной точки один из компонентов\классов (например, (T)TCP[Server\Client], (T)[Server\Client]Socket) * Применять "фишку" Delphi - режим ThreadBlocking * что-либо другое
Заранее благодарю всех дочитавших до конца и ответивших!
-
чем Indy то не угодил?
-
> DVM © (11.05.10 18:43) [1] > чем Indy то не угодил?
Лицензией, тяжестью исправления (в том смысле, что исправлять много приходится, чтобы что-либо добавить\изменить на более низком уровне), потом, помню, были проблемы с убиванием потоков ( http://pda.delphimaster.net/?id=1236577141&n=4).
-
> kernel © (11.05.10 18:50) [2]
Если хочешь максимум контроля над сервером - делай все сам на WinSock. Я бы наверное выбрал блокирующий режим и что-то типа пула потоков.
-
kernel © (11.05.10 18:07) НаписАл на базе него простенький прокси для обработки GET запросов Твоя задача сводится 1. принять заголовок у клиента, выдрать от туда host:port заказаного сервера 2. сконектиться по host:port и полностью возможно с минимальными изменениями отправить принятые в п1. от клиента данные 3. обеспечить прозрачное тунеллирование трафика клиент/сервер 4. (ОПЦИЯ) затормозить особо ретивые конекты наример тупым sleep (при буфере сокета в 8кб + sleep(1000) получим 8кб/сек максимум)
-
> пакеты пропускались (хотя TCP считают "гарантированным"). > Когда тут на форуме спросил в чем дело, мне тогда намекнули, > если мне не изменяет память, на метод Nagle
Похоже ты уже и сам не помнишь о чем был твой вопрос. Используется или не используется Nagle - на "пропуск пакетов" он повлиять никак не может в принципе.
-
Благодарю всех за ответы!
> DVM © (11.05.10 19:34) [3]
Все что нужно из контроля - общение с клиентом с заданными размерами пакетов (маленькими пакетами для вредных клиентов и большими - для остальных) с возможностью их "переработки" и подсчет отправленных\полученных данных. Все это, если я не ошибаюсь, успешно выполняют и обычные сокетообразные компоненты. Или все равно лучше применить чистый WinSock? :) Тут, наверное, больше интересует у кого из них (сок. компоненты vs WinSock API) производительность будет побольше. Или в этом плане разницы никакой нет?
> Slym © (11.05.10 20:37) [4]
Ну как работает прокси мне известно. Делал именно так, как Вы описали.
> Сергей М. © (11.05.10 22:02) [5]
Действительно подзабыл. Нашел те старые исходники на этих компонентах, там скорее всего была проблема в том, что в короткий промежуток времени сервер сам цеплялся к клиентам и отправлял всем клиентам команды. Для того чтобы всем все успешно отправилось, необходимо было ставить задержку около 1 сек. (по крайней мере у меня работало только не < ~1 сек.) после отсылки команды каждому ПК. Вот тут мне про Nagle скорее всего и намекнули.
-
> kernel © (13.05.10 14:08) [6]
> Тут, наверное, больше интересует у кого из них (сок. компоненты > vs WinSock API) производительность будет побольше. Или > в этом плане разницы никакой нет?
Компоненты они же на базе WinSock построены. Разницы между ними и чистым Winsock практически нет. По крайней мере это не то место, где следует искать разницу.
-
> kernel © (13.05.10 14:08) [6]
Другое дело, что вокруг WinSock можно написать свои простенькие обертки, больше подходящие к конкретной задаче - в этом их удобство.
-
Спасибо за ответы, DVM! Сделал свой класс на основе ServerSocket, все работает быстро и замечательно [тьфу-тьфу-тьфу], контроля полностью хватает. PS: собст-но это сообщение и отправил через пробный прокси :)
-
Всем привет! Чтобы не создавать отдельный топик, спрошу здесь (т.к. разговор связан с этой темой). Организовал нужный мне функционал на базе TServerSocket (режим - stThreadBlocking) с созданием отдельных потоков для каждого установленного соединения. Дошел до момента, где мне необходимо делать туннелирование между [клиент] <-> ["прокси-сервер"] <-> [запрашиваемый сервер] для работы SSL. Механизм такой: клиент посылает мне запрос CONNECT (HTTP/1.0), затем после того, как я (прокси) отправляю заголовок "200 OK - соединение установлено", в цикле, пока любое из двух соединений не разорвется (клиент или TargetHost), ловлю данные в буфер от клиента и тут же передаю запрашиваемому серверу, пока от клиента не начнет приходить ноль байт. Затем то же самое делаю, но наоборот, ловлю данные в буфер от запрашиваемого сервера и тут же передаю клиенту. И это все повторяется до тех пор, пока кто-либо из них не разорвет соединение. ...
const BufSize = 128;
...
ClientStream : TWinSocketStream;
TargetConnection: TTcpClient;
Buf: array [0..BufSize-1] of Byte;
...
ClientStream := TWinSocketStream.Create(ClientSocket, 60000);
...
while (КлиентИПунктНазначенияПодключены) do begin
ClientWait := (ClientStream.WaitForData(100));
if ClientWait then begin
RecLen := ClientStream.Read(Buf, BufSize);
while (ClientSocket.Connected) and (TargetConnection.Connected) and (RecLen > 0) do begin
TargetConnection.SendBuf(Buf, RecLen);
if (not ClientSocket.Connected) or (not TargetConnection.Connected) then Break;
if (not ClientStream.WaitForData(10)) then Break;
RecLen := ClientStream.Read(Buf, BufSize);
end;
end;
ServerWait := TargetConnection.WaitForData(100);
if ServerWait then begin
RecLen := TargetConnection.ReceiveBuf(Buf, BufSize);
while (ClientSocket.Connected) and TargetConnection.Connected) and (RecLen > 0) do begin
ClientStream.Write(Buf, RecLen);
if (not ClientSocket.Connected) or (not TargetConnection.Connected) then Break;
if (not TargetConnection.WaitForData(10)) then Break;
RecLen := TargetConnection.ReceiveBuf(Buf, BufSize);
end;
end;
end;
... Все бы хорошо (и даже отрабатываются несколько проходов общего цикла верно), но практически сразу общий цикл замирает (точнее, не замирает, а бесконечно крутится) и наблюдается странная картина: ClientStream.WaitForData(100) в начале цикла начинает все время возвращать True, в то время как количество принятых байт ( RecLen := ClientStream.Read ...) с этого сокета становится все время равно нулю. При этом, оба соединения не дисконнэктятся (хотя с чего бы им разрываться, если данные "недопередались"). Вопрос: Что я делаю не так? :)
-
То есть, проще говоря, через некоторое время цикл "крутится", а данные не хочет отдавать ни один сокет :/
-
procedure TPortMapClientThread.DoTunneling(Peer1,Peer2:TCustomWinSocket);
function SendBufFully(Peer:TCustomWinSocket;var Buf; Count: Integer):boolean;
var pBuf:PByte;
s:integer;
begin
result:=false;
pBuf:=@Buf;
while count>0 do
begin
s:=Peer.SendBuf(pBuf^,Count);
if s=0 then exit;
inc(pBuf,s);
dec(Count,s);
end;
result:=true;
end;
var
FDSet: TFDSet;
TimeVal: TTimeVal;
Buf:array[0..4095] of char;
r:integer;
begin
TimeVal.tv_sec := FPortMap.ClientTimeout div 1000;
TimeVal.tv_usec := (FPortMap.ClientTimeout mod 1000) * 1000;
while Peer1.Connected and Peer2.Connected do
begin
FD_ZERO(FDSet);
FD_SET(Peer1.SocketHandle, FDSet);
FD_SET(Peer2.SocketHandle, FDSet);
if select(0, @FDSet, nil, nil, @TimeVal)>0 then
begin
if FD_ISSET(Peer1.SocketHandle, FDSet) then
begin
r:=Peer1.ReceiveBuf(Buf,Length(Buf));
if r=0 then exit;
if not SendBufFully(Peer2,Buf,r) then exit;
end;
if FD_ISSET(Peer2.SocketHandle, FDSet) then
begin
r:=Peer2.ReceiveBuf(Buf,Length(Buf));
if r=0 then exit;
if not SendBufFully(Peer1,Buf,r) then exit;
end;
end else
exit;
end;
end;
USAGE: DoTunneling(ClientSocket,TargetConnection);
-
Спасибо, Slym! Немного позже попробую даный код и отпишусь здесь :)
-
Еще раз огромное спасибо, Slym! Первые тесты туннелирования пока дали успешный результат. PS: FPortMap.ClientTimeout поставил = 60000 [мс] для пробы. Хотя в Инди HTTP прокси, например, таймаут на каждое подключение при туннелировании = 100 мс. Какое оптимальное значение можно поставить? :)
-
kernel © (19.05.10 20:35) [14] Какое оптимальное значение можно поставить? 10сек вполне хватит
-
> Slym © (20.05.10 07:58) [15]
Спасибо! :)
|