Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 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
4-1270795936
Рацелий
2010-04-09 10:52
2014.12.28
Перехват текущей папки||Вызов vista balloon


2-1386432858
Sulim4ik
2013-12-07 20:14
2014.12.28
Красно-черные деревья


6-1273586835
kernel
2010-05-11 18:07
2014.12.28
Сервер. Сокеты, потоки.


2-1386575573
TheOwl
2013-12-09 11:52
2014.12.28
как отследить click за пределами формы


15-1401136202
Юрий
2014-05-27 00:30
2014.12.28
С днем рождения ! 27 мая 2014 вторник





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский