Электронная библиотека » Александр Чиртик » » онлайн чтение - страница 21


  • Текст добавлен: 10 ноября 2013, 00:08


Автор книги: Александр Чиртик


Жанр: Программирование, Компьютеры


сообщить о неприемлемом содержимом

Текущая страница: 21 (всего у книги 24 страниц) [доступный отрывок для чтения: 8 страниц]

Шрифт:
- 100% +
Реализация сервера

Серверное приложение реализовано с использованием оконного интерфейса. Форма frmServer приложения во время работы показана на рис. 11.6.

Рис. 11.6. Форма сервера сообщений


Элемент управления ListBox (имя lstEvents), который представлен на форме, предназначен для вывода списка событий (присоединение, отсоединение клиентов, передача сообщений). Список помещается в рамку GroupBox1. Для списка и рамки задано значение client свойства align.

Кроме перечисленных элементов управления, на форму также помещены компоненты IdTCPServer (имя TCPServer) и Timer (имя Timer1). Для таймера задано значение True свойства Enabled и 50 свойства Intervel. Компонент TCPServer настроен на прослушивание порта 12345, а свойству Active присвоено значение True.

При реализации сервера основной программный код помещен в файле формы (Unit1.pas). Этот модуль условно можно разделить на две части: в первой реализованы специальные функции и процедуры (регистрации пользователей, пересылки текстовых сообщений между пользователями и т. д.), во второй части следуют процедуры-обработчики событий (методы класса TfrmServer).

Сначала рассмотрим процедуры обработки событий, так как они значительно проще, чем остальные функции и процедуры, и их рассмотрение вначале позволит лучше представить порядок функционирования приложения (листинг 11.9).

Листинг 11.9. Процедуры обработки событий серверного приложения (Unitl.pas)

procedure TfrmServer.Timer1Timer(Sender: TObject);

begin

//Если нужно, то скроем окно сервера

if (not SERVERVISIBLE) then

begin

frmServer.Visible:= False;

ShowWindow(Application.Handle,SW_HIDE);

end;

//Таймер больше не нужен

Timer1.Enabled:= False;

end;

procedure TfrmServer.TCPServerExecute(AThread: TIdPeerThread);

begin

//Обработаем сообщение, пришедшее от клиента

ProcessMessage(AThread.Connection, AThread.Connection.ReadLn);

end;

procedure TfrmServer.TCPServerConnect(AThread: TIdPeerThread);

begin

//Попытаемся добавить нового пользователя

if (AddClient(AThread.Connection)) then

//Пользователь должен прислать свое имя

ProcessMessage(AThread.Connection, AThread.Connection.ReadLn)

else

begin

//Нет места для нового пользователя

AThread.Connection.WriteLn('error:Достигнуто максимальное количество '+

'пользователей. Извините, невозможно принять вас в разговор.');

AThread.Connection.Socket.Close;

end;

end;

procedure TfrmServer.TCPServerDisconnect(AThread: TIdPeerThread);

var clDisconnected: client; //Структура с информацией об

//отсоединенном клиенте (заполнены только поля strName и strIP)

begin

//Удалим информацию об отсоединенном клиенте

clDisconnected:= DeleteClient(AThread.Connection);

if (clDisconnected.strName <> '')then

begin

//Сообщим о событии остальным клиентам

 SendAll('deluser:'+ clDisconnected.strName);

SendAll('Нас покинул "'+ clDisconn ected.strName + '".');

//Добавим событие в журнал

if (REPORT) then nAddEvent ('Отсоединился клиент "'+

clDisconn ected .strName + '" на компьютере "'+

clDisconnected.strIP + '"');

end;

end;

procedure TfrmServer.FormCreate(Sender: TObject);

begin

//Создаем критическую секцию

section:= TCriticalSection.Create;

end;


