Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Прикладное программирование на CC++ с нуля до мультимедийных и сетевых приложений
.pdf
Введение в компьютерные коммуникации 91
инсталлирован протокол IP, то машина имеет предопределенный адрес
127.0.0.1, который можно использовать, например, если вы создаете на своем
компьютере одновременно и локальный и удаленный хосты. Для отладки разрабатываемых коммуникационных программ имеет смысл все тестировать «на
себе» и только потом переходить к работе в сети.
Напомним также, что при подключении компьютера к сети ему присваивается реальный или внутрисетевой IP-адрес. При модемном подключении к
провайдеру присваиваемый адрес будет меняться от сеанса к сеансу. Для того
чтобы узнать свой текущий адрес в операционной системе Windows 98, можно
запустить программу winipcfg, а в более поздних версиях ОС достаточно открыть свойства значка модема на панели задач. Можно также воспользоваться
утилитой ipconfig.
Идентификация адресов хостов еще не достаточна для организации связи.
Дело в том, что на машине может работать одновременно несколько коммуникационных процессов. Вы можете в одно и то же время скачивать из Интернета необходимый файл и работать, например, в Интернет-пейджере ICQ.
Для того чтобы такая многозадачность могла работать и процессы не мешали
бы друг другу, каждому приложению, занимающемуся приемом или передачей данных, сопоставляется так называемый порт. Не следует путать такой
виртуальный порт с физическими последовательными и параллельными портами компьютера (COM и LPT), хотя и те и другие предназначены для обмена
данными. Порт присваивается процессу в виде номера, представленного целым числом в достаточно широких пределах. Вообще говоря, номер порта может быть назначен процессу произвольно, но необходимо позаботиться о том,
чтобы для двух или более запущенных на компьютере процессов порты не
оказались одинаковыми. Следует, также, помнить, что существуют умолчания, в соответствии с которыми назначаются порты некоторых стандартных
процессов. Так, например, порт 3128 назначается по умолчанию прокси-серверам, и занимать это значение другими процессами не рекомендуется. Здесь
уместно сделать одно крайне важное замечание. На практике некоторые провайдеры ограничивают произвол в использовании номеров портов обычными
пользователями — закрывают те или иные номера или диапазоны номеров.
Делается это с целью обеспечения безопасности сетей, защиты от вирусов. По
этой причине не исключена ситуация, когда разрабатываемое вами приложение отказывается работать в сети. Возможно, необходимо обратиться к провайдеру и узнать о возможности использования конкретного номера порта.
Теперь понятно, что всякий информационный процесс (процесс обмена
данными между клиентом и сервером или двумя узлами) однозначно задается
парой IP-адресов хостов и парой портов, назначенных приложениям на хостах. Совокупность адресов и портов процесса принято называть сокетом. Вообще говоря, понятие сокета в информатике более фундаментально, но точная формулировка автору неизвестна. Заметим только, что часто и не совсем
верно под сокетом понимается постоянное соединение двух хостов, в отличие
от соединения, создаваемого для «одноразовой» передачи данной порции информации.

92 Глава 3. Передача информации в сетях
Конкретные средства для передачи данных определяются средой разработки приложений. В используемой нами интегрированной оболочке Borland
C++ Builder такие средства существуют и весьма разнообразны. На вкладке
FastNet палитры визуальных компонентов собраны инструменты, позволяю-
щие создавать коммуникационные приложения для широкого круга задач.
Компоненты NMMsg è NMMSGServ являются клиентским и серверными
VCL, обеспечивающими передачу и прием текстовых сообщений между парой
узлов. Обмен информацией производится по протоколу транспортного уровня
TCP/IP. Для передачи данных произвольного типа (также по протоколу
TCP/IP) целесообразно использовать так называемые потоки (Stream). В поток может быть помещена порция информации любого вида, и эта порция
«переносится потоком» с одного компьютера на другой. Клиентская и серверная части потоков представлены компонентами NMStrm è NMStrmServ соответственно. Компонент NMUDP предназначен для передачи пользовательских
дейтаграмм по протоколу UDP. Здесь разделение на серверную и клиентскую
часть отсутствует. VCL наделена функциями как приема, так и передачи дейтаграмм.
На этой вкладке сосредоточены и многие другие коммуникационные визуальные компоненты. С их помощью можно создавать программы для работы с электронной почтой, FTP-системами, web-страницами и многое другое.
Непосредственно к тематике наших изысканий эти инструменты отношения
не имеют, однако для тех, кто заинтересуется указанными возможностями,
мы можем порекомендовать отличную книгу (Козлова А. В. Программирование для Интернет в C++ Builder 5. — М.: БИНОМ, 2001).
С помощью визуальных компонентов Builder'а можно организовывать и
сокетные соединения. Компоненты ClientSocket è ServerSocket, используемые
для этих целей, находятся в палитре компонентов на вкладке Internet. К сожалению, в версиях Builder'а до шестой включительно не реализованы средства
для работы с протоколом реального времени RTP. Тем не менее, уже включенных в пакет компонентов нам хватит для знакомства с программированием коммуникаций, к которому мы и переходим.
Простейшие коммуникационные программы
Начнем с программы, точнее — с пары программ, которые выполняют
очень простую задачу: Клиент передает по сети хосту сообщение, формируемое пользователем, а сервер принимает сообщение и представляет его для
прочтения. Данные передаются по протоколу ТСР по указанному IP-адресу
сервера и на указанный порт. Сообщение передается в виде текстовой строки.
Как указывалось в предыдущем разделе, для достижения указанной цели достаточно воспользоваться парой визуальных компонентов NMMsg è
NMMSGServ. Представляемые далее программы мы разрабатываем «по минимуму», то есть используя только самые необходимые средства.
Организация интерфейсов клиентской и серверной программы показана
на рис. 3.1.

Простейшие коммуникационные программы 93
Ðèñ. 3.1
В клиентском приложении в поле редактирования IP-address задается адрес сервера. Напомним, что для работы на одном компьютере с имитацией
сети можно указать адрес 127.0.0.1. В поле редактирования MESSAGE вписывается текст передаваемого сообщения. Кнопка RUN выполняет передачу, а
кнопка CLOSE завершает работу программы.
Если к моменту передачи запущен сервер, то на нем в поле RECEIVE
MESSAGE FROM отобразится имя, которое назначил себе клиент. В строке
MESSAGE показано само сообщение. Здесь кнопка CLOSE также завершает
работу.
Начнем проектирование программы. В новом проекте для клиента необходимо, в соответствии с рисунком, разместить на форме две метки (Label),
два поля редактирования (Edit), две кнопки (Button) и компонент NMMsg со
вкладки FastNet палитры стандартных компонентов. Уже на этапе проектирования свойство Text объекта Edit1 имеет смысл установить в значение
127.0.0.1 — адрес по умолчанию. Теперь посмотрим на вкладку Properties
(Свойства) компонента NMMSg инспектора объектов (Object Inspector), показанную на рис. 3.2.
Ðèñ. 3.2

