Практическая работа 8. Изучение принципов взаимодействия приклад. Практическая работа Изучение принципов взаимодействия прикладных программ с помощью транспортного протокола стека tcpIP
Скачать 89.71 Kb.
|
Практическая работа 8. Изучение принципов взаимодействия прикладных программ с помощью транспортного протокола стека TCP/IP Цель работы: изучение принципов организации обмена сообщениями между сетевыми приложениями по протоколу TCP и приобретение практических навыков создания клиент-серверных приложений на основе компонентов TClientSocket и TServerSocket. Теоретические сведения Транспортный протокол TCP TCP (Transmission Control Protocol) – это один из самых широко распространенных протоколов транспортного уровня. Главная функция TCP заключается в доставке сообщений без потерь, чего не может гарантировать протокол более низкого уровня IP (Internet Protocol). Для доставки сообщений предварительно устанавливается соединение между процессом-отправителем и процессом-получателем. Данное соединение осуществляет надежную доставку пакетов. Протокол TCP производит повторную передачу искаженного или утерянного пакета. Выделение всех необходимых для надежной доставки сообщений функций в отдельный уровень освобождает разработчиков прикладных программ и утилит от решения задач управления потоком дейтаграмм. Протокол обеспечивает сквозную передачу данных от отправителя к получателю. Поскольку TCP ориентирован на установление соединения, то адресат, получивший дейтаграмму, должен уведомить отправителя об этом. Подразумевается, что между отправителем и получателем устанавливается виртуальный канал, где они обмениваются сообщениями, часть из которых является подтверждениями о получении данных либо кодами ошибок. Виртуальный канал на самом деле может подразумевать несколько реальных физических каналов передачи данных, поскольку сообщение может проходить через один или несколько шлюзов. Когда некоторое приложение (процесс) прикладного уровня отправляет сообщение другому приложению с помощью TCP, предполагается, что сообщение является потоком, т.е. представляет собой поток байтов, передаваемых асинхронно. TCP получает поток байтов и собирает его в сегменты, добавляя заголовки в начало сегментов. Длина сегмента обычно определяется протоколом или выбирается администратором системы. Процесс обмена данными начинается с передачи запроса на установление соединения от машины-отправителя к машине-получателю. В запросе содержится специальное целое число, называемое номером сокета (socket). В ответ получатель посылает номер своего сокета. Номера сокетов отправителя и получателя однозначно определяют соединение (конечно, соединение также не возможно без указания IP-адресов отправителя и получателя, но эта задача решается протоколами более низкого уровня – IP). После установления соединения TCP начинает передавать сегменты сообщения. На более низком IP-уровне отправителя сегменты разбиваются на одну или несколько дейтаграмм. Пройдя через сеть, дейтаграммы поступают к получателю, где IP-уровень снова собирает из них сегменты и передает их TCP. TCP собирает все сегменты в сообщение. От TCP сообщение поступает к процессу получателю, где обрабатывается протоколом прикладного уровня. TCP на машине-получателе собирает целое сообщение из сегментов, руководствуясь порядковыми номерами сегментов, которые записаны в их заголовке. Если какой-то сегмент сообщения потерян или поврежден (что проверяется по контрольной сумме в заголовке сегмента), то отправителю посылается сообщение, содержащее номер ошибочного сегмента. В этом случае отправитель повторно передает сегмент. Если сегмент успешно принят, то получатель посылает отправителю подтверждение-квитанцию. В TCP применяется средство ограничения потока данных, называемое скользящим окном. Оно представляет собой фрагмент сообщения, который адресат готов принять. При установлении соединения отправителю сообщается размер окна (размер окна кратен размеру сегмента). После того, как отправитель передал количество байтов, соответствующее размеру окна, он должен ждать квитанции. Как только будет получена квитанция на переданные сегменты, окно сдвигается вправо на соответствующее число байтов, и новые сегменты могут быть переданы. Отправитель может передать без получения квитанций в сеть максимально столько сегментов, сколько их укладывается в скользящем окне. В процессе обмена данными получатель может присылать квитанции, в которых будет указан новый размер скользящего окна. Важную роль в протоколе TCP играют таймеры. Сегмент считается потерянным, если квитанция на него не поступила в течение заданного времени ожидания. При этом производится повторная передача сегмента. При получении квитанции таймер останавливается. Если получатель обнаруживает несколько правильных копий одного и того же сегмента, то все лишние копии просто отбрасываются и отправителю передается только одна квитанция. Транспортный протокол UDP Протокол UDP (User Datagram Protocol) является более простым транспортным протоколом, чем протокол TCP. Он предоставляет прикладным процессам услуги транспортного уровня, которые мало чем отличаются от услуг более низкого уровня, предоставляемых протоколом IP. Протокол UDP обеспечивает доставку дейтаграмм, но не требует подтверждения их получения. Поэтому он не требует установления соединения между передающим и принимающим процессами. Протокол UDP используется в тех случаях, когда требуется передать данные без установления соединения. Такая связь в принципе не надежна, так как отправителю не сообщается, правильно ли принято его сообщение и получено ли оно вообще. Для проверки возникновения ошибок может использоваться контрольная сумма пакета, но ошибки никак не обрабатываются – они либо игнорируются, либо их обработка выполняется уже на более высоком, прикладном уровне. Данные, отправляемые прикладным процессом через UDP, достигают места назначения как единое целое, не дробясь на части. Например, если процесс отправитель передал пять сообщений через порт, то и процесс-получатель должен считать из порта пять сообщений. Размер каждого записанного сообщения должен совпадать с размером каждого прочитанного. Протокол UDP используется тогда, когда требуется простейший механизм передачи данных. Тогда контроль ошибок либо не выполняется (например, в прикладном протоколе TFTP – Trivial File Transfer Protocol – простейший протокол передачи файлов), либо выполняется на прикладном уровне (например, в управляющем протоколе SNMP – Simple Network Management Protocol или в файловой системе NFS – Network File System). Порты, мультиплексирование и демультиплексирование Пакеты, поступающие на транспортный уровень, организуются операционной системой в виде множества очередей к точкам входа различных прикладных процессов. В терминологии TCP/IP такие системные очереди называются портами. Порт однозначно определяет приложение в пределах компьютера. Порты приложений не стоит путать с портами (интерфейсами) оборудования. Существуют два способа присвоения порта приложению – централизованный и локальный. За каждым из этих способов закреплен свой диапазон номеров портов: для централизованного - от 0 до 1023, для локального – от 1023до 65 535. Если процессы представляют собой популярные общедоступные службы, такие как FTP, telnet, HTTP, TFTP, DNS и т. п., то за ними закрепляются стандартные присвоенные (assigned) номера, иногда также называемые хорошо известными (well-known) номерами. Централизованное присвоение службам номеров портов выполняется организацией Internet Assigned Numbers Authority (IANA). Эти номера закрепляются и публикуются в стандартах Internet (RFC 1700). Примеры хорошо известных портов приведены в таблице 1. Для тех служб, которые еще не стали столь распространенными, чтобы закреплять за ними стандартные номера, номера портов выделяются локально. На каждом компьютере операционная система ведет список занятых и свободных номеров портов. При поступлении запроса от приложения, выполняемого на данном компьютере, операционная система выделяет ему первый свободный номер. Такие номера называют динамическими (dynamic). В дальнейшем все сетевые приложения должны адресоваться к данному приложению с указанием назначенного ему номера порта. После того как приложение завершит работу, выделенный ему локальный номер порта возвращается в список свободных и может быть назначен другому приложению. Таблица 1 – Примеры номеров портов наиболее популярных служб сети Internet Все сказанное выше о портах в равной степени относится к обоим протоколам транспортного уровня. Нет никакой зависимости между назначением номеров для приложений, использующих протокол TCP, и приложений, работающих с протоколом UDP. Приложения, которые передают данные на уровень IP, используя протокол UDP, получают номера, называемые портами UDP. Аналогично приложениям, обращающимся к протоколу TCP, выделяются порты TCP. В том и другом случаях это могут быть как назначенные, так и динамические номера. Диапазоны чисел, из которых выделяются номера портов TCP и UDP, совпадают. Протоколы TCP и UDP ведут для каждого номера порта две очереди: очередь пакетов, поступающих в данный порт из сети, и очередь пакетов, отправляемых данным портом в сеть. Процедура приема данных протоколом TCP (или UDP), поступающих от нескольких прикладных служб, называется мультиплексированием. Обратная процедура распределения протоколом TCP (или UDP) поступающих от сетевого уровня пакетов между набором высокоуровневых служб, идентифицированных номерами портов, называется демультиплексированием. Логические соединения Для надежной передачи данных между двумя прикладными процессами предусматривается установление логического соединения. Номер порта в совокупности с номером сети и номером конечного узла однозначно определяют прикладной процесс в сети. Этот набор идентифицирующих параметров (IP-адрес, номер порта) имеет название сокет (socket). Каждый взаимодействующий процесс идентифицируется сокетом – парой (IP-адрес интерфейса, номер порта), а каждое соединение – парой сокетов взаимодействующих процессов. Каждый процесс одновременно может участвовать в нескольких соединениях. Так, например, если (IP1, n1), (IP2, n2), (IP3, nЗ) – сокеты трех разных процессов, то возможно образование следующих соединений: соединение 1 – {(IP1, n1), (IP2, n2)}; соединение 2 – {(IP1, n1), (IP3, n3)}; соединение 3 – {(IP2, n2), (IP3, n3)}. Каждая такая пара однозначно идентифицирует соединение. Сутыо же понятия «соединение» является договоренность о параметрах, характеризующих процедуру обмена данными между двумя процессами. В протоколе TCP каждая сторона соединения посылает противоположной стороне следующие параметры: максимальный размер сегмента, который она готова принимать; максимальный объем данных (возможно несколько сегментов), которые она разрешает другой стороне передавать в свою сторону, даже если та еще не получила подтверждения на предыдущую порцию данных; начальный порядковый номер байта, с которого она начинает отсчет потока данных в рамках данного соединения. После того как в результате переговорного процесса модулей TCP с двух сторон соединения параметры процедуры обмена определены, одни из них остаются постоянными в течение всего сеанса связи, а некоторые адаптивно изменяются. Когда устанавливается несколько соединений, то может случиться, что несколько машин пошлют запросы на соединение, в которых указаны одинаковые порты источники и получатели. Однако путаницы с соединениями не возникает, потому что IP-адреса у всех машин разные, следовательно, каждое соединение будет однозначно определено своим сокетом. Программирование обмена данными на основе транспортных протоколов TCP должен взаимодействовать не только с протоколами нижележащего уровня, но и с протоколами и приложениями прикладного уровня. Связь с прикладным уровнем осуществляется с помощью набора сервисных примитивов. Сервисные примитивы определены в стандарте протокола, а для прикладных программ они доступны в форме библиотек работы с сокетами. При установлении соединения каждая из сторон выполняет некоторые операции, называемые открытием соединения. Открытие может быть пассивным или активным. Как правило, одна из сторон производит активное открытие соединения, а другая – пассивное, тогда соединение устанавливается. Оба режима подчиняются четким правилам. Пассивное соединение еще иногда называют серверным, а активное – клиентским. При активном соединении процесс прикладного уровня передает программному обеспечению TCP на той же ЭВМ сервисный примитив запроса на установление соединения с номером сокета, после чего TCP отправляет получателю запрос на установление соединения, затем ждет ответа. После установления соединения активный процесс (клиент) может инициировать прием или передачу данных. При пассивном соединении прикладная программа переводит программное обеспечение TCP в режим ожидания запроса на соединение от удаленной системы. Когда поступает запрос, программное обеспечение TCP осуществляет установку соединения, после чего пассивный процесс (сервер) готов принимать и передавать данные. Программный интерфейс сокетов изначально был разработан для операционной системы (ОС) UNIX. Библиотека функций, поддерживающих этот интерфейс, входит в ядро всех ОС типа UNIX и Linux. Однако принципы работы с этим программным интерфейсом применимы к большинству ОС, поддерживающих TCP/IP (например, в семействе ОС и оболочек типа Windows программный интерфейс сокетов реализован в динамической библиотеке Winsock.dll). Для протокола TCP пассивное (на стороне сервера) соединение с сокетом приводит к выполнению следующих функций: создание сокета и установление его типа (в ОС типа UNIX функция socket); настройка сокета на конкретное соединение (указывает адрес и номер порта – в ОС типа UNIX – функция bind); создание очередей клиентов (в ОС типа UNIX – функция listen); ожидание приходящего запроса на соединение с сокетом (в ОС типа UNIX – функция accept); прием и передача данных от клиента (в ОС типа UNIX – функции read, write, send, recv и их модификации); закрытие соединения с клиентом (в ОС типа UNIX – функция close). Получив входящий запрос на соединение, сервер должен решать как бы две задачи одновременно: обслуживать уже установленное с клиентом соединение в соответствии с прикладным протоколом (принимать и отдавать данные клиенту) и ожидать поступления новых запросов на соединение от других клиентов. Обычно в развитых ОС (к ним относятся все современные ОС) эта проблема решается за счет возможностей параллельного выполнения нескольких процессов. Сервер может породить новый процесс (или новую цепочку выполнения – thread), который и должен будет заняться обслуживанием уже установленного соединения, а основной процесс сервера может закрыть текущее соединение и вновь вернуться к ожиданию запросов на соединение от других клиентов. В ОС типа UNIX создание нового процесса решается с помощью функции fork, при этом за вновь созданным процессом сохраняются все соединения, сделанные в основном процессе. Для протокола TCP активное (на стороне клиента) соединение с сокетом приводит к выполнению следующих функций: создание сокета и установление его типа (в ОС типа UNIX функция socket); установление соединения с сервером (указывает адрес и номер порта – в ОС типа UNIX – функция connect); прием и передача данных (в ОС типа UNIX – функции read, write, send, recv и их модификации); закрытие соединения с сервером (в ОС типа UNIX – функция close). Клиент, как правило, не требует для своей работы параллельного выполнения нескольких процессов. В среде программирования Borland Delphi существуют специальные классы, которые позволяют выполнять те же действия, что и библиотека сокетов в ОС UNIX. Они взаимодействуют с библиотекой Winsock.dll на основе специальных технологий ОС (ActiveX технологии и COM-объекты). В среде Borland Delphi версии 3.0 для целей клиентского и серверного соединений служит класс объектов TTCP; а в среде Borland Delphi версии 5.0 и выше для клиентского соединения существует класс объектов TClientSocket, а для серверного – TServerSocket. Естественно, пользователь может на основе базовых классов разрабатывать свои собственные классы, которые будут поддерживать соединения по определенным им самим прикладным протоколам. Для того чтобы создать сокет, достаточно создать экземпляр объекта выбранного класса (TTCP – в среде Borland Delphi версии 3.0 на страничке компонент «Internet», TClientSocket или TServerSocket – в среде Borland Delphi версии 5.0 и выше также на страничке компонент «Internet»). Это можно выполнить при проектировании приложения в среде разработки или же средствами языка программирования при выполнении приложения. Чтобы специфицировать (настроить) сокет, необходимо созданному экземпляру объекта присвоить нужные значения в указанные свойства (properties) – как правило, это свойства с именами вида «Port» и «Host» (имена и состав свойств зависят от версии среды разработки). Это тоже можно сделать как в режиме проектирования приложения, так и командами присвоения свойств объекта в тексте программы. После этого сокет инициализирован и с ним можно работать. Для работы сокета клиента необходимо открыть сокет (процедура Open устанавливает в Thru свойство Active), затем использовать процедуры установления соединения, передачи и приема данных, а в конце работы закрыть сокет (процедура Close). При удалении экземпляра объекта автоматически прекратит существование и связанный с ним сокет. Краткое описание основных свойств, событий и методов объектов приведено в пункте 3.8 «Справочные данные». По установлению соединения наступит событие, которое программист должен соответствующим образом обработать. В среде Borland Delphi версии 3.0 программист сам должен создавать потоки выполнения для обслуживания соединения (для выполнения потоков служат экземпляры класса объектов TThread), а в среде Borland Delphi версии 5.0 и выше это можно сделать в автоматическом режиме. Более подробную информацию о функциях программного интерфейса с сокетами можно получить в справке соответствующей среды разработки или специализированной литературе. Пример реализации простейшего клиент-серверного приложения на основе сокетов Запустить Delphi. На пустой форме (возможен вариант совмещения функций клиента и сервера в одном приложении) разместить два компонента с закладки Internet: TClientSocket и TServerSocket (в 6 и 7 версии Delphi они могут отсутствовать; для установки надо выполнить шаги: Component – Install Packages – Add – dclsockets60.bpl или dclsockets70.bpl). Для клиентской части программы понадобятся два компонента TEdit (один для ввода ip-адреса или dns-имени сервера, другой – для ввода передаваемой информации) и одна кнопка «Послать» для соединения с сервером и отправки ему сообщения. Для серверной части понадобится один компонент TMemo для выдачи служебной информации о подключениях и отображения принимаемых сообщений). В свойствах TServerSocket: установить Port в 4000, затем – Active в true. В обработчике Button1Click написать код для соединения с сервером: ClientSocket1.Host:=Edit1.Text; ClientSocket1.Port:=4000; ClientSocket1.Active:=true; Теперь, при нажатии на Button1 произойдет соединение с сервером, адрес которого был указан в Edit1. При установлении соединения произойдет событие ClientSocket1.OnConnect, которое, например, надо обработать так, чтобы на сервер передавалась строка из Edit2 и происходило отсоединение: Socket.SendText(Edit2.Text); ClientSocket1.Active:=False; При подключении клиента к ServerSocket1 произойдет событие Server- Socket1.OnClientConnect, в котором нужно вывести следующую информацию: Memo1.Lines.Add('Client connected from: '+Socket.RemoteHost); При попытке передать клиентом на сервер сообщение на сервере произойдет событие ServerSocket1.OnClientRead, в котором текст считывается и выводится в Memo1: ReceivedString:=''; {локальная переменная типа string} while Socket.ReceiveLength>0 do ReceivedString:=ReceivedString+Socket.ReceiveText; Memo1.Lines.Add('Received string: '+ReceivedString); При отсоединении клиента произойдет событие OnClientDisconnect, в ко- тором мы добавим в Memo1 соответствующее сообщение: Memo1.Lines.Add('Client '+Socket.RemoteHost+' disconnected.'); Запустите проект и протестируйте его. В качестве ip-адреса можно использовать адреса соседних компьютеров, на которых запущены аналогичные программы или адрес 127.0.0.1 для тестирования на локальном компьютере. Примечание: Оправка сообщений с сервера клиентам осуществляется с использованием тех же методов и событий соответственно на стороне сервера и клиентов. Отличие заключается в необходимости точного указания номера со- единения в свойстве Connections[Index: Integer]: TcustomWinSocket. Например: ServerSocket1.Socket.Connections[i].SendText('Test'); Здесь i = (0..N-1) – номер соединения, N – общее количество соединений. Задание на практическую работу 1. Ознакомиться с теоретическим и справочным материалом. Приложение сервера постоянно опрашивает входящие соединения, хранит историю сообщений определенной глубины (например, 20), список зарегистрированных пользователей и другую информацию, необходимую для решения задачи. Приложение клиента должно обеспечивать: регистрацию нового пользователя и его авторизацию по имени пользователя и паролю на сервере, передачу сообщений на сервер и прием сообщений от сервера, переданных на сервер другими клиентами. Справочные данные (учить) Основные свойства компонента ServerSocket: Active – инициализирует соединение (открывает для прослушивания socket) ; Name – имя экземпляра класса для использования в программе; Port – номер порта, на котором ведется прослушивание входящих соединений. Основные события компонента ServerSocket: OnAccept – зафиксировано входящее соединение; OnClientConnect – установлено соединение с клиентом; OnClientDisconnect – разорвано соединение с клиентом; OnClientError – ошибка при работе с клиентом; OnClientRead – чтение данных, получаемых от клиента, только в этом со- бытии разрешается принимать данные от клиента; OnClientWrite – запись данных, передаваемых клиенту, только в этом со- бытии разрешается передавать данные клиенту; OnListen – сервер успешно проинициализирован для прослушивания socket. Основные свойства компонента ClientSocket: Active – инициализирует соединение; Host – IP-адрес сервера, с которым устанавливается соединение; Name – имя экземпляра класса для использования в программе; Port – номер порта, на котором производится соединение с сервером. Основные события компонента ClientSocket: OnConnect – установлено соединение с сервером; OnDisconnect – разорвано соединение с сервером; OnError – ошибка при работе с сервером; OnRead – чтение данных, получаемых от сервера, только в этом событии разрешается принимать данные от сервера; OnWrite – запись данных, передаваемых серверу, только в этом событии разрешается передавать данные серверу. Основные методы класса CustomWinSocket: ReceiveText – принимает данные в виде строки символов; SendText – отправляет данные в виде строки символов. Контрольные вопросы 1. Назначение протокола TCP и принцип его работы. 2. Назначение протокола UDP и принцип его работы. 3. Назовите отличия протоколов TCP и UDP. 4. С какой целью используются порты? 5. В чем заключается сущность мультиплексирования и демультиплекси- рования? 6. Поясните сущность понятия «логическое соединение». Каким образом оно определяется в сети? 7. Поясните сущность активного режима работы сетевого приложения. 8. Поясните сущность пассивного режима работы сетевого приложения. |