Использование компонента TServerSocket
|
Попал фидошник в ад. Ему черт говорит:
- Тебе определено 1000 лет наказания, но т. к. я люблю анекдоты, то разрешаю их тебе рассказывать. Пока ты их рассказываешь, этот срок будет вычтен из твоего наказания.
Ну, фидошник начинает травить анекдоты. Год... два... 100 лет... 900 лет прошло, он все рассказывает. Наконец, на 999 году замолкает.
- Все, анекдоты закончились? Ну, давай хоть год тебя помучаю.
- Да погоди ты, сейчас вторую часть офтопик-листа достану!
|
В Delphi документации по многопотоковому TServerSocket налито довольно много воды, и начинающему программисту сложно понять суть дела. Давайте попытаемся пролить немного света на этот раздел хелпа.
Вообще-то, создать многопотоковый сервер, который ожидает пришедшие сообщения на сокете довольно просто. В Delphi для этой цели достаточно использовать компонент TServerSocket.
Давайте рассмотрим структуру работы данного компонента:
- Добавляем TServerSocket в Вашу основную форму.
- Устанавливаем свойство Servertype в stThreadBlocking
- Создаём новый "unit" (показанный ниже) содержащий поток сервера.
Устанавливаем следующий код на OnSocketGetThread
procedure TfrmMain.fSocketGetThread(Sender: TObject;
ClientSocket: TServerClientWinSocket;
var
SocketThread: TServerClientThread);
begin
// Здесь создаём объект TServerThread, который я привожу ниже.
// Новый объект создаётся каждый раз, когда когда установлен канал связи.
SocketThread := TServerThread.Create( FALSE, ClientSocket );
end;
|
TServerThread - это объект, который я создаю самостоятельно. Объект наследуется от TServerClientThread и содержит код, который обычно читает и пишет данные из/в сокет.
Созданный Unit, содержит следующий код:
unit serverthread;
interface
uses
windows, scktcomp, SysUtils, Classes, Forms;
type
EServerThread = class( Exception );
// serverthread это потомок TServerClientThread
TServerThread = class( TServerClientThread )
private
fSocketStream : TWinSocketStream;
public
procedure ClientExecute; override;
// ClientExecute отменяет
// TServerClientThread.ClientExecute
// и содержит код, который
// выполняется при старте потока
end;
implementation
procedure TServerThread.ClientExecute;
begin
inherited FreeOnTerminate := TRUE;
try
fSocketStream := TWinSocketStream.Create( ClientSocket, 100000 );
// 100000 - это таймаут в миллисекундах.
try
while ( not Terminated ) and ( ClientSocket.Connected ) do
try
// В это место обычно помещается код,
// ожидающий входных данных, читающий из сокета или пишущий в него
// Пример, приведённый ниже, показывает, что можно добавить в данную
// секцию программы.
except
on e:exception do
begin
// Если произошла ошибка, то закрываем сокет и выходим
ClientSocket.Close;
Terminate;
end;
end;
finally
fSocketStream.Free;
end;
except
on e:exception do
begin
// Если произошла ошибка, то закрываем сокет и выходим
ClientSocket.Close;
Terminate;
end;
end;
end;
|
Когда связь установлена, потоку необходимо ожидать входящих данных(запроса от клиента). Для этого можно использовать следующий код:
if (not Terminated) and (not fSocketStream.WaitForData(1000000)) then
begin
// Обработчик таймаута (т.е. если по истечении 1000000 миллисекунд
// от клиента не пришло запроса
end;
// В сокете есть входящие данные!
|
Для чтения данных, Вам понадобится создать буфер для хранения полученных данных. Обычно буфер - это PByteArray или массив символов. В этом примере я обозвал буфер как fRequest который является массивом символов. Кроме того я ожидаю фиксированное количество байт. Массив имеет постоянный размер REQUESTSIZE.
var
ac, readlen: integer;
begin
FillChar( fRequest, REQUESTSIZE, 0 );
ac := 0;
repeat
readlen := fSocketStream.read( fRequest[ac], 1024 );
// считываем блоки по 1024 байт, до тех пор, пока буфер
// не заполнится
ac := ac+readlen;
until
(readlen = 0) or (ac = REQUESTSIZE);
end;
|
Если readlen равно 0, значит больше нет входящих данных. Функция Чтения завершается через 100000 миллисекунд после запуска в TWinSocketStream.Create(). Если Вы не знаете сколько времени нужно ожидать запроса от клиента, то чем меньше будет таймаут, тем лучше. В большинстве случаев максимальный таймаут не должен превышать 30 секунд.
При посылке ответа, Вы должны знать, в каком режиме работает клиент. Многие клиенты ожидают только один пакет ответа, другие ожидают несколько пакетов. В этом примере, я подразумеваю клиента, который ожидает только один пакет, так что я должен послать мои данные назад в одном блоке:
fSocketStream.WriteBuffer(fRep, fReplySize);
|
fRep это буфер, содержащий ответ на запрос клиента, и fReplySize - это размер буфера.
|