94 Глава 3. Передача информации в сетях
Свойство FromName задает имя клиента, отправляющего сообщение. Мы,
не мудрствуя лукаво, просто назвали себя CLIENT, что и отображено в серверной части предыдущего рисунка. В свойстве Host можно было бы задать
предопределенным значение для IP-адреса сервера. Мы оставили это свойство пустым, поскольку задаем его в первом поле редактирования. В свойстве
Port задано значение 6711. Выбор порта, как отмечалось, достаточно произволен, однако есть рекомендация во избежание путаницы использовать для
NMMsg именно этот номер. На остальных свойствах останавливаться не будем — мы же работаем «по минимуму». Значит, примем то, что предлагается
по умолчанию.
Теперь приведем весь небольшой листинг программы.
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//—————————————————————————————————————#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//—————————————————————————————————————__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
NMMsg1->Host=Edit1->Text;
}
//—————————————————————————————————————void __fastcall TForm1::Button1Click(TObject *Sender)
{
NMMsg1->PostIt(NMMsg1->FromName);
NMMsg1->PostIt(Edit2->Text);
}
//—————————————————————————————————————void __fastcall TForm1::Button2Click(TObject *Sender)
{
Close();
}
Инициализация формы сопровождается присвоением свойству
NMMsg1->Host значения, содержащегося в первом поле редактирования. Обработчик нажатия первой кнопки выполняет две операции посылки данных — два раза вызывает метод NMMsg1->PostIt(). Аргументом этой функции будет текстовая строка, посылаемая на сервер. Как можно видеть, первый
раз это будет свойство NMMsg1->FromName, то есть значение выбранного
нами для себя имени клиента. Второй раз метод PostIt отправляет строку, находящуюся во втором поле редактирования — свойство Edit2->Text. Вот, собственно, и все, что содержится в минимальной программе клиента!
При проектировании серверного приложения на форме нужно разместить
две метки, два поля редактирования, закрывающую кнопку и компонент
NMMSGServ все с той же вкладки FastNet палитры компонентов. Прежде

Простейшие коммуникационные программы 95
всего, нужно в инспекторе объектов проконтролировать то, что порт сервера
задается значением 6711. Листинг серверного приложения еще меньше, чем
программа клиента.
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//—————————————————————————————————————#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//—————————————————————————————————————__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//—————————————————————————————————————void __fastcall TForm1::NMMSGServ1MSG(TComponent *Sender,
const AnsiString sFrom, const AnsiString sMsg)
{
Edit1->Text=sFrom;
Edit2->Text=sMsg;
}
//—————————————————————————————————————void __fastcall TForm1::Button1Click(TObject *Sender)
{
Close();
}
Нам необходимо организовать обработчик события, заключающегося в
том, что сервер обнаруживает поступление сообщения от клиента. Это стандартное событие (Event) для компонента NMMSGServ. Для написания обработчика нужно при проектировании на форме выделить компонент. В инспекторе объектов для него перейти на вкладку Events и дважды щелкнуть
мышью на событии OnMSG. Откроется текст программы с пока пустым телом обработчика. Как можно видеть из листинга, функция-обработчик будет
называться NMMSGServ1MSG, а в теле обработчика стоят два выражения
присваивания, заносящие в поля редактирования аргументы sFrom è sMsg
этой функции. Проницательный читатель уже догадался, что первый аргумент
это имя клиента, а второй — само сообщение.
Теперь нам остается стартовать на компьютере обе программы, написать в
клиенте свое сообщение, отослать его серверу кнопкой RUN и, если все сделано правильно, практически мгновенно принять сообщение на сервере.
В локальной сети или, тем более, в Интернете задержка при передаче сообщения может быть заметной, а иногда и весьма значительной, но это уже, как
говорится, дело техники, а не программиста.
Перейдем к паре программ, демонстрирующих передачу информации с
помощью так называемых потоков. Объектами, транспортирующими данные,
здесь будут визуальные компоненты NMStream è NMStreamServ для клиент-

96 Глава 3. Передача информации в сетях
ской и серверной программы соответственно. В отличие от предыдущих приложений, ориентированных на пересылку текстовых строк, потоковый метод
позволяет передавать данные произвольного типа. Мы здесь все же по-прежнему перешлем строку (ориентированными на поток средствами), но на сервере запишем эту строку в файл. Кроме того, здесь мы воспользуемся инструментами контроля за процессом соединения компьютеров и самой транспортировкой.
Интерфейсы клиентского и серверного приложения представлены на
рис. 3.3.
Ðèñ. 3.3
По нажатию первой кнопки клиентской программы происходит пробное
соединение с сервером с сразу следующим отсоединением. В информационном поле в правой части окна отобразится результат такого теста. На рисунке
продемонстрировано успешное соединение — текст Connection OK и отсоединение — текст Disconnected. Â ïîëå MESSAGE вводится передаваемая строка.
Нажатие кнопки SENT выполняет отсылку данных. В информационном поле
в случае успеха появляется сообщение Message sent. Кнопка STOP, как обычно, закрывает программу.
В серверной программе каждое подключение клиента сопровождается информацией CLIENT connect. Можно видеть, что в нашем примере присутствует два таких сообщения на сервере. Первое связано с тестовой проверкой, а
второе — реакция на пересылку сообщения. Кроме того, получение потока с
данными сопровождается информацией RECEIVE STREAM.
При проектировании программы-клиента на форму необходимо поместить три кнопки, метку, поле редактирования. Для организации информационного поля следует разместить также ранее нами не используемый компонент Memo. Со вкладки FastNet берем инструмент NMStream. Для него в инспекторе объектов зададим IP-адрес хоста 127.0.0.1 и оставим предлагаемый
по умолчанию порт. Для объекта Memo установим в ИСТИНА (true) свойст
-

Простейшие коммуникационные программы 97
во вертикальной полосы прокрутки. Вы легко сделаете это в инспекторе объектов.
Листинг клиентского приложения представлен далее.
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//—————————————————————————————————————#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//—————————————————————————————————————__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Memo1->Clear();
}
//—————————————————————————————————————void __fastcall TForm1::Button1Click(TObject *Sender)
{
NMStrm1->Connect();
NMStrm1->Disconnect();
}
//—————————————————————————————————————void __fastcall TForm1::Button2Click(TObject *Sender)
{
Close();
}
//—————————————————————————————————————void __fastcall TForm1::NMStrm1ConnectionFailed(TObject *Sender)
{
Memo1->Lines->Add("Can't connect");
}
//—————————————————————————————————————void __fastcall TForm1::NMStrm1Connect(TObject *Sender)
{
Memo1->Lines->Add("Connection OK!");
}
//—————————————————————————————————————void __fastcall TForm1::NMStrm1Disconnect(TObject *Sender)
{
Memo1->Lines->Add("Disconnected");
}
//—————————————————————————————————————void __fastcall TForm1::NMStrm1MessageSent(TObject *Sender)
{
Memo1->Lines->Add("Message sent");
}
//—————————————————————————————————————void __fastcall TForm1::Button3Click(TObject *Sender)
{
TMemoryStream *MS;

98 Глава 3. Передача информации в сетях
MS = new TMemoryStream();
StreamLn(MS,Edit1->Text);
NMStrm1->PostIt(MS);
MS->Free();
}
При инициализации формы выполняется очистка информационного поля
Memo. Нажатие первой кнопки Button1 производит тестовое подключение к
серверу и отключение от сервера с помощью методов Connect è Disconnect
объекта NMStrm. С кнопкой Button2 все ясно — это закрытие приложения.
Далее следует ряд функций обработки некоторых событий для компонента NMStrm. Суть обрабатываемых событий понятна из названий функций.
В результате возникновения тех или иных ситуаций в информационное поле
помещаются соответствующие строки. Напомним, что для написания обработчика необходимо в инспекторе объектов на вкладке Events компонента
дважды щелкнуть мышью справа от названия события и в пустое тело функции вставить соответствующий код.
Главная акция программы — передача информации на сервер выполняется обработчиком нажатия кнопки Button3. Поскольку мы организуем потоковый обмен данными, необходимо создать поток нужного типа. В данном случае мы объявляем класс потока в оперативной памяти TMemoryStream и создаем объект MS этого класса. Отметим, что в Builder'е можно оперировать с
потоками других типов, например, со строковым потоком TStringStream. Нетрудно понять, что выражение StreamLn(MS,Edit1->Text); помещает в поток
MS строку, содержащуюся в данный момент в поле редактирования. Метод
PostIt выполняет отправку потока на сервер. Последнее выражение в теле
функции освобождает поток.
Текст серверной программы представлен в листинге.
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//—————————————————————————————————————#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//—————————————————————————————————————__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Memo1->Clear();
}
//—————————————————————————————————————void __fastcall TForm1::NMStrmServ1ClientContact(TObject *Sender)
{
Memo1->Lines->Add("CLIENT contact");
}
//—————————————————————————————————————void __fastcall TForm1::NMStrmServ1MSG(TComponent *Sender,
const AnsiString sFrom, TStream *strm)

