Клиент-серверные приложения на Delphi (TClientSocket, TServerSocket)

Cети и Интернет   3 октября 2011  Автор статьи: admin 

В этом уроке я покажу вам, как можно создавать различные сетевые приложения на Delphi  с использованием компонентов TServerSocket и TClientSocket. Данный урок поможет вам в разработке своих программ или компьютерных игр, которые смогут взаимодействовать посредством локальной или сети Интернет.

Для начала давайте найдем необходимые компоненты TServerSocket и TClientSocket. Чаще всего они не входят в стандартный пакет установки Delphi, но их можно установить дополнительно. Для этого запустим Delphi, зайдем в «Component/Install Packages», затем нажмем кнопку «Add». В открывшемся диалоговом окне нужно найти файл «dclsocketsXX.bpl» (он лежит в папке bin, которая находится в папке с Delphi), где XX — это числовой номер версии вашего Delphi. Находим файл, нажимаем «Открыть», а затем в окне «Install Packages» нажимаем «OK».

Теперь, во вкладке «Internet» появились два компонента — TServerSocket и TClientSocket, работу с которыми мы и будем рассматривать в этом уроке.

Не сложно догадаться по названиям самих компонентов, что компонент  TClientSocket выполняет роль клиента в сети, который подсоединяется к серверу с компонентом  TServerSocket.

[note]В рамках данной статьи, в качестве сервера мы будем рассматривать серверную программу, которая выполняет роль сервера, а клиентами мы будем называть клиентские программы, которые соединяются с сервером.[/note]

Сервер служит обычно для сетевого взаимодействия клиентов. Таким образом клиент может отсылать какую-либо информацию на сервер, а сервер будет эту информацию отсылать остальным клиентам.

Давайте рассмотрим работу сетевого чата. Предположим, что к серверу чата подключено несколько клиентов. Один из клиентов отправляет новое сообщение на сервер. Сервер принимает сообщение и отправляет его обратно остальным клиентам. Остальные клиенты принимают сообщение с сервера и таким образом и осуществляется передача сообщений чата.

[note]Такие сетевые приложения, в работе которых участвуют клиент и сервер, называются клиент-серверными приложениями.[/note]

Теперь попробуем разобраться, как же все таки работать с этими компонентами. Попробуем реализовать аналогичный клиент-серверный чат.

Для начала создадим сервер, который должен принимать сообщение и отсылать его всем клиентам обратно.

Создадим новое приложение в Delphi, поместим на него компонент TServerSocket и в его параметрах укажем следующие значения:

Active = false
Name = srv
Port = 22500
ServerType = stNonBlocking

Параметр Active отвечает за состояние сервера, активен он или не активен на данный момент.

В событие OnCreate формы не забудем добавить srv.active:=true, чтобы сервер запустился.

В параметре Port необходимо указать порт, по которому будут передаваться пакеты с информацией от клиента к серверу и обратно.

Также поместим на форму компонент Memo1 (дадим ему название log), который нам понадобиться для того, чтобы отслеживать работу чата.

Теперь у srv создадим событие onClientConnect, в котором мы сможем фиксировать подключения клиентов к серверу. В коде самого события напишем:

[cc lang=»delphi»]procedure TForm1.srvClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
log.Lines.Add(‘Подключился клиент с IP адресом ‘+Socket.RemoteAddress);
end;[/cc]

Таким образом мы можем узнать IP адрес подключившегося к серверу клиента.

В переменной Socket в событии хранится вся необходимая информация о клиента, который произвел подключение к серверу.

Обработчик события onClientRead служит для получения и обработки пришедшей от клиента информации. Рассмотрим пример:

[cc lang=»delphi»]procedure TForm1.srvClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
str:string;
i:integer;
begin
str:=Socket.ReceiveText;
log.Lines.Add(str);
for i:= 0 to srv.Socket.ActiveConnections -1 do
srv.Socket.Connections[i].SendText(str);
end;[/cc]

Переменной str мы присваиваем значение принятого текстового сообщения от клиента и добавляем принятое сообщение в лог. После этого мы отсылаем сообщение всем клиентам. Кол-во подключенных клиентов можно узнать в  srv.Socket.ActiveConnections.

Предположим, если мы хотим отправить сообщение конкретному клиенту, то это можно сделать так:

[cc lang=»delphi»]srv.Socket.Connections[a].SendText(‘test message’); [/cc]

В данном случае в качестве переменной a выступает числовой идентификатор в  srv.Socket.Connections. Эти идентификаторы находятся в диапазоне от 0 до  srv.Socket.ActiveConnections -1. Но как же, например, отправить сообщение клиенту с определенным IP адресом, который нам извествен заранее? Делается это так:

