-
Сервер на асинхронных событиях. В нем есть нить для обработки подключений клиентов и по одной нити на каждого клиента, а главная нить только создает слушающий сокет и запускает обслуживающую его нить.
Проблема в том, что на кадое новое подключение создается новый поток. А подключившихся клиентов будет несколько тысяч ? Сервер неприменно помрет.
Помогите переделать сервер так, чтобы клиентский поток обслуживал не одного клиента, а пакет из нескольких клиентов.
Написанный ниже пример рабочий и стабильный. Надеюсь найдутся ребята с доброй душой. Кому нужно, вышлю полностью исходник.
-
unit ListenThread;
interface
uses
SysUtils, Classes, WinSock, WinSock2_Events;
type
TListenThread = class(TThread)
private
FMessage: string;
FServerSocket: TSocket;
FEvents: array[0..1] of TWSAEvent;
FClientThreads: TList;
FServerMsg: Boolean;
procedure DoLogMessage;
protected
procedure Execute; override;
procedure LogMessage(const Msg: string);
public
constructor Create(ServerSocket: TSocket; ServerMsg: Boolean);
destructor Destroy; override;
procedure StopServer;
end;
implementation
uses
MainServerUnit, ClientThread;
constructor TListenThread.Create(ServerSocket: TSocket; ServerMsg: Boolean);
begin
FServerSocket := ServerSocket;
FServerMsg := ServerMsg;
FEvents[0] := WSACreateEvent;
if FEvents[0] = WSA_INVALID_EVENT then
raise ESocketError.Create('Ошибка при создании события для сервера: ' + GetErrorString);
FEvents[1] := WSACreateEvent;
if FEvents[1] = WSA_INVALID_EVENT then
raise ESocketError.Create('Ошибка при создании события для сервера: ' + GetErrorString);
if WSAEventSelect(FServerSocket, FEvents[1], FD_ACCEPT) = SOCKET_ERROR then
raise ESocketError.Create('Ошибка при привязывании серверного сокета к событию: ' + GetErrorString);
FClientThreads := TList.Create;
inherited Create(False);
end;
destructor TListenThread.Destroy;
begin
FClientThreads.Free;
WSACloseEvent(FEvents[0]);
WSACloseEvent(FEvents[1]);
inherited;
end;
-
продолжение модуля ListenThreadprocedure TListenThread.Execute;
var
ClientSocket: TSocket;
ClientAddr: TSockAddr;
ClientAddrLen: Integer;
NetEvents: TWSANetworkEvents;
I: Integer;
WaitRes: Cardinal;
begin
LogMessage('Сервер начал работу');
repeat
WaitRes := WSAWaitForMultipleEvents(2, @FEvents, False, 15000, False);
case WaitRes of
WSA_WAIT_EVENT_0:
begin
LogMessage('Сервер получил сигнал завершения работы');
Break;
end;
WSA_WAIT_EVENT_0 + 1:
begin
if WSAEnumNetworkEvents(FServerSocket, FEvents[1], NetEvents) = SOCKET_ERROR then
begin
LogMessage('Ошибка при получении списка событий: ' + GetErrorString);
Break;
end;
if NetEvents.lNetworkEvents and FD_ACCEPT = 0 then
begin
LogMessage('Внутренняя ошибка сервера - неизвестное событие');
Break;
end;
if NetEvents.iErrorCode[FD_ACCEPT_BIT] <> 0 then
begin
LogMessage('Ошибка при подключении клиента: ' + GetErrorString(NetEvents.iErrorCode[FD_ACCEPT_BIT]));
Break;
end;
ClientAddrLen := SizeOf(ClientAddr);
ClientSocket := accept(FServerSocket, @ClientAddr, @ClientAddrLen);
if ClientSocket = INVALID_SOCKET then
begin
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
LogMessage('Ошибка при подключении клиента: ' + GetErrorString);
Break;
end;
end;
FClientThreads.Add(TClientThread.Create(ClientSocket, ClientAddr));
end;
WSA_WAIT_TIMEOUT:
begin
for I := FClientThreads.Count - 1 downto 0 do
if TClientThread(FClientThreads[I]).Finished then
begin
TClientThread(FClientThreads[I]).Free;
FClientThreads.Delete(I);
end;
if FServerMsg then
for I := 0 to FClientThreads.Count - 1 do
TClientThread(FClientThreads[I]).SendString('Время на сервере ' + TimeToStr(Now));
end;
WSA_WAIT_FAILED:
begin
LogMessage('Ошибка при ожидании события сервера: ' + GetErrorString);
Break;
end;
else
begin
LogMessage('Внутренняя ошибка сервера - неожиданный результат ожидания ' + IntToStr(WaitRes));
Break;
end;
end;
until False;
for I := 0 to FClientThreads.Count - 1 do
begin
TClientThread(FClientThreads[I]).StopThread;
TClientThread(FClientThreads[I]).WaitFor;
TClientThread(FClientThreads[I]).Free;
end;
closesocket(FServerSocket);
LogMessage('Сервер завершил работу');
Synchronize(ServerForm.OnStopServer);
end;
procedure TListenThread.StopServer;
begin
WSASetEvent(FEvents[0]);
end;
procedure TListenThread.LogMessage(const Msg: string);
begin
FMessage := Msg;
Synchronize(DoLogMessage);
end;
procedure TListenThread.DoLogMessage;
begin
ServerForm.AddMessageToLog(FMessage);
end;
end.
-
unit ClientThread;
interface
uses
Windows, Classes, WinSock, Winsock2_Events, ShutdownConst, SysUtils, SyncObjs;
type
TClientThread = class(TThread)
private
FMessage: string;
FHeader: string;
FSocket: TSocket;
FEvents: array[0..2] of TWSAEvent;
FSendBufSection: TCriticalSection;
FSendBuf: string;
procedure DoLogMessage;
function GetFinished: Boolean;
protected
procedure Execute; override;
procedure LogMessage(const Msg: string);
function DoSendBuf: Boolean;
public
constructor Create(ClientSocket: TSocket; const ClientAddr: TSockAddr);
destructor Destroy; override;
procedure SendString(const S: string);
procedure StopThread;
property Finished: Boolean read GetFinished;
end;
ESocketError = class(Exception);
implementation
uses
MainServerUnit;
constructor TClientThread.Create(ClientSocket: TSocket; const ClientAddr: TSockAddr);
begin
FSocket := ClientSocket;
FHeader := 'Сообщение от клиента ' + inet_ntoa(ClientAddr.sin_addr) + ':' +
IntToStr(ntohs(ClientAddr.sin_port)) + ': ';
FEvents[0] := WSACreateEvent;
if FEvents[0] = WSA_INVALID_EVENT then
raise ESocketError.Create(FHeader + 'Ошибка при создании события: ' + GetErrorString);
FEvents[1] := WSACreateEvent;
if FEvents[1] = WSA_INVALID_EVENT then
raise ESocketError.Create(FHeader + 'Ошибка при создании события: ' + GetErrorString);
FEvents[2] := WSACreateEvent;
if FEvents[2] = WSA_INVALID_EVENT then
raise ESocketError.Create(FHeader + 'Ошибка при создании события: ' + GetErrorString);
if WSAEventSelect(FSocket, FEvents[2], FD_READ or FD_WRITE or FD_CLOSE) = SOCKET_ERROR then
raise ESocketError.Create(FHeader + 'Ошибка при привязывании сокета к событию: ' + GetErrorString);
FSendBufSection := TCriticalSection.Create;
FreeOnTerminate := False;
inherited Create(False);
end;
destructor TClientThread.Destroy;
begin
FSendBufSection.Free;
WSACloseEvent(FEvents[0]);
WSACloseEvent(FEvents[1]);
WSACloseEvent(FEvents[2]);
inherited;
end;
procedure TClientThread.SendString(const S: string);
begin
FSendBufSection.Enter;
try
FSendBuf := FSendBuf + S + #0;
finally
FSendBufSection.Leave;
end;
LogMessage('Сообщение \"' + S + '\" поставлено в очередь для отправки');
WSASetEvent(FEvents[1]);
end;
function TClientThread.DoSendBuf: Boolean;
var
SendRes: Integer;
begin
FSendBufSection.Enter;
try
if FSendBuf = '' then
begin
Result := True;
Exit;
end;
SendRes := send(FSocket, FSendBuf[1], Length(FSendBuf), 0);
if SendRes > 0 then
begin
Delete(FSendBuf, 1, SendRes);
Result := True;
end
else
begin
Result := WSAGetLastError = WSAEWOULDBLOCK;
if not Result then
LogMessage('Ошибка при отправке данных: ' + GetErrorString);
end;
finally
FSendBufSection.Leave;
end;
end;
-
продолжение модуля ClientThread
procedure TClientThread.Execute;
const
RecvBufSize = 4096;
var
RecvBuf: array[0..RecvBufSize - 1] of Byte;
RecvRes: Integer;
NetEvents: TWSANetworkEvents;
Str: string;
StrLen: Integer;
ReadLength: Boolean;
Offset: Integer;
BytesLeft: Integer;
P: Integer;
L: Integer;
LoopExit: Boolean;
WaitRes: Cardinal;
begin
LogMessage('Соединение установлено');
ReadLength := True;
Offset := 0;
BytesLeft := SizeOf(Integer);
repeat
WaitRes := WSAWaitForMultipleEvents(3, @FEvents, False, WSA_INFINITE, False);
case WaitRes of
WSA_WAIT_EVENT_0:
begin
LogMessage('Получен сигнал об остановке нити');
shutdown(FSocket, SD_BOTH);
Break;
end;
WSA_WAIT_EVENT_0 + 1:
begin
WSAResetEvent(FEvents[1]);
if not DoSendBuf then
Break;
end;
WSA_WAIT_EVENT_0 + 2:
begin
if WSAEnumNetworkEvents(FSocket, FEvents[2], NetEvents) = SOCKET_ERROR then
begin
LogMessage('Ошибка при получении списка событий: ' + GetErrorString);
Break;
end;
if NetEvents.lNetworkEvents and FD_READ <> 0 then
begin
if NetEvents.iErrorCode[FD_READ_BIT] <> 0 then
begin
LogMessage('Ошибка в событии FD_READ: ' + GetErrorString(NetEvents.iErrorCode[FD_READ_BIT]));
Break;
end;
RecvRes := recv(FSocket, RecvBuf, SizeOf(RecvBuf), 0);
if RecvRes > 0 then
begin
P := 0;
LoopExit := False;
while P < RecvRes do
begin
L := BytesLeft;
if P + L > RecvRes then
L := RecvRes - P;
if ReadLength then
Move(RecvBuf[P], (PChar(@StrLen) + Offset)^, L)
else
Move(RecvBuf[P], Str[Offset + 1], L);
Dec(BytesLeft, L);
if BytesLeft = 0 then
begin
ReadLength := not ReadLength;
Offset := 0;
if ReadLength then
begin
LogMessage('Получена строка: ' + Str);
BytesLeft := SizeOf(Integer);
Str := AnsiUpperCase(StringReplace(Str, #0, '#0', [rfReplaceAll])) + ' (EventSelect server)';
SendString(Str);
Str := '';
end
else
begin
if StrLen <= 0 then
begin
LogMessage('Неверная длина строки от клиента: ' + IntToStr(StrLen));
LoopExit := True;
Break;
end;
BytesLeft := StrLen;
SetLength(Str, StrLen);
end;
end
else
Inc(Offset, L);
Inc(P, L);
end;
if LoopExit then
Break;
end
else if RecvRes = 0 then
begin
LogMessage('Клиент закрыл соединение');
Break;
end
else
begin
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
LogMessage('Ошибка при получении данных от клиента: ' + GetErrorString);
end;
end;
end;
if NetEvents.lNetworkEvents and FD_WRITE <> 0 then
begin
if NetEvents.iErrorCode[FD_WRITE_BIT] <> 0 then
begin
LogMessage('Ошибка в событии FD_WRITE: ' + GetErrorString(NetEvents.iErrorCode[FD_WRITE_BIT]));
Break;
end;
if not DoSendBuf then
Break;
end;
if NetEvents.lNetworkEvents and FD_CLOSE <> 0 then
begin
if NetEvents.iErrorCode[FD_CLOSE_BIT] <> 0 then
begin
LogMessage('Ошибка в событии FD_CLOSE: ' + GetErrorString(NetEvents.iErrorCode[FD_CLOSE_BIT]));
Break;
end;
LogMessage('Клиент закрыл соединение');
shutdown(FSocket, SD_BOTH);
Break;
end;
end;
WSA_WAIT_FAILED:
begin
LogMessage('Ошибка при ожидании сообщения: ' + GetErrorString);
Break;
end;
else
begin
LogMessage('Внутренняя ошибка сервера - неверный результат ожидания ' + IntToStr(WaitRes));
Break;
end;
end;
until False;
closesocket(FSocket);
LogMessage('Нить остановлена');
end;
-
продолжение модуля ClientThread
function TClientThread.GetFinished: Boolean;
begin
Result := WaitForSingleObject(Handle, 0) = WAIT_OBJECT_0;
end;
procedure TClientThread.StopThread;
begin
WSASetEvent(FEvents[0]);
end;
procedure TClientThread.LogMessage(const Msg: string);
begin
FMessage := FHeader + Msg;
Synchronize(DoLogMessage);
end;
procedure TClientThread.DoLogMessage;
begin
ServerForm.AddMessageToLog(FMessage);
end;
end.
-
unit MainServerUnit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, WinSock, ListenThread;
type
TServerForm = class(TForm)
GroupBox1: TGroupBox;
LabelPortNumber: TLabel;
EditPortNumber: TEdit;
BtnStartServer: TButton;
LabelServerState: TLabel;
MemoLog: TMemo;
BtnStopServer: TButton;
ChkBoxServerMsg: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure BtnStartServerClick(Sender: TObject);
procedure BtnStopServerClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
FListenThread: TListenThread;
procedure StopServer;
public
procedure AddMessageToLog(const Msg: string);
procedure OnStopServer;
end;
var
ServerForm: TServerForm;
function GetErrorString(Error: Integer = 0): string;
implementation
function GetErrorString(Error: Integer): string;
var
Buffer: array[0..2047] of Char;
begin
if Error = 0 then
Error := WSAGetLastError;
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil, Error, $400,
@Buffer, SizeOf(Buffer), nil);
Result := Buffer;
end;
procedure TServerForm.AddMessageToLog(const Msg: string);
begin
MemoLog.Lines.Add(Msg);
MemoLog.SelStart := Length(MemoLog.Text);
end;
procedure TServerForm.OnStopServer;
begin
LabelPortNumber.Enabled := True;
EditPortNumber.Enabled := True;
BtnStartServer.Enabled := True;
BtnStopServer.Enabled := False;
ChkBoxServerMsg.Enabled := True;
LabelServerState.Caption := 'Сервер не работает';
FListenThread := nil;
end;
procedure TServerForm.FormCreate(Sender: TObject);
var
WSAData: TWSAData;
begin
if WSAStartup($101, WSAData) <> 0 then
begin
MessageDlg('Ошибка при инициализации библиотеки WinSock', mtError, [mbOK], 0);
Application.Terminate;
end;
OnStopServer;
end;
procedure TServerForm.BtnStartServerClick(Sender: TObject);
var
ServerSocket: TSocket;
ServerAddr: TSockAddr;
begin
FillChar(ServerAddr.sin_zero, SizeOf(ServerAddr.sin_zero), 0);
ServerAddr.sin_family := AF_INET;
ServerAddr.sin_addr.S_addr := INADDR_ANY;
try
ServerAddr.sin_port := htons(StrToInt(EditPortNumber.Text));
if ServerAddr.sin_port = 0 then
begin
MessageDlg('Номер порта должен лежать в диапазоне 1-65535', mtError, [mbOK], 0);
Exit;
end;
ServerSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if ServerSocket = INVALID_SOCKET then
begin
MessageDlg('Ошибка при создании сокета:'#13#10 + GetErrorString, mtError, [mbOK], 0);
Exit;
end;
if bind(ServerSocket, ServerAddr, SizeOf(ServerAddr)) = SOCKET_ERROR then
begin
MessageDlg('Ошибка при привязке сокета к адресу:'#13#10 + GetErrorString, mtError, [mbOK], 0);
closesocket(ServerSocket);
Exit;
end;
if listen(ServerSocket, SOMAXCONN) = SOCKET_ERROR then
begin
MessageDlg('Ошибка при перводе сокета в режим прослушивания:'#13#10 + GetErrorString, mtError, [mbOK], 0);
closesocket(ServerSocket);
Exit;
end;
FListenThread := TListenThread.Create(ServerSocket, ChkBoxServerMsg.Checked);
LabelPortNumber.Enabled := False;
EditPortNumber.Enabled := False;
BtnStartServer.Enabled := False;
BtnStopServer.Enabled := True;
ChkBoxServerMsg.Enabled := False;
LabelServerState.Caption := 'Сервер работает';
except
on EConvertError do
MessageDlg('\"' + EditPortNumber.Text + '\" не является целым числом', mtError, [mbOK], 0);
on ERangeError do
MessageDlg('Номер порта должен лежать в диапазоне 1-65535', mtError, [mbOK], 0);
end;
end;
procedure TServerForm.StopServer;
begin
BtnStopServer.Enabled := False;
if Assigned(FListenThread) then
begin
FListenThread.StopServer;
while Assigned(FListenThread) do
begin
Application.ProcessMessages;
Sleep(10);
end;
end;
end;
procedure TServerForm.BtnStopServerClick(Sender: TObject);
begin
StopServer;
end;
procedure TServerForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
StopServer;
end;
end.
-
AcceptEx ConnectEx CreateIoCompletionPort GetQueuedCompletionStatus
и 1000+ клиентов и серверов в одном доп. потоке.
Коли шибко нужно - могу и исходник предложить
-
Да, очень нужно! Исходником был бы очень рад. Мой ящик xss22@mail.ru
Заранее огромнейшее спасибо
-
> xss22 (15.02.2012 20:54:00) [0]
Как бы не так, ассинхронные события и потоки. Делай все в главной нити, по событиям, а потоки только для длительных операций работы с базой, да и то только пул потоков
-
> xss22 (16.02.2012 08:03:08) [8]
Есть такой продукт как ICS, вот примеры к нему легко доступны, возьми их для понимания как все это делается в одном потоке.
-
Если объем данных в запросах к серверу и его соотв.ответах на эти запросы сравнительно небольшой, но при этом обработка сервером клиентских запросов времяемкая, то вне зависимости от модели работы с гнездами (синхронная или асинхронная) можно реализовать след.схему:
- транспортную логику - акцептирование кл.запросов на соединение, прием инф.запросов и передача инф.ответов - реализует один-единственный поток "ListeningTransportThread" (например, основной)
- логику обработки принятых инф.запросов и формирования инф.ответов реализуют доп.потоки "RequestProcessingThread", помещенные в пул, размер которого не превышает определенного значения.
- логику распределения принятых инф.запросов по свободным потокам из пула реализует доп.поток "ThreadPoolManager"
-
А где по ICS можно документацию найти? Желательно на русском.
-
> xss22 (16.02.2012 11:58:12) [12]
Документпции практически нет, это халява сэр.
-
Anatoly Podgoretsky, ты можешь дать свой ICQ или скайп или майл агент? Чтобы иногда задать вопросы. Кстати, меня зовут Алексей.
-
> xss22 (16.02.2012 13:13:14) [14]
майл агента нет как класс. ICQ и скайп только представительские. email никому не даю. Для вопросов существует форум, на личные вопросы ругаюсь и не отвечаю.
-
Тогда у меня вопрос по ICS. Разбираю демку сервера OverbyteIcsThrdSrvV3 понятно как работать с подключенным клиентом. Но как найти в Client as TMyClient конкретно какого то клиента по Nick ? Прилагаю код демки: unit OverbyteIcsThrdSrvV3_1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, OverbyteIcsIniFiles, StdCtrls, ExtCtrls, OverbyteIcsWSocket, OverbyteIcsWSocketS,
OverbyteIcsWSocketTS, OverbyteIcsWndControl;
const
WM_APPSTARTUP = WM_USER + 1;
WM_LOG_MESSAGE = WM_USER + 2;
var
LockDisplay : TRtlCriticalSection;
type
TMyClient = class(TWSocketThrdClient)
public
RcvdLine1 : String;
ConnectTime : TDateTime;
Nick : String;
end;
TThrdSrvForm = class(TForm)
ToolPanel: TPanel;
DisplayMemo: TMemo;
ClientsPerThreadEdit: TEdit;
Label1: TLabel;
DisconnectAllButton: TButton;
ClearMemoButton: TButton;
WSocketThrdServer1: TWSocketThrdServer;
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure WSocketThrdServer1ClientConnect(Sender: TObject;
Client: TWSocketClient; Error: Word);
procedure WSocketThrdServer1ClientDisconnect(Sender: TObject;
Client: TWSocketClient; Error: Word);
procedure WSocketThrdServer1BgException(Sender: TObject; E: Exception;
var CanClose: Boolean);
procedure FormDestroy(Sender: TObject);
procedure WSocketThrdServer1ClientCreate(Sender: TObject;
Client: TWSocketClient);
procedure ClientsPerThreadEditChange(Sender: TObject);
procedure DisconnectAllButtonClick(Sender: TObject);
procedure ClearMemoButtonClick(Sender: TObject);
procedure WSocketThrdServer1ThreadException(Sender: TObject;
AThread: TWsClientThread; const AErrMsg: String);
private
FIniFileName : String;
FInitialized : Boolean;
FLogList : TStringList;
procedure Display(const Msg : String);
procedure WmLogMessage(var Msg: TMessage); message WM_LOG_MESSAGE;
procedure WMAppStartup(var Msg: TMessage); message WM_APPSTARTUP;
procedure ClientDataAvailable(Sender: TObject; Error: Word);
procedure ProcessData(Client : TMyClient);
procedure ClientBgException(Sender : TObject;
E : Exception;
var CanClose : Boolean);
procedure ClientLineLimitExceeded(Sender : TObject;
Cnt : LongInt;
var ClearData : Boolean);
public
property IniFileName : String read FIniFileName write FIniFileName;
end;
var
ThrdSrvForm : TThrdSrvForm;
implementation
uses
OverbyteIcsUtils;
procedure TThrdSrvForm.FormCreate(Sender: TObject);
begin
FIniFileName := OverbyteIcsIniFiles.GetIcsIniFileName;
FLogList := nil;
FLogList := TStringList.Create;
end;
procedure TThrdSrvForm.FormDestroy(Sender: TObject);
begin
EnterCriticalSection(LockDisplay);
try
if Assigned(FLogList) then
FLogList.Free;
finally
LeaveCriticalSection(LockDisplay);
end;
end;
procedure TThrdSrvForm.FormShow(Sender: TObject);
begin
if not FInitialized then begin
FInitialized := TRUE;
DisplayMemo.Clear;
PostMessage(Handle, WM_APPSTARTUP, 0, 0);
end;
end;
procedure TThrdSrvForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
end;
procedure TThrdSrvForm.WmLogMessage(var Msg: TMessage);
var
I : Integer;
begin
DisplayMemo.Lines.BeginUpdate;
try
if DisplayMemo.Lines.Count > 200 then begin
for I := 1 to 50 do
DisplayMemo.Lines.Delete(0);
end;
EnterCriticalSection(LockDisplay);
try
DisplayMemo.Lines.AddStrings(FLogList);
FLogList.Clear;
finally
LeaveCriticalSection(LockDisplay);
end;
finally
DisplayMemo.Lines.EndUpdate;
DisplayMemo.Perform(EM_SCROLLCARET, 0, 0);
end;
end;
procedure TThrdSrvForm.Display(const Msg : String);
begin
EnterCriticalSection(LockDisplay);
try
FLogList.Add(Msg);
PostMessage(Handle, WM_LOG_MESSAGE, 0, 0);
finally
LeaveCriticalSection(LockDisplay);
end;
end;
procedure TThrdSrvForm.WMAppStartup(var Msg: TMessage);
begin
WSocketThrdServer1.Proto := 'tcp';
WSocketThrdServer1.Port := '2211';
WSocketThrdServer1.Addr := '0.0.0.0';
WSocketThrdServer1.ClientClass := TMyClient;
WSocketThrdServer1.Listen;
Display('Waiting for clients...');
end;
-
продолжение кода из демки OverbyteIcsThrdSrvV3
procedure TThrdSrvForm.WSocketThrdServer1ClientCreate(Sender: TObject;
Client: TWSocketClient);
var
Cli : TMyClient;
begin
Cli := Client as TMyClient;
Cli.LineMode := TRUE;
Cli.LineEdit := TRUE;
Cli.LineLimit := 255;
Cli.OnDataAvailable := ClientDataAvailable;
Cli.OnLineLimitExceeded := ClientLineLimitExceeded;
Cli.OnBgException := ClientBgException;
Cli.ConnectTime := Now;
end;
procedure TThrdSrvForm.WSocketThrdServer1ClientConnect(
Sender : TObject;
Client : TWSocketClient;
Error : Word);
begin
with Client as TMyClient do begin
Display('Client connected.' +
' Remote: ' + PeerAddr + '/' + PeerPort +
' Local: ' + GetXAddr + '/' + GetXPort +
' ThrdID : $' + IntToStr(ClientThread.ThreadID) +
' ThrdCnt: #' + IntToStr(WSocketThrdServer1.ThreadCount) + #13#10 +
'There is now ' +
IntToStr(TWSocketThrdServer(Sender).ClientCount) +
' clients connected.');
Client.LineMode := TRUE;
Client.LineEdit := TRUE;
Client.LineLimit := 255;
Client.OnDataAvailable := ClientDataAvailable;
Client.OnLineLimitExceeded := ClientLineLimitExceeded;
Client.OnBgException := ClientBgException;
TMyClient(Client).ConnectTime := Now;
end;
end;
procedure TThrdSrvForm.WSocketThrdServer1ClientDisconnect(
Sender : TObject;
Client : TWSocketClient;
Error : Word);
var
MyClient : TMyClient;
ClientThreadID : Integer;
begin
MyClient := Client as TMyClient;
if Assigned(MyClient.ClientThread) then ClientThreadID := MyClient.ClientThread.ThreadID
else ClientThreadID := -1;
Display('Client disconnecting: ' + MyClient.PeerAddr + ' ' +
'Duration: ' + FormatDateTime('hh:nn:ss',
Now - MyClient.ConnectTime) + ' Error: ' + IntTostr(Error) +
' ThrdID: $' + IntToStr(ClientThreadID) +
' ThrdCnt: #' + IntToStr(TWSocketThrdServer(Sender).ThreadCount) + #13#10 +
'There is now ' +
IntToStr(TWSocketThrdServer(Sender).ClientCount - 1) +
' clients connected.');
end;
procedure TThrdSrvForm.ClientLineLimitExceeded(
Sender : TObject;
Cnt : LongInt;
var ClearData : Boolean);
begin
with Sender as TMyClient do begin
Display('Line limit exceeded from ' + GetPeerAddr + '. Closing.');
ClearData := TRUE;
Close;
end;
end;
procedure TThrdSrvForm.ClientDataAvailable(
Sender : TObject;
Error : Word);
var
Cli : TMyClient;
begin
Cli := Sender as TMyClient;
Cli.RcvdLine1 := Cli.ReceiveStr;
while (Length(Cli.RcvdLine1) > 0) and
IsCharInSysCharSet(Cli.RcvdLine1[Length(Cli.RcvdLine1)], [ #13, #10]) do
Cli.RcvdLine1 := Copy(Cli.RcvdLine1, 1, Length(Cli.RcvdLine1) - 1);
Display('Received from ' + Cli.GetPeerAddr + ': ''' + Cli.RcvdLine1 + '''');
ProcessData(Cli);
end;
procedure TThrdSrvForm.ProcessData(Client : TMyClient);
var
I : Integer;
AClient : TMyClient;
begin
if CompareText(Client.RcvdLine1, 'help') = 0 then
Client.SendStr('Commands are:' + #13#10 +
' exit' + #13#10 +
' who' + #13#10 +
' time' + #13#10 +
' threadexception' + #13#10 )
else if CompareText(Client.RcvdLine1, 'exit') = 0 then
Client.CloseDelayed
else if CompareText(Client.RcvdLine1, 'time') = 0 then
Client.SendStr(DateTimeToStr(Now) + #13#10)
else if CompareText(Client.RcvdLine1, 'who') = 0 then begin
Client.SendStr('There are ' + IntToStr(WSocketThrdServer1.ClientCount) +
' connected users:' + #13#10);
for I := WSocketThrdServer1.ClientCount - 1 downto 0 do begin
AClient := TMyClient(WSocketThrdServer1.Client[I]);
Client.SendStr(AClient.PeerAddr + ':' + AClient.GetPeerPort + ' ' +
DateTimeToStr(AClient.ConnectTime) + #13#10);
end;
end
exception') = 0 then
PostMessage(Client.Handle, WM_TRIGGER_EXCEPTION, 0, 0)
*)
else if CompareText(Client.RcvdLine1, 'threadexception') = 0 then
PostThreadMessage(GetCurrentThreadID, WM_THREAD_EXCEPTION_TEST, 0, 0)
else
if Client.State = wsConnected then
Client.SendStr('Unknown command: ''' + Client.RcvdLine1 + '''' + #13#10);
end;
-
продолжение кода из демки OverbyteIcsThrdSrvV3
procedure TThrdSrvForm.WSocketThrdServer1BgException(
Sender : TObject;
E : Exception;
var CanClose : Boolean);
begin
Display('Server exception occured: ' + E.ClassName + ': ' + E.Message);
CanClose := FALSE;
end;
procedure TThrdSrvForm.ClientBgException(
Sender : TObject;
E : Exception;
var CanClose : Boolean);
begin
with Sender as TMyClient do begin
Display('Client exception occured: ' + E.ClassName + ': ' + E.Message);
CanClose := TRUE;
end;
end;
procedure TThrdSrvForm.ClientsPerThreadEditChange(Sender: TObject);
var
Num : Integer;
begin
try
Num := StrToInt((Sender as TEdit).Text);
except
Num := 1;
end;
WSocketThrdServer1.ClientsPerThread := Num;
end;
procedure TThrdSrvForm.DisconnectAllButtonClick(Sender: TObject);
begin
WSocketThrdServer1.DisconnectAll;
end;
procedure TThrdSrvForm.ClearMemoButtonClick(Sender: TObject);
begin
DisplayMemo.Clear;
end;
procedure TThrdSrvForm.WSocketThrdServer1ThreadException(
Sender : TObject;
AThread : TWsClientThread;
const AErrMsg : String);
begin
Display(AErrMsg);
end;
initialization
InitializeCriticalSection(LockDisplay);
finalization
DeleteCriticalSection(LockDisplay);
end.
-
> xss22 (16.02.2012 13:56:16) [16]
А чего его искать, когда это поле в потоке Nick: String; Оно сразу доступно.
-
Не надо заваливать форум такими простынями, неудивляйся, что их будут удалять, достаточно опубликовать ссылку, заинтересованые лица смогут ее посмотреть, не мешая работе форума.
-
Anatoly Podgoretsky, Вы меня не поняли. Или я не правильно выразился. В общем подключено 20 клиентов. Каждый имеет свой уникальный Nick. Но иногда требуется отправить сервером пакет игрокам Nick='user3' и Nick='user5'. Как их найти в Client as TMyClient. Вот например еслиб у нас был массив игроков, например: Player : array of record
Nick :string[15];
PosX: Single;
PosY: Single;
PosZ: Single;
IdMap : string[4];
end; то я бы сделал вот так: for j := 0 to Length(Player)-1 do
if Player[j].Nick='user3' then
... Но как сделать подобное с нашим случаем, описанным выше в демке ???
-
> xss22 (16.02.2012 14:24:21) [21]
Если ты хочешь послать другому клиенту, то ты должен это где то хранить
-
> xss22
Ты так хорошо и за здравие начал про "Многонитевой TCP-сервер на асинхронных событиях" - и при этом так позорно и за упокой скатился до неумения разобраться в чужой тривиальной "демке")
|