скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

Использование компонента 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 - это размер буфера.






Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook