Лекция N3. Реализация архитектуры «Клиент-сервер» с использованием механизма сокетов Сокет – это описатель сетевого соединения с другими приложениями. С помощью сокетов программист может разрабатывать свои протоколы для обмена информацией между сервером и клиентом. Для создания сокета необходимо: IP-адрес локальной машины (клиента) Номер порта TCP, который использует приложение на локальной машине (на машине клиента) IP-адрес машины с которой устанавливается соединение (сервер) Номер порта TCP, на который отзывается приложение, ожидающее установления связи (на стороне сервера) Схема взаимодействия сервера и клиента Обзор сокетов Библиотека Winsock поддерживает два вида сокетов - синхронные (блокируемые) и асинхронные (неблокируемые). Синхронные сокеты задерживают управление на время выполнения операции, а асинхронные возвращают его немедленно, продолжая выполнение в фоновом режиме, и, закончив работу, уведомляют об этом вызывающий код. Независимо от вида, сокеты делятся на два типа - потоковые и дейтаграммные. Потоковые сокеты работают с установкой соединения, обеспечивая надежную идентификацию обоих сторон и гарантируют целостность и успешность доставки данных. Дейтаграмные сокеты работают без установки соединения и не обеспечивают ни идентификации отправителя, ни контроля успешности доставки данных, зато они заметно быстрее потоковых. Выбор того или иного типа сокетов определяется транспортным протоколом на котором работает сервер, - клиент не может по своему желанию установить с дейтаграммным сервером потоковое соединение. Замечание: дейтаграммные сокеты опираются на протокол UDP, а потоковые на TCP. Принципы применения сокетов При создании сокета, указывается в какой области происходит работа: UNIX (AF_UNIX), TCP/IP (AF_INET), X25, DECNET, APPLETALK... Каждой области соответствует свой тип протокола. Кроме того, задается тип, определяющий свойства коммуникации: - SOCK_DGRAM: сообщения посылаются в форме дейтаграмм. Связанный с ним протокол связи нe является таким надежным (нарушается последовательность, возможны потери данных) в реэиме без установления логического соединения, как UDP в области AF_INET; - SOCK_STREAM: посылаются потоки байтов, понятие "сообщения" не вводится. Используемый протокол связи надежен, с установлением виртуального соединения, как TCP в области AF_INET; - SOCK_ RAW: обеспечивает доступ к протоколам самого низкого уровня, таким как IP в области AF_INET, либо реализует новые протоколы. Если используются сокеты над UDP размер передаваемых данных ограничен несколькими килобайтами, от 2 Кб до 8 Кб, в зависи-мости от системы. Сокет-интерфейс можно использовать для связи между двумя процессами на одной машине. В этом случае необходимо указать, что работа производится в области AF_UNIX. Вызовы сокетов для области AF_UNIX те же, что и для области AF_INET; меняются только структуры, связанные с адресами. Данное сходство вызо-вов позволяет достаточно легко переходить от локальных задач к сетевым и обратно. Использование в режиме с установлением виртуального соединения Клиент : - создает сокет; - подсоединяется к серверу, предоставляя адрес удаленного сокета (адрес Internet сервера и номер сервисного порта). Это соединение автоматически присваивает клиенту номер порта; - осуществляет считывание или запись на сокет; - закрывает сокет. Сервер: - создает сокет; - связывает сокет-адрес (адрес Internet и номер порта) с сервисной программой: "binding"; - переводит себя в состояние "прослушивания" входящих соединений; - для каждого входящего соединения: - принимает соединение (создается новый сокет с теми же характеристиками, что и исходный; - считывает и записывает на новый сокет; - закрывает новый сокет. На рис. 4.4. показаны примитивы, используемые для сокетов типа SOCK_STREAM. Рис. 4.4. Использование сокетов с установлением логического соединения. Некоторые вызовы способные заблокировать программу : Клиент: - connect () до того, как сервер осуществит accept (); - write () при переполнении буфера передачи; - read () до того, как будет получен хотя бы один символ вследствие операции записи, осуществленной сервером. Сервер: - accept () до того, как клиент осуществит connect (); - read () до того, как будет получен хотя бы один символ, вследствие операции записи, осуществленной клиентом; - write () при переполнении буфера передачи. Использование в режиме дейтаграмм Клиент: - создает сокет; - связывает сокет-адрес с сервисной программой: "binding" (операция, являющаяся необходимой только в случае, если процесс должен получить данные); - считывает или осуществляет запись на сокет. Сервер: - создает сокет; - связывает сокет-адрес с сервисной программой: "binding" (операция необходима только в случае, если процесс должен получить данные); - считывает или осуществляет запись на сокет. На рис. 4.5. показаны примитивы, используемые для сокетов типа SOCK_DGRAM. Адресация Много вызовов, связанных с сокетами требуют в качестве аргу-мента указатель структуры, содержащий сокет-адрес, общая структура которого определена в файле <sys/socket.h>: struct sockaddr { u_short sa_family; /*AF_UNIX или AF_INET*/ char sa_data [14];/*абсолютный адрес протокола*/ }; Тип u_short, также как и определенное число других используемых типов, определены в файле <sys/types.h>. Рис. 4.5. Использование сокетов в режиме дейтаграмм. Адресация в области AF_INET В области AF_INET клиент и сервер явно указывают используемый транспортный протокол (TCP или UDP). Сервер связывает свою сервисную программу с сокет-адресом (адрес Internet и номер порта), затем переходит в состояние ожидания запросов от кли-ентов. Клиент адресует свои запросы, предоставляя серверу адрес Internet и номер сервисного порта. В файле <netinet/in.h> определены следующие структуры: struct in_addr { u_long s_addr; }; struct sockaddr_in { short sin_family; /*AF_INET*/ u_short sin_port; /*номер порта*/ struct in_addr sin_addr; /*машинный адрес Internet*/ char sin_zero [8]; /*не использован*/ }; Краткое описание компонента TServerSocket (Borland) Здесь мы познакомимся с основными свойствами, методами и событиями компонента TServerSocket. Свойства Методы События Socket - класс TServerWinSocket, через который Вы имеете доступ к открытым сокетным каналам. Далее мы рассмотрим это свойство более подробно, т.к. оно, собственно и есть одно из главных. Тип: TServerWinSocket; ServerType - тип сервера. Может принимать одно из двух значений: stNonBlocking - синхронная работа с клиентскими сокетами. При таком типе сервера Вы можете работать с клиентами через события OnClientRead и OnClientWrite. stThreadBlocking - асинхронный тип. Для каждого клиентского сокетного канала создается отдельный процесс (Thread). Тип: TServerType; ThreadCacheSize - количество клиентских процессов (Thread), которые будут кэшироваться сервером. Здесь необходимо подбирать среднее значение в зависимости от загруженности Вашего сервера. Кэширование происходит для того, чтобы не создавать каждый раз отдельный процесс и не убивать закрытый сокет, а оставить их для дальнейшего использования. Тип: Open - Запускает сервер. По сути, эта команда идентична присвоению значения True свойству Active; Close - Останавливает сервер. По сути, эта команда идентична присвоению значения False свойству Active. OnClientConnect - возникает, когда клиент установил сокетное соединение и ждет ответа сервера (OnAccept); OnClientDisconnect - возникает, когда клиент отсоединился от сокетного канала; OnClientError - возникает, когда текущая операция завершилась неудачно, т.е. произошла ошибка; OnClientRead - возникает, когда клиент передал берверу какие-либо данные. Доступ к этим данным можно получить через пеаедаваемый параметр Socket: TCustomWinSocket; OnClientWrite - возникает, когда сервер может отправлять данные клиенту по сокету; OnGetSocket - в обработчике этого события Вы можете отредактировать параметр ClientSocket; OnGetThread - в обработчике этого события Вы можете определить уникальный процесс (Thread) для каждого отдельного клиентского канала, присвоив параметру SocketThread нужную подзадачу TServerClientThread; OnThreadStart, OnThreadEnd - возникает, когда подзадача (процесс, Integer; Active - показатель того, активен в данных момент сервер, или нет. Т.е., фактически, значение True указывает на то, что сервер работает и готов к приему клиентов, а False - сервер выключен. Чтобы запустить сервер, нужно просто присвоить этому свойству значение True. Тип: Boolean; Port - номер порта для установления соединений с клиентами. Порт у сервера и у клиентов должны быть одинаковыми. Рекомендуются значения от 1025 до 65535, т.к. от 1 до 1024 могут быть заняты системой. Тип: Integer; Service - строка, определяющая службу (ftp, http, pop, и т.д.), порт которой будет использован. Это своеобразный справочник соответствия номеров портов различным стандартным протоколам. Тип: string; Thread) запускается или останавливается, соответственно; OnAccept - возникает, когда сервер принимает клиента или отказывает ему в соединении; OnListen - возникает, когда сервер переходит в режим ожидания подсоединения клиентов. TServerSocket.Socket (TServerWinSocket) Итак, как же сервер может отсылать данные клиенту? А принимать данные? В основном, если Вы работаете через события OnClientRead и OnClientWrite, то общаться с клиентом можно через параметр ClientSocket (TCustomWinSocket). Про работу с этим классом можно прочитать в статье про клиентские сокеты, т.к. отправка/посылка данных через этот класс аналогична - методы (Send/Receive)(Text,Buffer,Stream). Также и при работе с TServerSocket.Socket. Однако, т.к. здесь мы рассматриваем сервер, то следует выделить некоторые полезные свойства и методы: ActiveConnections (Integer) - количество подключенных клиентов; ActiveThreads (Integеr) - количество работающих процессов; Connections (array) - массив, состоящий из отдельных классов TClientWinSocket для каждого подключенного клиента. Например, такая команда: ServerSocket1.Socket.Connections[0].SendText('Hello!'); отсылает первому подключенному клиенту сообщение 'Hello!'. Команды для работы с элементами этого массива - также (Send/Receive)(Text,Buffer, Stream); IdleThreads (Integer) - количество свободных процессов. Такие процессы кэшируются сервером (см. ThreadCacheSize); LocalAddress, LocalHost, LocalPort - соответственно - локальный IP-адрес, хост-имя, порт; RemoteAddress, RemoteHost, RemotePort - соответственно - удаленный IP-адрес, хост-имя, порт; Методы Lock и UnLock - соответственно, блокировка и разблокировка сокета.