[cc lang=»delphi»]for i:= 0 to srv.Socket.ActiveConnections -1 do
if srv.Socket.Connections[i].RemoteAddress= ‘123.123.123.123’ then begin
srv.Socket.Connections[i].SendText(‘test message’);
break;
end;[/cc]

Мы перебираем все соединения на сервере. Если IP адрес клиента совпадает с «123.123.123.123» (может быть и любой другой), то отправляем сообщение этому клиенту и завершаем цикл.

Ну и наконец, рассмотрим еще одно событие у TServerSocket — onClientDisconnect, которое обрабатывает отключение клиента от сервера.

[cc lang=»delphi»]procedure TForm1.srvClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
log.Lines.Add(‘Клиент ‘+socket.RemoteAddress+’ отключился от сервера.’);
end; [/cc]

Теперь перейдем к клиенту. Создадим новое приложение Delphi и поместим в него компонент TClientSocket и дадим этому компоненту имя «client». Присвоим параметрам компонента client значения:

Active = false
Port = 25500
ClientType = ctNonBlocking
Address = 127.0.0.1

В параметре address укажите IP адрес компьютера, на котором запущено серверное приложение. Если провести подключение по IP адресу «127.0.0.1», то компьютер подключится грубо говоря сам к себе и будет работать с серверной программой, которая запущена там же, где и запущена клиентская. Такой способ очень удобен для тестирования сетевых приложений.

[warning]Значение Port у клиента необходимо присвоить такое же как и у сервера! [/warning]

Также добавим компонент TMemo с именем log, чтобы отображать пришедшие от сервера сообщения.

Рассмотрим параметр OnCreate формы, где производиться подключение к серверу.

[cc lang=»delphi»]procedure TForm1.FormCreate(Sender: TObject);
begin
{Пытаемся установить соединение}
ClientSocket1.Open;
end;[/cc]

Теперь разместим на форме клиента кнопку Button1 и однострочное текстовое поле Edit1. Они нам понадобятся для отправки сообщений на сервер. В событии OnClick у компонента Button1 напишите:

[cc lang=»delphi»]Client.Socket.SendText(Edit1.Text);[/cc]

Таким образом на сервер будет отправляться сообщение, введенное в Edit1.

После того как сообщение было отослано на сервер, сервер отсылает сообщение всем клиентам, поэтому нужно принять сообщение от сервера. Для этого нам понадобиться событие OnRead:

[cc lang=»delphi»]procedure ClientRead(Sender: TObject; Socket: TCustomWinSocket);
begin
{Если пришло сообщение — добавляем его в log}
Log.Lines.Add(Socket.ReceiveText);
end; [/cc]

Осталось только написать событие отключения от сервера. Для этого можем создать еще одну кнопку и в ее событии OnClick указать

[cc lang=»delphi»] client.close; [/cc]

Запустите клиент и сервер. Попробуйте отослать сообщение на сервер, сервер в ответ должен вернуть его вам. Если возникает ошибка «Asynchronous socket error 10061», то это скорее всего связано с недоступностью сервера. Проверьте соответствие портов, IP адресов и работоспособность сетевого соединения.