Первая и последняя из приведенных в листинге 11.9 процедур не имеют непосредственного отношения к работе TCP-сервера. Процедура TfrmServer. Timer1Timer вызывается только один раз при первом срабатывании таймера Timer1. В ней, исходя из заданного значения глобальной переменной SERVERVISIBLE, происходит (или не происходит) скрытие окна сервера. Значение глобальной переменной SERVERVISIBLE (а также переменной REPORT) определяется в момент запуска сервера. Процедура TfrmServer. FormCreate создает объект синхронизации, используемый остальными функциями и процедурами для предотвращения одновременного доступа к общим данным нескольких потоков (сервер же многопоточный).

Остальные три процедуры используются непосредственно для организации взаимодействия сервера с клиентами. Как было сказано ранее, сервер хранит информацию о присоединенных к нему клиентах. Хранилищем этой информации является массив структур (подробно он будет рассмотрен позже). Здесь же нужно сказать, что при присоединении к серверу нового клиента (процедура TfrmServer. TCPServerConnect) предпринимается попытка найти для информации о новом пользователе место в указанном массиве (вызов функции AddClient). Если место нашлось, то функция AddClient возвращает значение True, и сервер переходит в режим регистрации пользователя. Для регистрации клиентская программа должна передать серверу имя пользователя (сообщение с префиксом name:).

Особенностью реакции сервера на отключение клиентской программы (процедура TfrmServer. TCPServerDisconnect) является то, что, помимо удаления информации об отсоединившемся клиенте (вызов функции DeleteClient), все остальные пользователи уведомляются об отсоединении собеседника (вызовы функции SendAll).

При получении сообщения от клиента (процедура TfrmServer.Execute) происходит лишь передача полученной строки функции ProcessMessage, которая и выполняет анализ текста сообщения и определяет действия, которые сервер должен выполнять.

Теперь рассмотрим функции и процедуры, которые прямо или косвенно используются описанными выше обработчиками событий и на которых по большей части и основывается работа серверного приложения. Часть файла Unitl.pas, содержащая объявление типов данных, переменных и подключения модулей (добавленные вручную), которые нужны для работы сервера, приведена в листинге 11.10.

Листинг 11.10. Типы данных и переменные серверного приложения (Unitl.pas)

unit Unit1;

interface

uses

 .. ., SyncObjs;

type

TfrmServer = class(TForm)

lstEvent s: TListBox; //Список событий

...

end;

var

frmServer: TfrmServer;

REPORT: Boolean; //Если = True, то все события записываются

//в ListBox окна сервера

SERVERVISIBLE: Boolean; //Если = True, то окно показывается на экране

//и приложение есть на Панели задач

implementation

//Следующая структура используется для хранения информации о

//пользователе, подключившемся к серверу

type

client = record

fUsed: Boolean; {Ячейка занята}

fNamed: Boolean; {Клиент сообщил свое имя}

strName: string; {Имя пользователя}

strIP: string; {IP-адрес клиента}

Connection: TIdTCPServerConnection; {Соединение клиента с сервером}

end;

const

MAX_CLIENT = 100;//Максимальное количество клиентов

var

clients: array [1..MAX_CLIENT] of client;//Массив со сведениями

//о клиентах

section: TCriticalSection; //Критическая секция для синхронизации

//потоков


Код процедуры, записывающей событие в журнал (ListBox на форме сервера), приведен в листинге 11.11.

Листинг 11.11. Добавление события в журнал сервера

procedure AddEvent(strEvent: string);

begin

section.Enter;

frmServer.lstEvents.Items.Append(strEvent);

section.Leave;

end;


В листинге 11.12 приведен код процедуры, рассылающей текстовое сообщение всем присоединенным к серверу клиентам.

Листинг 11.12. Рассылка сообщения всем клиентам

procedure SendAll(strMessage: string);

var

i: Integer;

begin

for i:=1 to MAX_CLIENT do

if (clients[i].fNamed)then

begin

try

clients[i].Connection.WriteLn(strMessage);

except

//При возникновении ошибки отключим клиента и продолжим рассылку

ErrorCloseConnection(clients[i].Connection);

end;

end;

end;


В листинге 11.13 приведен код процедуры, отсылающей текстовое сообщение strMessage клиенту с заданным именем strName.

Листинг 11.13. Отправление сообщения клиенту с заданным именем

procedure SendTo(strMessage: string; strName: string);

var

i: Integer;

begin

for i:=1 to MAX_CLIENT do

if (clients[i].fNamed)then

if (clients[i].strName = strName) then

//Нашли клиента с заданным именем

try

clients[i].Connection.WriteLn(strMessage);

except

//При возникновении ошибки отключим клиента и продолжим рассылку

ErrorCloseConnection(clients[i].Connection);

end;

end;


Процедура, приведенная в листинге 11.14, находит и помечает как занятую для нового пользователя запись в массиве clients. Если свободных записей в массиве не осталось, значит, достигнуто максимальное количество пользователей.

Листинг 11.14. Добавление информации о новом клиенте

function AddClient(Connection: TIdTCPServerConnection): Boolean;

var

i: Integer;

begin

section.Enter;

for i:=1 to MAX_CLIENT do

begin

if (not clients[i].fUsed) then

begin

//Нашли свободную запись – заполним ее (клиент пока безымянный)

clients[i].fUsed:= True;

clients[i].Connection:= Connection;

clients[i].strIP:= Connection.Socket.Binding.PeerIP;

AddClient:= True;

section.Leave;

Exit;

end;

end;

section.Leave;

AddClient:= False;

end;


Процедура DeleteClient, приведенная в листинге 11.15, освобождает запись заданного пользователя в массиве clients.

Листинг 11.15. Удаление информации о клиенте

function DeleteClient(Connection: TIdTCPServerConnection):client;

var

i: Integer;

begin

section.Enter;

for i:=1 to MAX_CLIENT do

if (clients[i].fUsed) then

if (clients[i].Connection = Connection) then

begin

//Вот она – запись о нужном клиенте

clients[i].fUsed:= False;

clients[i].fNamed:= False;

clients[i].Connection:= Nil;

DeleteClient:= clients[i];

clients[i].strName:= '';

clients[i].strIP:= '';

section.Leave;

Exit;

end;

end;


Процедура SendClientList, приведенная в листинге 11.16, отправляет клиентской программе заданного пользователя (только что зарегистрировавшегося) сообщения addclient: с именем каждого зарегистрированного ранее пользователя.

Листинг 11.16. Отправление списка всех присоединенных клиентов

procedure SendClientList(Connection: TIdTCPServerConnection);

var

i: Integer;

begin

for i:= 1 to MAX_CLIENT do

if (clients[i].fNamed) then

if (clients[i].Connection <> Connection) then

try

//Сообщим имя очередного найденного пользователя

 Connection.WriteLn('adduser:'+ clients[i].strName);

except

//При возникновении ошибки отключим клиента и продолжим

//рассылку

ErrorCloseConnection(clients[i].Connection);

end;

end;


Процедура ErrorCloseConnection (листинг 11.17) вызывается при ошибке отправки сообщений пользователям (например, при нарушении сетевого соединения). Эта процедура отключает пользователя, соединение с которым работает с ошибками, и сообщает об этом другим пользователям.

Листинг 11.17. Закрытие соединения с клиентом (при возникновении ошибки)

procedure ErrorCloseConnection(Connection: TIdTCPServerConnection);

var

clError: client; //Информация о пользователе, соединение

//с которым прервалось (только имя и IP-адрес)

begin

//Отключим соединение, работающее с ошибками

clError:= DeleteClient(Connection);

//Сообщим об отключении остальным пользователям

SendAll('deluser:'+ clError.strName);

SendAll(' Нас покинул "'+ clError.strName + '".');

//Добавим событи е в журнал

if (REPORT) then AddEvent('Из-за ошибки отсоединен клиент "'+

  clError.strName + '" на компьютере "'+ clError.strIP + '"');

end;


Процедура RegisterClient, код которой приведен в листинге 11.18, регистрирует пользователя под указанным в сообщении name: именем (ранее уже выполнялась функция AddClient, которая нашла для записи этого пользователя место в массиве clients). Если имя, под которым хочет зарегистрироваться пользователь, используется, то клиентской программе посылается соответствующее уведомление, после чего соединение разрывается.

Листинг 11.18. Регистрация нового клиента

procedure RegisterClient(Connection: TIdTCPServerConnection;

strName: string);

var

i: Integer;

begin

//Проверим, что имя клиента еще не использовалось

for i:=1 to MAX_CLIENT do

begin

if (clients[i].fNamed) then

if (clients[i].strName = strName) then

begin

//Дублирование имени – придется разрывать соединение

Connection.WriteLn('error:Пользователь с именем "'+ strName +

'" уже участвует в разговоре.');

DeleteClient(Connection);

Connection.Socket.Close;

Exit;

end;

end;

//Поиск записи о нужном клиенте и присвоение ему имени

for i:=1 to MAX_CLIENT do

begin

if (not clients[i].fNamed and clients[i].fUsed) then

if (clients[i].Connection = Connection) then

begin

//Вот он, наш клиент ...

clients[i].fNamed:= True;

clients[i].strName:= strName;

//Сообщим другим о появлении нового участника

 SendAll('adduser:'+ strName);

SendAll('text: К нам присоединился "'+ strName +

'". Поприветствуем!');

//Отсылаем новому клиенту список остальных участников разговора

SendClientList(Connection);

//Разрешим новому клиенту отсылать сообщения

Connection.WriteLn('ok:');

//Если нужно, то добавим событие в список

if (REPORT) then AddEvent('Присоединен клиент "'+ strName +

'" на компьютере "'+ Connection.Socket.Binding.PeerIP + '"');

end;

end;

end;


В листинге 11.19 приведена служебная функция, возвращающая имя пользователя по ссылке на объект TIdTCPServerConnection, соответствующий этому клиенту.

Листинг 11.19. Определение имени клиента по его соединению с сервером

function GetClientName(Connection: TIdTCPServerConnection):string;

var

i: Integer;

begin

for i:=1 to MAX_CLIENT do

if (clients[i].fNamed) then

if (clients[i].Connection.Socket.Binding.Handle =

Connection.Socket.Binding.Handle) then

begin

GetClientName:= clients[i].strName;

Exit;

end;

end;


Наконец, в листинге 11.20 приведена главная процедура серверного приложения, обрабатывающая сообщения, полученные от клиентов.

Листинг 11.20. Обработка сообщения от клиента

procedure ProcessMessage(Connection: TIdTCPServerConnection;

strMessage: string);

var

strName: string; //Имя отправителя сообщения

strAction: string; //Строка с обозначением действия (префикс)

len: Integer; //Длина строки strAction

begin

//Определим действие, которое хочет выполнить клиент

len:= Pos(':', strMessage);

strAction:= Copy(strMessage,1,len-1);

Delete(strMessage,1,len);

if (strAction= 'name') then

begin

//Клиент сообщает свое имя – пытаемся его зарегистрировать

RegisterClient(Connection, strMessage);

end

else if (strAction = 'text') then

begin

//Клиент передает сообщение всем – подпишем сообщение и отошлем

strMessage:= GetClientName(Connection) + ': '+ strMessage;

SendAll('text:'+ strMessage);

//Если надо, то сохраняем сообщение в списке событий

if (REPORT) then AddEvent('Сообщение от '+ strMessage);

end

else

begin

//Клиент передает сообщение определенному собеседнику

//(строка strAction содержит имя собеседника)

strName:= GetClientName(Connection);

SendTo('text:'+ strName + ': '+ strMessage, strAction);

if (strName <> strAction) then

//Передадим копию сообщения отправителю

Connection.WriteLn('text:'+ strName + 'для '+ strAction + ': '

+ strMessage);

//Если надо, то сохраним сообщение в списке событий

if (REPORT) then AddEvent('Сообщение для '+ strAction +

'от '+ strName + ': '+ strMessage);

end;

end;


Информация о каждом пользователе (участнике разговора) хранится в отдельной структуре client:


type

client = record

fUsed: Boolean; {Ячейка занята}

fNamed: Boolean; {Клиент сообщил свое имя}

strName: string; {Имя пользователя}

strIP: string; {IP-адрес клиента}

Connection: TIdTCPServerConnection; {Соединение клиента с сервером}

end;


Непосредственно к пользователю имеют отношение только три последних поля структуры. Самым полезным из них является ссылка на объект TIdTCPSe rve rConnection, с помощью которой сервер может в любое время отправить данные определенному пользователю.

Информация обо всех пользователях хранится в массиве clients. Его размер ограничен (константой MAXCLIENT) и определяет максимальное количество пользователей-участников разговора. Так как используется массив с постоянным количеством элементов, то для индикации того, что ячейка массива занята (значение True) или свободна (значение False) можно использовать специальный флаг (поле fUsed). Поле fName структуры client используется для фиксации факта отправления клиентской программой имени пользователя (клиентские программы незарегистрированных пользователей сообщения не получают). Изначально значение поля fNamed равно False и, только если имя пользователя сообщено серверу и не используется одним из участников разговора, устанавливается в значение True.

Одним из самых сложных моментов работы рассматриваемого сервера является обеспечение синхронизации доступа к массиву clients. Для этого используется критическая секция. Она же используется и для синхронизации добавления событий в список lstEvents сервера.

Наконец, чтобы сервер можно было запускать с отключенным протоколированием событий, а также чтобы окно сервера не мешало пользователю, можно хранить значения переменных REPORT и SERVERVISIBLE в INI-файле, что, собственно, и сделано: значения этих переменных хранятся в секции [Common] файла Server.ini. Для реализации считывания при запуске сервера соответствующих значений из INI-файла код в модуле Server (файл Server.dpr) изменен следующим образом (листинг 11.21).

Листинг 11.21. Изменения в модуле Server

program Server;

uses

Forms,

Unit1 in 'Unit1.pas' {frmServer},

IniFiles, Dialogs;

{$R *.res}

var

{Переменные из из INI-файла}

config: TIniFile;

strPath: string;

begin

//Грузим информацию из INI-файла

strPath:=

Copy(Application.ExeName,1,Length(Application.ExeName) – 3) + 'ini';

config:= TIniFile.Create(strPath);

SERVERVISIBLE:= config.ReadBool('Common', 'ServerVisible', False);

REPORT:= config.ReadBool('Common','EventReport', False);

config.Free;

try

//Запуск ксервера

Application.Initialize;

Application.CreateForm(TfrmServer, frmServer);

Application.Run;

except

MessageDlg('Не удается запустить сервер сообщений. '+

'Возможно, он был запущен ранее.', mtError, [mbOK], 0);

end;

end.


В приведенном листинге код создания формы помещен в блок try. Это сделано только для того, чтобы сервер не «падал», отображая всем прекрасно знакомое окно с сообщением о критической ошибке, возникающей при попытке ошибочного запуска собственной копии.

Соответственно, INI-файл для запуска сервера с видимым окном и включенным протоколированием имеет следующий вид:


[Common]

ServerVisible=1

EventReport=1

Реализация клиентского приложения

Проект клиентской программы имеет имя Client. Внешний вид формы клиентского приложения во время его работы представлен на рис. 11.7.

Рис. 11.7. Форма клиента при ведении разговора


Приведенная на рис. 11.7 форма имеет имя frmClient. Свойства основных элементов управления (существенные для работы приложения), помещенных на форму, приведены в табл. 11.2.

Таблица 11.2. Свойства элементов управления формы frmClient

Ниже приведены функции и процедуры, не являющиеся обработчиками событий, но имеющие большое значение для работы клиентского приложения.

Приведенная в листинге 11.22 процедура обновляет форму при удачном подключении к серверу.

Листинг 11.22. Обновление формы при присоединении к серверу

proceduree Connect();

begin

with frmClienttdo

begin

cmbConn ect.Caption := 'Отключиться ';

txtUser.Enabled:= False;

txtServer.Enabled:= False;

Caption:= ' Разговорник ['+ txtUser.Text + 'подключен к'

+ txtServer.Text + ']';

lstUsers.Enabled:= True;

cmbSend.Enabled:= True;

txtMessage.Enabled:= True;

txtChat.Enabled:= True;

end;

end;


Процедура Disconnect, приведенная в листинге 11.23, обновляет форму при отключении от сервера (в таком виде форма frmClient предстает первоначально).

Листинг 11.23. Обновление формы при отсоединении от сервера

procedure Disconnect();

begin

with frmClient do

begin

cmbConn ect.Caption:= ' Подключиться ';

txtUser.Enabled:= True;

txtServer.Enabled:= True;

Caption:= ' Разго ворник';

lstUsers.Enabled:= False;

lstUsers.Clear;

cmbSend.Enabled:= False;

txtMessage.Enabled:= False;

txtChat.Enabled:= False;

end;

end;


Процедура ProcessMessage (листинг 11.24) обрабатывает сообщение, полученное от сервера, аналогично той же процедуре в серверном приложении (естественно, сообщения и реакция на них отличны от серверных).

Листинг 11.24. Обработка строки, полученной от сервера

procedure ProcessMessage(strMessage: string);

var

strAction: string; //Тип сообщения (префикс сообщения)

len: Integer; //Длина строки strAction

begin

//Определим тип сообщения и выполним соответствующие действия

len:= Pos(':', strMessage);

strAction:= Copy(strMessage,1,len-1);

Delete(strMessage,1,len);

if (strAction = 'ok') then

begin

//Регистрация пользователя завершена – можно отправлять сообщения

Connect;

end

else if (strAction = 'error') then

begin

//Ошибка!!!

frmClient.TCPClient.Disconnect;

Disconnect;

MessageDlg(strMessage, mtError, [mbOK], 0);

end

else if (strAction = 'adduser') then

begin

//К разговору присоединился новый пользователь

frmClient.lstUsers.Items.Add(strMessage);

end

else if (strAction = 'deluser') then

begin

//Какой-то пользователь отсоединился

frmClient.lstUsers.Items.Delete(

frmClient.lstUsers.Items.IndexOf(strMessage));

end

else begin

//Покажем принятое сообщение

frmClient.txtChat.Lines.Add(strMessage);

end;

end;


Далее приведены обработчики событий, на которых, собственно, и основана работа клиентской программы. Обработчик нажатия кнопки cmbConnect, приведенный в листинге 11.25, пытается соединиться с сервером. Если клиент присоединен к серверу, то эта же кнопка используется для его отсоединения.

Листинг 11.25. Соединение с сервером и отсоединение от него

procedure TfrmClient.cmbConnectClick(Sender: TObject);

begin

if (cmbConn ect.Caption = ' Подключиться') then

begin

//Проверим, чтобы были введены имена сервера и пользователя

if (txtServer.Text = '')then

begin

MessageDlg('Введите имя сервера в текстовое поле.',

mtInformation, [mbOK], 0);

Exit;

end

else if (txtUser.Text = '')then

begin

MessageDlg('Введите имя пользователя в текстовое поле.',

mtInformation, [mbOK], 0);

Exit;

end;

//Пытаемся подключиться к серверу

try

TCPClient.Host:= txtServer.Text;

TCPClient.Connect;

except

MessageDlg(' Не удается соединиться с сервером ',mtError, [mbOK], 0);

end;

end

else

//Отключаемся от сервера

TCPClient.Disconnect;

end;


Обработчик нажатия кнопки cmbSend (листинг 11.26) отправляет сообщение, которое могут прочесть все пользователи, присоединенные к серверу.

Листинг 11.26. Отправка сообщения всем собеседникам

procedureTfrmClient.cmbSendClick(Sender: TObject);

begin

if (txtMessage.Text <> '') then

begin

//Отправка сообщения всем собеседникам

TCPClient.WriteLn('text:'+ txtMessage.Text);

txtMessage.Text:= '';

txtMessage.SetFocus;

end;

end;


При двойном щелчке кнопкой мыши на имени в списке пользователей отправляется сообщение, которое получает только выделенный в списке пользователь (листинг 11.27).

Листинг 11.27. Отправка сообщения заданному собеседнику

procedure  TfrmClient.lstUsersDblClick(Sender: TObject);

begin

if ((lstUsers.ItemIndex >= 0) and (txtMessage.Text <> '')) then

begin

//Отправим сообщение только для выбранного собеседника

//(сообщение вида "имя_собеседника:текст_сообщения ")

TCPClient.WriteLn(lstUsers.Items.Strings[lstUsers.ItemIndex] +

':'+ txtMessage.Text);

txtMessage.SetFocus;

end;

end;


После соединения с сервером, то есть в обработчике TfrmClient. TCPClientConnected, приведенном в листинге 11.28, клиентская программа отправляет имя пользователя серверу. При отсоединении от сервера (тот же листинг 11.28) происходит соответствующее оформление внешнего вида формы frmClient.

Листинг 11.28. Обработка соединения с сервером и отсоединения от него

procedure TfrmClient.TCPClientConnected(Sender: TObject);

begin

//Отправляем на сервер имя пользователя

TCPClient.WriteLn('name:'+ txtUser.Text);

end;

procedure TfrmClient.TCPClientDisconnected(Sender: TObject);

begin

//Оформим форму для отсоединенного от сервера состояния

Disconnect;

end;


Ключевой обработчик приведен в листинге 11.29 (именно по таймеру проверяется факт получения сообщения от сервера). Для элемента управления TCPClient значение таймаута установлено для того, чтобы при отсутствии принятых данных клиентская программа не переходила надолго в состояние ожидания, а генерировалось исключение, по которому можно было бы судить, что данных нет (см. блок try в этом обработчике).

Листинг 11.29. Проверка наличия данных от сервера

procedure TfrmClient.Timer1Timer(Sender: TObject);

var strMessage: string;

begin

//Проверим, нет ли для нас сообщения

if (TCPClient.Connected)then

begin

try

strMessage:= TCPClient.ReadLn;

if (strMessage <> '')then

ProcessMessage(strMessage);

except

on EIdReadTimeout do; //Ошибки тайм-аута игнорируем

else

//При остальных ошибках от соединяемся от сервера

TCPClient.Disconnect;

end;

end;

end;

end.


Примечание

Чтобы при запуске клиентского приложения из среды Delphi постоянно не появлялись сообщения об исключениях (возникают при истечении таймаута в TfrmClient. Timer1Timer), снимите флажок Stop on Delphi Exceptions на вкладке Language Exceptions окна Debugger Options (меню Tools ► Debugger Options).

На этом рассмотрение сетевого взаимодействия средствами Delphi в рамках этой книги завершено. Конечно, в главе перечислены далеко не все типы соединений и служб, поддерживаемых хотя бы компонентами, поставляемыми вместе с Delphi. Для рассмотрения работы со всеми имеющимися компонентами понадобилось бы написать целую книгу. Тем не менее, хочется надеется, что приведенные в главе примеры помогут вам в освоении механизмов программного взаимодействия между частями компьютерной сети.


Страницы книги >> Предыдущая | 1 2 3 4 5 6 7 8
  • 0 Оценок: 0

Правообладателям!

Данное произведение размещено по согласованию с ООО "ЛитРес" (20% исходного текста). Если размещение книги нарушает чьи-либо права, то сообщите об этом.

Читателям!

Оплатили, но не знаете что делать дальше?


Популярные книги за неделю


Рекомендации