Форум: "Сети";
Текущий архив: 2014.12.28;
Скачать: [xml.tar.bz2];
ВнизСервер. Сокеты, потоки. Найти похожие ветки
← →
kernel © (2010-05-11 18:07) [0]Доброго времени суток, уважаемые!
Вопрос у меня немного поверхностный, но важный (по крайней мере, для меня :)), больше похоже на "посоветуйте" -).
Есть задача, написать аналог 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
* что-либо другое
Заранее благодарю всех дочитавших до конца и ответивших!
← →
DVM © (2010-05-11 18:43) [1]чем Indy то не угодил?
← →
kernel © (2010-05-11 18:50) [2]
> DVM © (11.05.10 18:43) [1]
> чем Indy то не угодил?
Лицензией, тяжестью исправления (в том смысле, что исправлять много приходится, чтобы что-либо добавить\изменить на более низком уровне), потом, помню, были проблемы с убиванием потоков (http://delphimaster.net/view/6-1236577141/).
← →
DVM © (2010-05-11 19:34) [3]
> kernel © (11.05.10 18:50) [2]
Если хочешь максимум контроля над сервером - делай все сам на WinSock. Я бы наверное выбрал блокирующий режим и что-то типа пула потоков.
← →
Slym © (2010-05-11 20:37) [4]kernel © (11.05.10 18:07)
НаписАл на базе него простенький прокси для обработки GET запросов
Твоя задача сводится
1. принять заголовок у клиента, выдрать от туда host:port заказаного сервера
2. сконектиться по host:port и полностью возможно с минимальными изменениями отправить принятые в п1. от клиента данные
3. обеспечить прозрачное тунеллирование трафика клиент/сервер
4. (ОПЦИЯ) затормозить особо ретивые конекты наример тупым sleep (при буфере сокета в 8кб + sleep(1000) получим 8кб/сек максимум)
← →
Сергей М. © (2010-05-11 22:02) [5]
> пакеты пропускались (хотя TCP считают "гарантированным").
> Когда тут на форуме спросил в чем дело, мне тогда намекнули,
> если мне не изменяет память, на метод Nagle
Похоже ты уже и сам не помнишь о чем был твой вопрос.
Используется или не используется Nagle - на "пропуск пакетов" он повлиять никак не может в принципе.
← →
kernel © (2010-05-13 14:08) [6]Благодарю всех за ответы!
> 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 скорее всего и намекнули.
← →
DVM © (2010-05-13 16:25) [7]
> kernel © (13.05.10 14:08) [6]
> Тут, наверное, больше интересует у кого из них (сок. компоненты
> vs WinSock API) производительность будет побольше. Или
> в этом плане разницы никакой нет?
Компоненты они же на базе WinSock построены. Разницы между ними и чистым Winsock практически нет. По крайней мере это не то место, где следует искать разницу.
← →
DVM © (2010-05-13 16:26) [8]
> kernel © (13.05.10 14:08) [6]
Другое дело, что вокруг WinSock можно написать свои простенькие обертки, больше подходящие к конкретной задаче - в этом их удобство.
← →
kernel © (2010-05-14 21:46) [9]Спасибо за ответы, DVM!
Сделал свой класс на основе ServerSocket, все работает быстро и замечательно [тьфу-тьфу-тьфу], контроля полностью хватает.
PS: собст-но это сообщение и отправил через пробный прокси :)
← →
kernel © (2010-05-18 17:36) [10]Всем привет! Чтобы не создавать отдельный топик, спрошу здесь (т.к. разговор связан с этой темой).
Организовал нужный мне функционал на базе 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 ...) с этого сокета становится все время равно нулю. При этом, оба соединения не дисконнэктятся (хотя с чего бы им разрываться, если данные "недопередались").
Вопрос: Что я делаю не так? :)
← →
kernel © (2010-05-18 17:40) [11]То есть, проще говоря, через некоторое время цикл "крутится", а данные не хочет отдавать ни один сокет :/
← →
Slym © (2010-05-19 08:06) [12]
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);
← →
kernel © (2010-05-19 15:48) [13]Спасибо, Slym! Немного позже попробую даный код и отпишусь здесь :)
← →
kernel © (2010-05-19 20:35) [14]Еще раз огромное спасибо, Slym! Первые тесты туннелирования пока дали успешный результат.
PS: FPortMap.ClientTimeout поставил = 60000 [мс] для пробы. Хотя в Инди HTTP прокси, например, таймаут на каждое подключение при туннелировании = 100 мс. Какое оптимальное значение можно поставить? :)
← →
Slym © (2010-05-20 07:58) [15]kernel © (19.05.10 20:35) [14]
Какое оптимальное значение можно поставить?
10сек вполне хватит
← →
kernel © (2010-05-20 18:53) [16]
> Slym © (20.05.10 07:58) [15]
Спасибо! :)
Страницы: 1 вся ветка
Форум: "Сети";
Текущий архив: 2014.12.28;
Скачать: [xml.tar.bz2];
Память: 0.51 MB
Время: 0.002 c