На днях, в свой проект MapWindows GIS в Delphi я добавил сетевую поддержку. То есть создал отдельный сервер и отдельный клиент. Смысл заключается в том, что работает сервер приложения, пользователь запускается клиент и в пользователь вводит запрос: Москва Тверская 6. Затем сервер обрабатывает запрос, получает результаты поиска из Яндекс.Карт и отправляет полученную картинку клиенту, затем уже в клиенте в компоненте TMap отображается часть данной карты, которая соответствует запросу пользователя. В итоге пользователь может ее масштабировать, сохранять и так далее.
Поэтому в данной статье я хочу рассказать, как я реализовывал клиента и сервер. Это я делал с помощью TClientSocket и TServerSocket, в данной статье мы и рассмотрим подробно те методы, которые использовал я у себя, в своем проекте.
Для начала давайте посмотрим, как эти компоненты можно установить себе в IDE. Если Вы используете IDE Delphi 7, то в ней по умолчанию данные компоненты присутствуют, но они, к сожалению, не установлены, но это не проблема. Нам достаточно открыть Delphi и установить.
Для этого выполним команду Component-Install Packages… и в появившемся окне необходимо нажать на кнопку Add. После этого необходимо указать путь к файлу dclsockets70.bpl, который обычно, по умолчанию, находится в папке BIN. После этого необходимо нажать на кнопку Ок. Все, компоненты у Вас должны появиться на вкладке Internet (TClientSocket и TServerSocket).
В проекте MapWindow GIS, я начинал всю работу, с минимальной разработке сервера. Для начала установил компонент TServerSocket на форму. И по нажатию на кнопку Запустить сервер задал первоначальные настройки, для его инициализации:
Server.Port:=FormServerSetting.SpinEditPort.Value;//указываем порт сервера
Server.Active:=True;//активируем его
Server.Open;
if Server.Active then
begin
//выводим сообщение что сервер запущен и работает
end;
……..
//выводим ошибку, если сервер не запустился
Для инициализации сервера на своей машине, я задавал лишь только свободный порт (который не занят другими приложениями) и активировал его.
В принципе и все, для работы мне достаточно было того, чтобы сервер был запущен и я смог обрабатывать запросы клиентов, которые они посылают.
Для того, чтобы мне получить список клиентов, которые подключаются к серверу и дальнейшей работы с ними, я установил компонент TCheckListBox на форму и на событие OnclientConnect компонента TServerSocket, написал следующий код:
procedure TFormServer.ServerClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
//отслеживаем подключение клиента
RichEditLog.SelAttributes.Color:=clGreen;
RichEditLog.SelAttributes.Style:=[fsBold];
CheckListClient.Items.Add(Socket.RemoteHost);
RichEditLog.Lines.Add('['+TimeToStr(Time)+'] Client connected: '+Socket.RemoteHost);//добавляем в список клиента, который подключился
RichEditLog.Perform(WM_VSCROLL,SB_BOTTOM,0);
end;
То есть, я в список добавляю имена тех клиентов, которые подключаются к серверу, для дальнейшего получения информации о них.
Например, можно получить подробную информацию о клиенте:
procedure TFormInfoClient.FormShow(Sender: TObject);
begin
//выводим ифнормацию о клиенте
Caption:='Информация о клиенте: '+FormServer.CheckListClient.Items[FormServer.CheckListClient.ItemIndex];
LocalName.Caption:=FormServer.Server.Socket.Connections[FormServer.CheckListClient.ItemIndex].LocalHost;
LocalHost.Caption:=FormServer.Server.Socket.Connections[FormServer.CheckListClient.ItemIndex].LocalAddress;
LocalPort.Caption:=IntToStr(FormServer.Server.Socket.Connections[FormServer.CheckListClient.ItemIndex].LocalPort);
RemoteName.Caption:=FormServer.Server.Socket.Connections[FormServer.CheckListClient.ItemIndex].RemoteHost;
RemoteHost.Caption:=FormServer.Server.Socket.Connections[FormServer.CheckListClient.ItemIndex].RemoteAddress;
RemotePort.Caption:=IntToStr(FormServer.Server.Socket.Connections[FormServer.CheckListClient.ItemIndex].RemotePort);
end;
Можно получить следующие данные:
- Локальное имя
- Локальный адрес
- Локальный порт
- Удаленное имя
- Удаленный адрес
- Удаленный порт
Информацию я получаю о том клиента, с помощью данного кода, которого я выделил в списке компонента TCheckListBox.
Ничего сложного, как видите, нет, для того, чтобы отправить сообщение клиенту, можно воспользоваться следующим кодом:
FormServer.Server.Socket.Connections[FormServer.CheckListClient.ItemIndex].SendText('#message#'+RichEditMessage.Text);
В квадратных скобках, я указываю, какому клиенту мы будем отправлять сообщение (оно равно выделенному клиенту в компоненте TCheckListBox), в сообщение я указываю #message# — что означает, что это обычное сообщение от сервера, которое следует просто вывести в окне.
Для того чтобы получить сообщение от клиента, серверу, нам понадобится событие OnClientRead компонента TServerSocket и текстовая переменная, в которую мы будем записывать запрос, который посылает клиент.
procedure TFormServer.ServerClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
query:String;
begin
//получаем запрос от клиента на карту
try
query:=Socket.ReceiveText;
if pos('query',query)<>0 then
begin
//запрашиваем у Яндекса или Google координаты карты, соответсвующие запросу клиента
end;
//если просто сообщение от клиента, то выводим его
if pos('#message#',query)<>0 then
begin
end;
……
Из данного кода можно увидеть, что клиент может посылать как обычное сообщение серверу, так и запрос на получение карты, вида: Москва, Тверская, 6.
Для этого мне надо определить, где обычное сообщение, а где именно запрос, на получение карты, чтобы сервер в дальнейшем смог его обработать. В этом случае я к сообщениям клиента, в самом начале, добавляю такие идентификаторы:
- #message#
- #query#
Если вначале сообщения клиента присутствует идентификатор #message#, то сервер распознает, как обычное сообщение от клиента. Если вначале сообщения присутствует идентификатор #query#, то это означает, что клиент послал запрос на получение карты.
Вот и все, дальше сервер обрабатывает запрос и отправляет карту клиенту, которая отображается в компоненте TMap.
Также клиент, в любое время может отключить от сервера, нам это также необходимо отследить, чтобы удалить его из общего списка клиентов, подключенных к серверу. Для этого выделяем компонент TServerSocket и на событие OnClientDisconnect пишем следующий код:
procedure TFormServer.ServerClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
var
i:integer;
begin
try
//отслеживаем отключение клиента
RichEditLog.SelAttributes.Color:=clRed;
RichEditLog.SelAttributes.Style:=[fsBold];
for i := 0 to Server.Socket.ActiveConnections-1 do
begin
if Server.Socket.Connections[i].Handle=Server.Socket.Connections[i].Handle then
begin
RichEditLog.Lines.Add('['+TimeToStr(Time)+'] Client disconnected: '+Socket.RemoteHost);
CheckListClient.Items.Delete(i);
RichEditLog.Perform(WM_VSCROLL,SB_BOTTOM,0);
end;
end;
finally
//-//-//-//-//-//
end;
end;
Мы проходим по всем клиентам, которые у нас есть в списке и если какого-то ненаходим, то удаляем его из компонента TCheckListBox, это означает, что клиент в своем приложении нажал на кнопку Отключиться.
Кстати, на сервере также можно отключать клиентов, для этих целей можно использовать следующий код:
procedure TFormServer.N1Click(Sender: TObject);
begin
//отключаем клиента вручную
if CheckListClient.ItemIndex<0 then
begin
Application.MessageBox('Выберите клиента','Ошибка',MB_OK+MB_ICONERROR);
exit;
end;
Server.Socket.Connections[CheckListClient.ItemIndex].Destroy;
CheckListClient.Items.Delete(CheckListClient.ItemIndex);
end;
Тут я думаю, должно быть все понятно, отключаем клиента, который выделен в списке, в компоненте TCheckListBox.
Что касается сервера, то вроде бы все, я все рассказал, что я использовал в своем приложении MapWindow GIS и получении карт с сервиса Яндекс.Карты. Теперь давайте перейдем к рассмотрению клиента.
Для начала на форму клиента, необходимо установить компонент TClientSocket, после этого произвести настройки, к какому серверу он будет подключаться.
Это делается следующим образом:
procedure TForm1.Button1Click(Sender: TObject);
begin
if ClientSocket1.Active<>True then
begin
ClientSocket1.Port:=SpinEdit1.Value;
ClientSocket1.Host:=Edit1.Text;
ClientSocket1.Active:=True;
end;
end;
Указываем ему следующие данные:
- Порт
- Хост
Далее активируем его, если сервер и клиент находятся на одном компьютере (то в хост можно указывать localhost или же 127.0.0.1).
После того, как нажмете на кнопку подключить, имя данного клиента должно отобразиться в окне нашего сервера (в компоненте TCheckListBox, помните?).
Если нас сервер отключает, то можно вывести соответсвующее сообщение, для этого необходимо на событие OnDisconnect компонента TClientSocket написать следующий код:
procedure TForm1.ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
if flag=False then
begin
ShowMessage('Вас отключили с сервера');
flag:=true;
end;
end;
Чтобы клиент сам мог отключиться, то можно воспользоваться следующим кодом:
procedure TForm1.Button2Click(Sender: TObject);
begin
ClientSocket1.Active:=False;
end;
Для того чтобы отправить запрос серверу, я использую следующий код:
procedure TForm1.Button3Click(Sender: TObject);
begin
ClientSocket1.Socket.SendText('#query#="Москва"');
end;
В данном случае сервер будет распознавать данное сообщение, как запрос и естественно в дальнейшем будет его обрабатывать (посылать запрос на Яндекс.Карты и полученную карту отправлять клиенту).
Если я от клиента – серверу, отправляю обычное сообщение, то я указываю:
procedure TForm1.Button3Click(Sender: TObject);
begin
ClientSocket1.Socket.SendText('#message#="Москва"');
end;
В данном случае сервер будет распознавать данное сообщение, как обычное и на сервис Яндекс.Карты не будет отправлять запрос.
Вот примерно и все, хочется сказать еще одно, что на событие OnRead компонента TClienSocket я получаю нашу карту, которую в дальнейшем отображаю в окне клиента, в компоненте TMap. Клиент может ее увеличивать, сохранять, рисовать на ней.
За проектом MapWindow GIS, можно следить на странице.
Данный метод можно применять не только для карт, а в любой момент, где Вы не хотите, чтобы клиенты получали доступ к Интернету, но имели возможность для получения какой-то информации из Интернета.