Адресация сокетов для протокола IP Цель работы Изучить: - возможности реализации архитектуры клиент-сервер на основе интерфейса сокетов Windows Sockets API; - типы сокетов TCP/IP; - основные методики и API-функции для разработки сетевых приложений с использованием Winsock. Постановка задачи 1.Изучить методические указания к лабораторной работе, материалы лекций и рекомендуемую литературу. 2.Разработать консольное клиент-серверное приложение, демонстрирующее взаимодействие на основе потоковых сокетов. 3.Разработать консольное клиент-серверное приложение, демонстрирующее взаимодействие на основе дейтаграммных сокетов. Методические указания Понятие сокета Сокет (Socket - гнездо, разъем) - абстрактное программное понятие, используемое для обозначения в прикладной программе конечной точки канала связи с коммуникационной средой, образованной вычислительной сетью. Соединяя вместе два сокета, можно передавать данные между разными процессами (локальными и удаленными). Реализация сокетов обеспечивает инкапсуляцию протоколов сетевого и транспортного уровней. Интерфейс, используемый приложением при взаимодействии с программным обеспечением транспортного протокола, называется интерфейсом прикладного программирования (Application Programming Interface - API). API интерфейс определяет набор операций, которые могут быть выполнены приложением при взаимодействии с программным обеспечением протокола. Функции прикладного программного интерфейса сокетов (Sockets API) обеспечивают идентификацию конечных точек соединения, установку соединения, отправку сообщений, ожидание входящих сообщений, разрыв соединения и обработку ошибок. Протоколы и семейства адресов Важнейшим преимуществом сокетов Windows является предоставление единого независимого интерфейса сетевого программирования (Sockets API) для различных сетевых протоколов. Платформы Win32 поддерживают разнообразные сетевые стеки протоколов : TCP/IP, IPX/SPX, NetBIOS/SMB, AppleTalk, ATM, Infrared Sockets. Каждому из них соответствует свое семейство адресов сокетов. Напрмер, TCP/IP соответствует семейство адресов AF_INET, IPX/SPX – AF_NS, ATM –AF_ATM и т.д. Семейство адресов – важнейший параметр сокета. Он указывает используемый в настоящее время протокол и ограничивает применение других параметров сокета. Мы рассмотрим адресацию сокетов только для стека протоколов TCP/IP, как самого распространенного на сегодняшний день. Адрес сокета при использовании протоколов TCP/IP задает следующий набор значений: - номер сети; - номер конечного узла; - номер порта прикладной службы. Инициализация Winsock Перед вызовом любой функции Winsock необходимо загрузить соответствующую версию библиотеки Winsock. Для использования в приложении Winsock 2 необходимо подключить библиотеку Ws2_32.lib и заголовочный файл Winsock2.h. Имена новых или обновленных API-функций, добавленные в Winsock 2, начинаются с префикса WSA. Инициализацию Winsock выполняет функция WSAStartup: int WSAStartup( WORD wVersionRequested, LPWSADATA IpWSAData); Параметр wVersionRequested задает версию загружаемой библиотеки Winsock. На современных платформах Win32 используется версия 2.2. Для получения значения параметра wVersionRequested можно использовать макрос MAKEWORD(2, 2) либо значение 0х0202. Параметр IpWSAData — указатель на структуру LPWSADATA, которая при вызове функции WSAStartup заполняется сведениями о версии загружаемой библиотеки. По завершении работы с библиотекой Winsock необходимо вызвать функцию WSACleanup для выгрузки библиотеки и освобождения ресурсов: int WSACleanup (void); Адресация сокетов для протокола IP Для задания IP-адреса и номера порта службы используется структура SOCKADDR_IN. Она определена в include-файле in.h следующим образом: struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; Поле sin_family при использовании семейства адресов IP должно быть равно AF_INET. Поле sin_port задает, какой коммуникационный порт будет использован для идентификации службы. Поле sin_addr структуры SOCKADDR_IN хранит IP-адрес в 4-байтном виде с типом unsigned long int. В зависимости от того, как это поле использовано, оно может представлять и локальный, и удаленный IP-адрес. Поле sin_zero играет роль заполнителя, чтобы структура SOCKADDR_IN по размеру равнялась структуре SOCKADDR. Специальные IP-адреса Специальный адрес INADDR_ANY (0.0.0.0) позволяет приложению слушать клиента через любой сетевой интерфейс на несущем компьютере. Обычно приложения сервера используют этот адрес, чтобы привязать сокет к локальному интерфейсу для прослушивания соединений. Если на компьютере несколько сетевых адаптеров, то этот адрес позволит отдельному приложению получать отклики от нескольких интерфейсов. Второй специальный адрес – INADDR_BROADCAST (255.255.255.255), позволяет широковещательно рассылать пакеты по IP – сети. Для его использования необходимо в приложении задать параметр сокета SO_BROADCAST. Порядок байт В памяти компьютера IP-адрес и номер порта представляются в системном порядке (host-byte-order). Для процессоров Intel Pentium это порядок от менее значимого к более значимому байту (обратный). Стандарты Internet требуют, чтобы многобайтные значения передавались от старшего байта к младшему, что называется сетевым порядком (network-byte order) или прямым порядком следования байтов. Поэтому существует необходимость преобразования чисел из одной формы в другую. Для этого предназначен целый ряд функций. Например, функции htonl (Host TO Network Long), WSAHtonl, htons (Host TO Network Short), WSAHtons преобразуют числа из системного порядка в сетевой. Функции ntohl, WSANtohl, ntohs, WSANtohs решают обратную задачу (из сетевого в системный). Полезная вспомогательная функция inet_addr преобразует IP-адрес из точечно-десятичной нотации в 32-битное длинное целое без знака с сетевым порядком следования байт: unsigned long inet_addr( const char FAR *cp); Поле ср - строка, заканчивающаяся нулевым символом, в которой задается IP-адрес в точечно-десятичной нотации. Разрешение имен В Winsock предусмотрено две функции для разрешения имени в IP-адрес. Функции gethostbyname и WSAAsyncGetHostByName отыскивают в базе данных узла сведения об узле, соответствующие его имени. Обе функции возвращают структуру HOSTENT: struct hostent { char FAR * h_name; char FAR * FAR * h_aliases; short h_addrtype; short h_length; char FAR * FAR * h_addr_list; }; Поле h_name является официальным именем узла. Если в сети используется доменная система имен (Domain Name System, DNS), в качестве имени сервера будет возвращено полное имя домена (Fully Qualified Domain Name, FQDN). Если в сети для разрешения имен применяется локальный файл (hosts, lmhosts) - это первая запись после IP- адреса. Поле h_aliases – массив дополнительных имен узла, завершающийся нулем. Поле h_addrtype – возвращаемое семейство адресов. Поле h_length определяет длину в байтах каждого адреса из поля h_addr_list. Поле h_addr_list – массив, завершающийся 0 и содержащий IP-адреса узла. (Узел может иметь несколько IP-адресов). Каждый адрес в этом массиве представлен в сетевом порядке. Обычно приложение использует первый адрес из массива. Впрочем, при получении нескольких адресов, приложение должно выбирать адрес случайным образом из числа доступных, а не упорно использовать первый. API–функция gethostbyname определена так: struct hostent FAR *gethostbyname (const char FAR *name); Параметр name представляет дружественное имя искомого узла. При успешном выполнении функции возвращается указатель на структуру HOSTENT, которая хранится в системной памяти. Приложение не должно полагать, что эти сведения непременно статичны. Поскольку эта память обслуживается системой, оно не должно освобождать возвращенную структуру. WSAAsyncGetHostByName – асинхронная версия функции gethostbyname, оповещающая приложение о завершении своего выполнения с помощью сообщений Windows: HANDLE WSAAsyncGetHostByName ( HWND hWnd, unsigned int wMsg, const char FAR *name, char FAR * buf, int buflen); Параметры hWind – дескриптор окна, которое получит сообщение по завершении выполнения асинхронного запроса. Параметр wMsg –Windows-сообщение, которое будет возвращено по завершении выполнения асинхронного запроса. Параметр name - дружественное имя искомого узла. Параметр buf – указатель на область данных, куда помещается HOSTENT. Этот буфер должен быть больше структуры HOSTENT и иметь размер, определенный в MAXGETHOSTSTRUCT. Для преобразования IP-адреса в имя узла используются функции gethostbyaddr и WSAAsyncGetHostByAddr. Функция gethostbyaddr определена так: struct HOSTENT FAR * gethostbyaddr( const char FAR * addr, int len, int type); Параметр addr – указатель на IP–адрес в сетевом порядке. Параметр len задает длину параметра addr в байтах. Параметр type должен иметь значение AF_INET (IP-адрес). Функция WSAAsyncGetHostByAddr – асинхронная версия функции gethostbyaddr. Номера портов Приложения должны быть внимательны при выборе номера порта, поскольку некоторые доступные порты зарезервированы для использования популярными службами, такими как FTP, HTTP и т.д. Эти порты обслуживаются и распределяются центром Internet Assigned Numbers Authority (IANA), их описание содержится в RFC 1700. Номера портов разделяются на 3 категории (стандартные, зарегистрированные и динамические и/или частные): - Номера от 0 до 1023 зарезервированы для стандартных служб. - Порты с номерами от 1024 до 49151 являются регистрируемыми. Они используются для различных целей. - Порты с номерами от 49152 до 65535 представляют собой динамические и частные порты. Во избежание накладок с портами, уже занятыми системой или другим приложением, ваша программа должна выбирать порты, начиная с 1024. Можно вместо конкретного номера порта задать 0, тогда система сама выберет произвольный неиспользуемый в данный момент номер. Узнать номера портов, используемых стандартными службами, можно вызвав функцию getservbyname или WSAAsyncGetServByName. Эти функции извлекают статическую информацию из файла services, расположенного в папке %WINDOWS%\System32\Drivers\Etc Функция getservbyname определена так: struct servent FAR *getservbyname( const char FAR *name, const char FAR *proto); Параметр name представляет имя искомой службы. Параметр proto ссылается на строку, указывающую протокол, под которым зарегистрирована служба из параметра name. Структура servent имеет системный порядок следования байт. Функция WSAAsyncGetServByName – асинхронная версия getservbyname. Типы сокетов Существуют три основных типа сокетов: потоковые, дейтаграммые и сырые. Потоковые сокеты – это сокеты с установлением соединения, состоящие из потока байтов, который может быть двунаправленным. Т.е. через такую конечную точку приложение может и передавать, и получать данные. Потоковый сокет гарантирует обнаружение и исправление ошибок, обрабатывает доставку и сохраняет последовательность данных. Он подходит для передачи больших объемов данных, поскольку в этом случае накладные расходы, связанные с установлением соединения, незначительны по сравнению со временем передачи самого сообщения. Качество передачи достигается за счет использования протокола TCP. Дейтаграммные сокеты – это сокеты без установления соединения, не обеспечивающие надежность при передаче. Применяются для приложений, когда неприемлемы затраты времени, связанные с установлением явного соединения. Для передачи данных используется протокол UDP. Сырые сокеты (необрабатываемые, простые) – это сокеты, которые принимают пакеты сетевого уровня в обход протоколов транспортного уровня и отправляют их непосредственно приложению. |