Текст книги "Программирование в Delphi. Трюки и эффекты"
![](/books_files/covers/thumbs_240/programmirovanie-v-delphi-tryuki-i-effekty-31074.jpg)
Автор книги: Александр Чиртик
Жанр: Программирование, Компьютеры
сообщить о неприемлемом содержимом
Текущая страница: 21 (всего у книги 24 страниц) [доступный отрывок для чтения: 8 страниц]
Серверное приложение реализовано с использованием оконного интерфейса. Форма frmServer приложения во время работы показана на рис. 11.6.
![](i_084.png)
Рис. 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.
![](i_085.png)
Рис. 11.7. Форма клиента при ведении разговора
Приведенная на рис. 11.7 форма имеет имя frmClient. Свойства основных элементов управления (существенные для работы приложения), помещенных на форму, приведены в табл. 11.2.
Таблица 11.2. Свойства элементов управления формы frmClient
![](i_086.png)
Ниже приведены функции и процедуры, не являющиеся обработчиками событий, но имеющие большое значение для работы клиентского приложения.
Приведенная в листинге 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. Для рассмотрения работы со всеми имеющимися компонентами понадобилось бы написать целую книгу. Тем не менее, хочется надеется, что приведенные в главе примеры помогут вам в освоении механизмов программного взаимодействия между частями компьютерной сети.
Правообладателям!
Данное произведение размещено по согласованию с ООО "ЛитРес" (20% исходного текста). Если размещение книги нарушает чьи-либо права, то сообщите об этом.Читателям!
Оплатили, но не знаете что делать дальше?