Простейшие коммуникационные программы 99
{
TMemoryStream *SS;
SS = new TMemoryStream();
SS->CopyFrom(strm,strm->Size);
Memo1->Lines->Add("RECEIVE STREAM");
SS->SaveToFile("111.txt");
SS->Free();
}
//—————————————————————————————————————-
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Close();
}
В качестве контроля за состоянием сервера здесь представлен обработчик
события, заключающегося в контакте клиента с сервером. Информация о возникновении события помещается строкой в поле Memo. Обработчик
NMStrmServ1MSG реагирует на получение сервером сообщения от клиента.
Так же, как и в клиентской программе, здесь организуется поток в памяти SS.
Далее с помощью метода CopyFrom потока в него из полученного сообщения
извлекаются данные. Первым аргументом strm будет аргумент самого обработчика — собственно сам поток. Второй аргумент — свойство strm->Size представляет собой размер блока данных, переданный потоком от клиента, выраженный в количестве байтов. Следующее выражение информирует пользователя о получении потока в информационном поле. Как и планировалось, мы
не воспроизводим принятую строку, а помещаем ее в файл. Для этой цели
служит метод SaveToFile потока в памяти. Последнее действие обработчика —
очистка потока. После того как строка успешно принимается сервером, в рабочем каталоге программы создается текстовый файл 111.txt, который и будет
содержать строку. Подчеркнем, что следующий акт передачи данных уничтожит старый файл и создаст новый с тем же именем.
Обе предыдущие пары программ используют для передачи данных протокол ТСР. Теперь познакомимся с инструментом транспортировки данных (называемых в данном случае дейтаграммами) по протоколу UDP. Поскольку
один и тот же компонент NMUDP может выступать и в качестве клиента, и в
качестве сервера, мы разработаем единое приложение, пересылающее информацию от себя к себе, тем более что все написанные нами программы пока
ориентированы для работы на одном компьютере.
Сформулируем задачу следующим образом. Формируется массив случайных целых чисел. Подсчитывается и выводится в информационное поле сумма этих чисел. Массив рассматривается в качестве буфера в оперативной памяти. Этот буфер по протоколу UDP отправляется на «удаленный» (на самом
деле — локальный) хост. Там он читается как буфер, снова превращается в
массив. Подсчитывается сумма элементов массива и также выводится в информационное поле. Естественно, если программа работает правильно, ис
ходная и полученная суммы должны совпадать. Таким образом, рабочее окно
-

100 Глава 3. Передача информации в сетях
приложения после выполнения пересылки данных должно выглядеть так, как
это показано на рис. 3.4.
Ðèñ. 3.4
Итак, на форме нового проекта необходимо разместить две кнопки, информационное поле Memo и компонент NMUDP со вкладки FastNet. Сразу
установим необходимые свойства этого компонента в инспекторе объектов
(рис. 3.5).
Ðèñ. 3.5
IP-адрес удаленного хоста мы, как всегда, устанавливаем в 127.0.0.1. Поскольку мы общаемся сами с собой, удаленный и локальный порты должны
совпадать, а выбранное значение 6767 рекомендовано для служб UDP, но отнюдь не строго фиксировано. Остальные свойства нас пока не интересуют и
будут оставлены по умолчанию. Теперь переходим к листингу программы.
#include <stdlib.h>
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//—————————————————————————————————————-
#pragma package(smart_init)
#pragma resource "*.dfm"
#define MAX 2048
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