На этом наш урок можно считать завершенным. Смело задавайте вопросы в комментариях и подписывайтесь на рассылку новых уроков прямо на ваш e-mail!

  • Не видя ваш код, невозможно выявить причину ошибки. Эта ошибка вероятнее всего возникает из-за того, что вы динамически создаете какие либо данные или структуру и забываете эти данные вовремя удалять, чтобы не переполнялась оперативная память.

  • none

    А можно ли узнать IP сервака и отослать его клиенту?

  • none

    А можно ли узнать IP сервака и отослать его клиенту?

  • Tier_skg

    Статья очень помогла при написании НПК  (научно практической работы по информатике) в школе. Автору большое спасибо! 

  • Tier_skg

    Статья очень помогла при написании НПК  (научно практической работы по информатике) в школе. Автору большое спасибо! 

  • Дмитрий

    День добрый, при попытке отправить сообщение от сервера клиенту выскакивает сообщение List index out of bonds(1), что это может быть? клиент один, номер клиента просто ставлю в программе, от клиента к серверу сообщения проходит, все находится на одной машине, заранее спасибо…

    • Вероятно, при отправке сообщения в коде «srv.Socket.Connections[i]…» вы обращаетесь к несуществующему элементу Connections, в вашем случае так  «srv.Socket.Connections[1]…». Т.к. к вашему серверу подсоединен только 1 клиент, то его индекс в Connections будет равен нулю. Убедитесь, что та самая переменная i не превышает srv.Socket.ActiveConnections -1.

  • Дмитрий

    День добрый, при попытке отправить сообщение от сервера клиенту выскакивает сообщение List index out of bonds(1), что это может быть? клиент один, номер клиента просто ставлю в программе, от клиента к серверу сообщения проходит, все находится на одной машине, заранее спасибо…

  • Дмитрий

    День добрый, да с «0» сообщение отправилось, спасибо, ноя сделал процедурку по определению Socket.AcniveConnection и он определял как «1» по этому я и слал на «1», в чем ошибка?

  • Romka0484

    Спасибо за содержательную статью. Хотелось один момент прояснить: можно ли  в свойство Port прописать номер COM-порта . К примеру есть некое устройство, соединенное посредством COM. Данное устройство запускает подключение. от программы требуется просто совершить «раздачу данных». Tclientsocket  и Tserversocket решили бы проблему на раз два… 

  • Romka0484

    Спасибо за содержательную статью. Хотелось один момент прояснить: можно ли  в свойство Port прописать номер COM-порта . К примеру есть некое устройство, соединенное посредством COM. Данное устройство запускает подключение. от программы требуется просто совершить «раздачу данных». Tclientsocket  и Tserversocket решили бы проблему на раз два… 

  • Илья

    Подскажите, а обязательно ли должен быть открыт используемый порт?

    • Конечно. Порт сначала необходимо открыть, прежде чем мы начнем его использовать. Перед открытием порта убедитесь, что он уже не занят.

  • Илья

    Подскажите, а обязательно ли должен быть открыт используемый порт?

    • Конечно. Порт сначала необходимо открыть, прежде чем мы начнем его использовать. Перед открытием порта убедитесь, что он уже не занят.

  • БелыйВолк

    Скольким значным может быть порт ? И уточните чей ип адрес прописывать

    • Диапазон портов от 0 до 65535. Посмотреть уже занятые порты можно здесь: http://ru.wikipedia.org/wiki/Список_портов_TCP_и_UDP. Использовать IP нужно в той сети, с которой вы работаете, т.е. если вы работаете в локальной сети, то нужно использовать IP адрес удаленной машины именно в локальной сети. Если вы работаете с сетью Интернет, то и использовать нужно IP адрес в сети Интернет.

  • БелыйВолк

    Скольким значным может быть порт ? И уточните чей ип адрес прописывать

    • Диапазон портов от 0 до 65535. Посмотреть уже занятые порты можно здесь: http://ru.wikipedia.org/wiki/Список_портов_TCP_и_UDP. Использовать IP нужно в той сети, с которой вы работаете, т.е. если вы работаете в локальной сети, то нужно использовать IP адрес удаленной машины именно в локальной сети. Если вы работаете с сетью Интернет, то и использовать нужно IP адрес в сети Интернет.

  • AndHacker

    Спасибо автору за хорошие коментарии!

  • Даниил

    Как узнать на каком адресе создается сервер? И как серверу указать например свой глобальный адрес, а не адрес например в wi-fi сети?

  • sng

    интересная тема доступна написана мне как начинающему хороший толчек для разработки собственного софта автору большое спасибо

  • SAF

    передача файлов компонентами TClientSocket, TServerSocket основана по принципу протокола TCP?
    Cпасибо*

    • Да, именно.

      • SAF

        а FTP протокол фигурирует в этих компонентах? или передача по FTP делается по другому принципу?

        • Да, фигурирует, но реализовывать FTP на таком низком уровне не рекомендуется. Существуют готовые компоненты Indy для работы с FTP.

  • Алина

    Подскажите, работаю с сокет клиентом, при физическом разъединении нужно чтобы сразу выдавало собщение, где это прописать. Обработчик ошибок onError не помог — ошибка просто не возникает когда выдергиваешь кабель из гнезда…. Наверно есть какой-то параметр, который распознает имеется ли физическое соединение? заранее спасибо!

    • Алина

      Забыла пояснить, что интересует момент когда отпадает именно сервак, а на ближнем конце сеть в норме.

  • Татьяна

    здравствуйте, не получается запустить клиент, программа выводит [dcc32 Error] Unit1.pas(38): E2003 Undeclared identifier: ‘ClientSocket1’. Подскажите, где может быть ошибка?

  • Александр

    Подскажите пожалуйста а как быть с динамическими ip адресами, разве приведенный пример будет работать с ними?

  • Илья

    Здравствуйте, помогите пожалуйста.Ошибка c «procedure TForm1.FormCreate(Sender: TObject);
    begin
    ClientSocket1.Open;
    end;

    [Error] Unit1.pas(35): Undeclared identifier: ‘ClientSocket1’

    Что делать? Простите за много проблелов

    • Нужно создать компонент ClientSocket1. Раз такая ошибка возникла — значит он просто не существует.

  • Эльвира

    в коде ругается на .srvClientConnect, .srvClientRead и .srvClientDisconnect при TForm1. Делала все по инструкции, ни на что другое жалоб нет, но данные строки не распознает почему то, с чем это можнт быть связано?

Научиться программировать

  • на Delphi

  • на Java

  • на C++