Форум: "Сети";
Текущий архив: 2007.03.11;
Скачать: [xml.tar.bz2];
ВнизСерьезный TCP сервер. Найти похожие ветки
← →
Николас (2006-09-22 09:15) [0]Стоит задача в обслуживании большого колличества клиентов, 100-1000 одновременных TCP соединений с постоянным обменом данными... Причем связь с сервером будет осуществлятся по разным каналам, от GPRS до LAN. Сейчас это реализовано на WinAPI Winsock. На локальной машине все работает хорошо для одного клиента, но достаточно попробова подключиться по не очень хорошему каналу связи, то начинаются потери пакетов, реализация протокола не допускает потери даже одного пакета, поэтому должна быть надежная реализация сервера.
Основной сетевой код работает в основном потоке, осуществляет обработку событий сокета и отправляет пришедшие данные на обработку. Есть проблема в приеме данных, когда пришла только часть данных от одного клиента, то мы ждем остальных данных, функция обработки полученных данных вызвается пока не придут все данные и пока мы не удалим их из очереди сокета, это сильно нагружает процессор, вариант со SLeep(100) снимает нагрузку с процессора, но так же усыпляет весь поток на 100мс, что критично для данного способа, ведь остальные клиенты должнв продолжать работу...
Неужели придется создавать для каждого клиента отдельный поток ? Есть у вас варианты для реализации? Может уже готовый компонент использовать?
← →
Орион © (2006-09-22 09:27) [1]Тут есть намного большие специалисты, чем я, но раз уж их пока нет :))
> Неужели придется создавать для каждого клиента отдельный
> поток ?
Да.
> Есть у вас варианты для реализации?
> Может уже готовый компонент использовать?
Indy используй. Уже все реализовано за тебя ;)
Компонент - TIdTCPServer
← →
Ketmar © (2006-09-22 09:33) [2]логика изначально неверна. если "ждать" -- то блокирующие сокеты и потоки. если не ждать, то неблокирующие. вообще -- зависит от того, насколько сложна обработка. и вообще -- лучше подобное на базе какой-нибудь *nix-системы делать.
← →
Орион © (2006-09-22 09:46) [3]Если же хочется именно неблокирующий режим, то ждать - не верно.
Для каждого клиента создавай буфер (TMemoryStream, TStringStream?) и по пришествии очередной порции данных сохраняй ее в буфер, далее прверяй последний ли это кусок, если да - бери данные из буфера, обарабатывай, очищяй буфер; если нет - ничего не делай :)
Но это гемморное решение. Блокирующий режим - самое оно.
← →
Ketmar © (2006-09-22 09:51) [4]>[3] Орион(c) 22-Sep-XLI A.S., 09:46
>Но это гемморное решение. Блокирующий режим -
>самое оно.
при 500 активных потоках это становится большим вопросом. %-)
← →
Орион © (2006-09-22 09:56) [5]Ketmar, тогда уже "Не серьезный TCP сервер" %))
← →
Ketmar © (2006-09-22 10:01) [6]>[5] Орион(c) 22-Sep-XLI A.S., 09:56
>Ketmar, тогда уже "Не серьезный TCP сервер" %))
а сервера подобного плана на винде держать -- это уже само по себе несерьёзно. имо.
← →
Николас (2006-09-22 10:12) [7]Давайте не будем про несерьезность? Сервер Lineage II к примеру под винду и держит несколько тысяч клиентов одновременно. Насколько я понял там используется IO Completion Port... кто-нибудь знает про это ?
Создавать 1000 потоков я думаю будет слишком жестоко...:(
← →
Сергей М. © (2006-09-22 10:21) [8]
> IO Completion Port... кто-нибудь знает про это ?
Кто-нибудь обязательно знает...
Например, справка по Winsock2 и MSDN ...
см. WSASend/Recv
> Создавать 1000 потоков я думаю будет слишком жестоко
Жестоко или не жестоко - это надо просчитывать, опираясь на интенсивность клиентских запросов.
← →
Ketmar © (2006-09-22 10:22) [9]>[7] Николас 22-Sep-XLI A.S., 10:12
>Lineage II к примеру под винду и держит несколько
>тысяч клиентов одновременно.
что совершенно не говорит о необходимости дублирования сией порочной практики. %-)
← →
Polevi © (2006-09-22 10:44) [10]>Ketmar © (22.09.06 10:01) [6]
несерьезно делать подобные заявления
← →
Ketmar © (2006-09-22 10:46) [11]>[10] Polevi(c) 22-Sep-XLI A.S., 10:44
>>Ketmar c (22.09.06 10:01) [6]
>несерьезно делать подобные заявления
а я там не зря "имо" приписал. %-)
← →
Николас (2006-09-22 11:06) [12]Сейчас код примерно такой:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, WinSock, StdCtrls ;
const
ServerPort = "8000";
ServerAddr = "127.0.0.1";
WM_SERVERSOCKETEVENT = WM_USER + 1 ;
type
TForm1 = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure WMServerSocketEventHandler(var Msg: TMessage); message WM_SERVERSOCKETEVENT;
public
{ Public declarations }
procedure ReadFromServerSocket(Sock: Tsocket);
end;
var
Form1: TForm1;
ServerSock: TSocket;
world_sock_event_mask: integer;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
Data:TWSAData;
Addr:TSockAddr;
Block:u_long;
begin
//Ñåòåâàÿ èíèöèàëèçàöèÿ
WSAStartup($0101, Data);
// Îáû÷íàÿ ïîñëåäîâàòåëü&# 237;îñòü äåéñòâèé ïî ñîçäàíèþ ñîêåòà,
// ïðèâÿçêå åãî ê àäðåñó è óñòàíîâëåíèþ íà ïðîñëóøèâàíèå
ServerSock:=Socket(AF_Inet,Sock_stream,0);
Addr.sin_family:=PF_Inet;
Addr.sin_addr.S_addr:=Inet_addr(PChar(ServerAddr));
Addr.sin_port:=HToNS(StrToInt(ServerPort));
FillChar(Addr.sin_zero,SizeOf(Addr.sin_zero),0);
Bind(ServerSock,Addr,SizeOf(Addr));
Listen(ServerSock,SOMaxConn);
world_sock_event_mask:=FD_Read or FD_Accept or FD_Close;
WSAAsyncSelect(ServerSock, Form1.Handle, WM_SERVERSOCKETEVENT, world_sock_event_mask);
// Block:=0;
// IOCtlSocket(ServerSock,FIONBIO,Block);
Memo1.Lines.Add("*** Server listen on port " + ServerPort + ", IP " + ServerAddr);
end;
procedure TForm1.WMServerSocketEventHandler(var Msg: TMessage);
var
Sock: TSocket;
SockError,AcceptResult: Integer;
Addr: TSockAddr;
Len: Integer;
SockEvent: Word;
begin
try
Sock:=TSocket(Msg.WParam);
//Ïðîâåðÿåì íà íàëè÷èå îøèáêè
SockError := WSAGetSelectError(Msg.lParam);
if SockError <> 0 then begin
Memo1.Lines.Add("SockError sock="+IntToStr(Sock)+", error="+IntToStr(SockError));
CloseSocket(Sock);
Exit ;
end;
SockEvent := WSAGetSelectEvent(Msg.lParam) ;
//Memo1.Lines.Add("DEBUG SockEvent=" + IntToStr(SockEvent));
case SockEvent of
FD_CONNECT: Memo1.Lines.Add("CONNECT");
FD_OOB: Memo1.Lines.Add("OOB");
FD_Read: ReadFromServerSocket(Sock);
FD_Accept: begin
AcceptResult:=Accept(Sock,@Addr,@Len);
Memo1.Lines.Add("*** New connection sock="+IntToStr(AcceptResult)); //+" Worldsock sock="+IntToStr(Sock)+" Addr="+inet_ntoa(Addr.sin_addr)+" port="+IntToStr(ntohs(Addr.sin_port))+" Len="+IntToStr(Len));
Sleep(1000);
end;
FD_Close: begin
Memo1.Lines.Add("*** Close connection sock=" + IntToStr(sock));
Shutdown(Sock,SD_Send);
CloseSocket(Sock);
end;
FD_Write: ;
end ;
except
Memo1.Lines.Add("WMWorldSocketEventHandler error");
end;
end;
procedure TForm1.ReadFromServerSocket(Sock: Tsocket);
var
ByteBuff: array of Byte ;
received_len: Integer;
begin
try
SetLength(ByteBuff, 6);
received_len:=Recv(Sock,ByteBuff[0], 6, 0);
Memo1.Lines.Add("ReadFromServerSocket");
except
end;
end;
end.
← →
Сергей М. © (2006-09-22 11:12) [13]
> Николас (22.09.06 11:06) [12]
Вся обработка клиентских запросов здесь сводится к простейшему
> Memo1.Lines.Add("ReadFromServerSocket");
поэтому пока не вижу повода для волнений по сабжу ..
← →
Eraser © (2006-09-22 15:57) [14]> [0] Николас (22.09.06 09:15)
присоединяюсь к тем, кто советует использовать Инди. В 10 версии есть возможность использовать вместо потоков - волокна, что может ощутимо добавить производительности мощному серверу.
← →
Сергей М. © (2006-09-22 16:00) [15]
> Eraser © (22.09.06 15:57) [14]
Да нахрен автору волокна, если он и в нитях-то плавает)...
Ему обычный пул нитей, возможно, к месту будет ...
← →
Eraser © (2006-09-22 16:06) [16]> [15] Сергей М. © (22.09.06 16:00)
ну вот, тем более, для начала инди - самое оно, с настройкам по-умолчанию. да и принцип работы блокирующих сокетов проще понять, чем не блокирующих.
← →
Сергей М. © (2006-09-22 16:31) [17]
> Eraser © (22.09.06 16:06) [16]
Насчет "проще понять" - не согласен.
Есть TServerSocket, есть режим stThreadBlocking - куда уж проще исследовать именно его код, нежели перегруженный кучей идиотских наворотов "на перспективу" индейский код)
← →
Eraser © (2006-09-22 16:38) [18]> [17] Сергей М. © (22.09.06 16:31)
я имел ввиду, что проще понять блокирующий режим, а не компоненты инди :)
> Есть TServerSocket, есть режим stThreadBlocking - куда уж
> проще исследовать именно его код, нежели перегруженный кучей
> идиотских наворотов "на перспективу" индейский код)
согласен, только вот, AFAIK, реализации под .NET netу у этих компонентов, а массовый преход на эту "переспективную" платфору не за горами.
← →
Сергей М. © (2006-09-22 16:44) [19]
> массовый преход на эту "переспективную" платфору не за горами
Не за горами и 9-й вал вопросов на эту тему, ибо с насаживанием идиотской концепции а-ля "всем нужна, без нее никак" все больше и больше батонокидателей оказываются зза барьером происходящего по сути.
← →
Eraser © (2006-09-22 16:52) [20]> [19] Сергей М. © (22.09.06 16:44)
> ибо с насаживанием идиотской концепции а-ля "всем нужна,
> без нее никак"
в висте над этим постарались. теперь, прежде чем запутить обычное не_managed приложение, система 10 раз спросит делать это или нет, при этом "сервис", который задает эти вопросы мне лично отключить так и не удалось, вернее его нету в разделе "сервисы" да и вообще нигде, по-моему это жестко прошито в каком-то системном процессе и не имеет опции "отключить". С .NET приложениями таких заморочек нет. Так что через пол годика начнется "бум". imho мелкомягкие своего добились.
← →
y-soft © (2006-09-22 22:50) [21]>Николас (22.09.06 10:12) [7]
Сервер Lineage II к примеру под винду и держит несколько тысяч клиентов одновременно. Насколько я понял там используется IO Completion Port... кто-нибудь знает про это ?
Именно так. Где-то до 65000 соединений с помощью портов завершения держать можно при приемлемой затрате ресурсов. К тому же приложение станет масштабируемым. Все остальные модели работают гораздо хуже, а для блокирующих сокетов 1000 соединений это вообще реальный предел.
Обычно от этой технологии отпугивает кажущаяся сложность, но на самом деле ничего сложного нет, а при использовании функций системного пула потоков получается еще проще...
Про использование портов завершения с сокетами читайте у Оланда и Джонса, про функции системного пула потоков - у Рихтера...
Создавать 1000 потоков я думаю будет слишком жестоко...:(
Не только жестоко, но и весьма расточительно, даже если потоки основную часть времени будут находиться в ожидании. И медленнее из-за необходимости переключения контекстов. И загрузка процессора может подскакивать до 100% :(
При использовании портов завершения потоков может быть и всего пара вне зависимости от количества соединений, а при использовании BindIoCompletionCallback(...) даже и не придется заботиться об их создании и уничтожении...
← →
добрый (2006-09-23 21:07) [22]автор пишет эмулятор сервера WoW? :) киддисы. когда же они успокоятся.
← →
Орион © (2006-09-23 22:58) [23]> [22] добрый (23.09.06 21:07)
Откуда такая информация?
Или привеленные куски кода похожи на твой собственный эмулятор ВОВ?)))
← →
Николас (2006-09-24 04:07) [24]Сергей М. сейчас реализован пул нитей для исходящих пакетов, пропажи пакетов исчезли, думаю и для входящих подобное реализовать, но как это придумать пока в голову ничего не лезет :)
y-soft спасибо за разъяснения :) Не найдется у тя примера работы с IOCP на дельфи? Весь инет перерыл, нашел только у мелкософта на С++ :(
добрый, ага, WoW
← →
y-soft © (2006-09-24 08:21) [25]>Николас (24.09.06 04:07) [24]
Не найдется у тя примера работы с IOCP на дельфи?
Увы, дать свой код будет неэтично по отношению к работодателю - ведь программированием занимаюсь не по вечерам в свое удовольствие, а за зарплату в рабочее время...
Но на разумные вопросы ответить готов
← →
Николас (2006-09-24 14:04) [26]y-soft хотя бы помоги немного :)
Тут пример набросал работы, но не вызывается событие когда приходят данные :(
http://webfile.ru/56873/IOCPDemo.rar
← →
y-soft © (2006-09-24 17:30) [27]>Николас (24.09.06 14:04) [26]
Зря Вы выложили на Webfile.ru - при попытке скачать ничего не происходит
y-soft хотя бы помоги немного :)
Я же сказал - на разумные вопросы ответить готов
но не вызывается событие когда приходят данные :(
А AссeptEx вызывали для сокета? В очередь порта завершения его ставили?
Поставили ли в очередь порта слушающий сокет? Каким образом производите рассоединение?...
Приведите хотя бы общий порядок действий, а то трудно заниматься телепатией :)
А события завершения операций ввода/вывода приходят через функцию обратного вызова, точно так же, как и события соединения и рассоединения
← →
Николас (2006-09-24 23:26) [28]Перезалил сюда: http://rapidshare.de/files/34309493/IOCPDemo.rar.html
Все события кроме read обрабатываются нормально, потоки Accept и Worker создаются в зависимости от оклчисества процессоров, вообещем глянь код, не буду тут вставлять большие куски :)
← →
y-soft © (2006-09-25 08:32) [29]>Николас (24.09.06 23:26) [28]
Глянул навскидку Ваш код. Сейчас пока очень много работы, так что детально вникать пока не могу, постараюсь сделать это позже.
Но сразу могу сказать - класс, который Вы используете, написан неоптимально, реализация сводит на нет многие преимущества использования порта завершения :(
Что сразу же бросилось в глаза:
1. Сокеты для клиентских соединений создаются динамически, вызовом Accept, гораздо эффективнее создать сразу необходимое количество сокетов, а затем привязать из к слушающему вызовами AcceptEx... Появятся, правда, новые заботы - например, корректно закрывать клиентское соединение для повторного использования и учитывать состояние TCP WAIT_TIME...
2. Выгоднее все заботы о порте завершения переложить на систему. Один вызов BindIOCompletionCallback заменит множество строчек кода, и отпадет необходимость в потоках TAcceptThread и TWorkerThread
Продолжу позже - постараюсь описать алгоритм эффективного использования, а сейчас зовет труба :)
← →
vidiv © (2006-09-29 20:18) [30]А если создать для всех соединений один отдельный поток и использовать функцию select ? Такой вариант не подходит?
← →
Николас (2006-10-03 08:17) [31]vidiv НЕТ :) Представь 1000 клиентов которые постоянно обмениваются данными :)
Страницы: 1 вся ветка
Форум: "Сети";
Текущий архив: 2007.03.11;
Скачать: [xml.tar.bz2];
Память: 0.55 MB
Время: 0.042 c