- •Адресация
- •Повторители, мосты, коммутаторы и маршрутизаторы.
- •Эталонная сетевая модель osi. Межуровневые взаимодействия.
- •Физический уровень osi
- •Канальный уровень osi.
- •Сетевой уровень osi
- •Транспортный уровень osi.
- •Прикладной уровень osi.
- •Сеансовый уровень osi.
- •Представительский уровень osi.
- •Ethernet
- •TokenRing
- •FrameRelay
- •Адресация tcp/ip
- •Протокол arp
- •Протокол ip
- •Протокол icmp
- •Протокол udp
- •Протокол tcp
- •Протокол spx
- •NetWareCoreProtocol (ncp)
- •Протокол NetWare Core Packet Burst (ncpb)
- •Протокол sap
- •NetBeui
- •NetBios
- •Протокол smb
- •Основные определения winsock.
- •Сетевые протоколы, поддерживаемые Win32
- •Сокеты Windows
- •Winsock и модель osi
- •Создание сокета для tcp/ip
- •Создание сокетов и адресации для NetBios
- •Серверные api-функции
- •Api-функции клиента
- •Передача данных
- •Завершение сеанса
- •Передатчик для протоколов, не требующих соединения
- •Комплексный ввод-вывод
- •Режимы сокетов
- •Модель Select (Выбор)
- •WsaaSyncSelect
- •WsaEventSelect
- •Модель перекрытого ввода-вывода
- •Модель портов завершения
Winsock и модель osi
Поставщики транспорта из каталога Winsock, перечисленные WSAEnumProtocols, работают на транспортном уровне модели OSI, то есть каждый из них обеспечивает обмен данными. Впрочем, все они относятся к какому-то протоколу, а сетевой протокол работает на сетевом уровне, поскольку обусловливает способ адресации каждого узла в сети. Например, UDP и TCP — это транспорты, хотя оба относятся к протоколу IP. Интерфейс Winsock расположен между сеансовым и транспортным уровнями. Winsock позволяет открывать и закрывать сеанс связи и управлять им для любого данного транспорта. Под управлением Windows три верхних уровня: прикладной, представительский и сеансовый, — в основном относятся к приложению Winsock. Другими словами, приложение Winsock управляет всеми аспектами сеанса связи и при необходимости форматирует данные согласно целям программы.
Создание сокета для tcp/ip
Для создания сокета в операционной системе служит системный вызов socket(). Для транспортных протоколов семейства TCP/IP существует два вида сокетов: UDP-сокет – сокет для работы с датаграммами, и TCP сокет – потоковый сокет. При создании сокета необходимо точно специфицировать его тип. Эта спецификация производится с помощью трех параметров вызова socket().
Первый параметр указывает, к какому семейству протоколов относится создаваемый сокет.
Второй параметр служит для задания вида интерфейса работы с сокетом – будет это потоковый сокет, сокет для работы с датаграммами или какой-либо иной.
Третий параметр указывает протокол для заданного типа интерфейса. В стеке протоколов TCP/IP существует только один протокол для потоковых сокетов – TCP и только один протокол для датаграммных сокетов – UDP, поэтому для транспортных протоколов TCP/IP третий параметр игнорируется.
Для транспортных протоколов TCP/IP мы всегда в качестве первого параметра будем указывать предопределенную константу AF_INET (Address family – Internet) или ее синоним PF_INET(Protocol family – Internet).
Второй параметр будет принимать предопределенные значения SOCK_STREAM для потоковых сокетов и SOCK_DGRAM – для датаграммных.
Поскольку третий параметр в нашем случае не учитывается, в него мы будем подставлять значение 0.
Ссылка на информацию о созданном сокете помещается в таблицу открытых файлов процесса подобно тому, как это делалось для pip’ов и FIFO (см. семинар 5). Системный вызов возвращает пользователю файловый дескриптор, соответствующий заполненному элементу таблицы, который далее мы будем называть дескриптором сокета. Такой способ хранения информации о сокете позволяет, во-первых, процессам-детям наследовать ее от процессов-родителей, а, во-вторых, использовать для сокетов часть системных вызовов, которые уже знакомы нам по работе с pip’ами и FIFO: close(), read(), write().
|
Системный вызов для создания сокета Прототип системного вызова #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol); Описание системного вызова Параметр domain определяет семейство протоколов, в рамках которого будет осуществляться передача информации. Мы рассмотрим только два таких семейства из нескольких существующих. Для них имеются предопределенные значения параметра: PF_INET – для семейства протоколов TCP/IP; PF_UNIX – для семейства внутренних протоколов UNIX, иначе называемого еще UNIX domain. Параметр type определяет семантику обмена информацией: будет ли осуществляться связь через сообщения (datagrams), с помощью установления виртуального соединения или еще каким-либо способом. SOCK_DGRAM – для обмена информацией через сообщения. Параметр protocol специфицирует конкретный протокол для выбранного семейства протоколов и способа обмена информацией. Он имеет значение только в том случае, когда таких протоколов существует несколько. В нашем случае семейство протоколов и тип обмена информацией определяют протокол однозначно. Поэтому данный параметр мы будем полагать равным 0. Возвращаемое значение В случае успешного завершения системный вызов возвращает файловый дескриптор (значение большее или равное 0), который будет использоваться как ссылка на созданный коммуникационный узел при всех дальнейших сетевых вызовах. При возникновении какой-либо ошибки возвращается отрицательное значение. |
Адреса сокетов. Настройка адреса сокета. Системный вызов bind()
Когда сокет создан, необходимо настроить его адрес. Для этого используется системный вызов bind(). Первый параметр вызова должен содержать дескриптор сокета, для которого производится настройка адреса. Второй и третий параметры задают этот адрес.
Во втором параметре должен быть указатель на структуру struct sockaddr, содержащую удаленную и локальные части полного адреса.
Указатели типа struct sockaddr * встречаются во многих сетевых системных вызовах; они используются для передачи информации о том, к какому адресу привязан или должен быть привязан сокет. Рассмотрим этот тип данных подробнее. Структура struct sockaddr описана в файле <sys/socket.h> следующим образом:
struct sockaddr {
short sa_family;
char sa_data[14];
};
Для работы с семейством протоколов TCP/IP мы будем использовать адрес сокета следующего вида, описанного в файле <netinet/in.h>:
struct sockaddr _in{
short sin_family;
/* Избранное семейство протоколов
– всегда AF_INET */
unsigned short sin_port;
/* 16-битовый номер порта в сетевом
порядке байт */
struct in_addr sin_addr;
/* Адрес сетевого интерфейса */
char sin_zero[8];
/* Это поле не используется, но должно
всегда быть заполнено нулями */
};
Первый элемент структуры – sin_family задает семейство протоколов. В него мы будем заносить уже известную нам предопределенную константу AF_INET (см. предыдущий раздел).
Удаленная часть полного адреса – IP-адрес – содержится в структуре типа struct in_addr, с которой мы встречались в разделе "Функции преобразования IP-адресов inet_ntoa(),inet_aton()" .
Для указания номера порта предназначен элемент структуры sin_port, в котором номер порта должен храниться в сетевом порядке байт. Существует два варианта задания номерапорта: фиксированный порт по желанию пользователя и порт, который произвольно назначает операционная система. Первый вариант требует указания в качестве номера портаположительного заранее известного числа и для протокола UDP обычно используется при настройке адресов сокетов и при передаче информации с помощью системного вызова sendto()(см. следующий раздел). Второй вариант требует указания в качестве номера порта значения 0. В этом случае операционная система сама привязывает сокет к свободному номеру порта. Этот способ обычно используется при настройке сокетов программ клиентов, когда заранее точно знать номер порта для программисту необязательно.
IP–адрес при настройке также может быть определен двумя способами. Он может быть привязан к конкретному сетевому интерфейсу (т.е. сетевой плате), заставляя операционную систему принимать/передавать информацию только через этот сетевой интерфейс, а может быть привязан и ко всей вычислительной системе в целом (информация может быть получена/отослана через любой сетевой интерфейс). В первом случае в качестве значения поля структуры sin_addr.s_addr используется числовое значение IP-адреса конкретного сетевого интерфейса в сетевом порядке байт. Во втором случае это значение должно быть равно значению предопределенной константы INADDR_ANY, приведенному к сетевому порядку байт.
Третий параметр системного вызова bind() должен содержать фактическую длину структуры, адрес которой передается в качестве второго параметра. Эта длина меняется в зависимости от семейства протоколов и даже различается в пределах одного семейства протоколов. Размер структуры, содержащей адрес сокета, для семейства протоколов TCP/IP может быть определен как sizeof(struct sockaddr_in).
|
Системный вызов для привязки сокета к конкретному адресу Прототип системного вызова #include <sys/types.h> #include <sys/socket.h> int bind(int sockd, struct sockaddr *my_addr, int addrlen); Описание системного вызова Системный вызов bind служит для привязки созданного сокета к определенному полному адресу вычислительной сети. Параметр sockd является дескриптором созданного ранее коммуникационного узла, т. е. значением, которое вернул системный вызов socket(). Параметр my_addr представляет собой адрес структуры, содержащей информацию о том, куда именно мы хотим привязать наш сокет – то, что принято называть адресом сокета. Он имеет тип указателя на структуру-шаблон struct sockaddr, которая должна быть конкретизирована в зависимости от используемого семейства протоколов и заполнена перед вызовом. Параметр addrlen должен содержать фактическую длину структуры, адрес которой передается в качестве второго параметра. Эта длина в разных семействах протоколов и даже в пределах одного семейства протоколов может быть различной (например, для UNIX Domain). Возвращаемое значение Системный вызов возвращает значение 0 при нормальном завершении и отрицательное значение – в случае ошибки.
Адресация В IPX-сетях сегменты соединяются через IPX-маршрутизаторы. Каждому сег-менту назначается уникальный четырехбайтный номер сети (network num-ber). Эти номера используются IPX-маршрутизатами для управления подклю-чениями между разными сегментами сети. Компьютер, подключенный к сег-менту сети, идентифицируется при помощи шестибайтного номера узла (node number) — обычно это физический адрес сетевого адаптера. Узел (компьютер) вправе запускать несколько процессов связи по IPX, для разли-чения которых применяются номера сокетов Для подготовки Winsock-клиента или сервера к подключению по IPX, необходимо настроить структуру SOCKADDRJPX Она определена в заголо-вочном файле Wsipx h, ссылка на него в приложении должна идти вслед за Winsock2 h Структура SOCKADDRJPX определена так typedef struct sockaddr_ipx { short sa_family, char sa_netnum[4], char sa_nodenum[6], unsigned short sa_socket, } SOCKADDR_IPX, *PSOCKADDR_IPX, FAR *LPSOCKADDR_IPX, Поле sajamily всегда равно AF_IPX Поле sajnetnum — 4-байтный номер сети в сегменте IPX-сети Поле sajiodenum — б-байтный номер узла Поле sa_socket представляет сокет или порт, используемый для различения IPX-подключений на одном узле. Создание сокета Создать IPX-сокет можно несколькими способами Для открытия 1РХ-соке-та вызовите функции socket или WSASocket с семейством адресов AFJPX, ти-пом сокета SOCKJDGRAM и протоколом NSPROTOJPX s = socket(AF_IPX, SOCK.DGRAM, NSPROTO.IPX), s = WSASocket(AF_IPX, SOCK.DGRAM, NSPROTO_IPX, NULL, 0, WSA_FLAG_OVERLAPPED), Заметьте, что третий параметр протокола должен быть обязательно задан и не равен 0 Это важно, поскольку поле протокола может использоваться для настройки особых типов IPX-пакетов. Как мы уже говорили, IPX обеспечивает ненадежное соединение без дей-таграмм Если приложению требуется надежное соединение с применени-ем этого протокола, оно может использовать поверх IPX протоколы более высокого уровня, такие как SPX и SPX II Для этого необходимо при вызове функций socket или WSASocket задать соответствующие значения полей типа — SOCK_SEQPACKET или SOCKJTREAM, и протокола — NSPROTO_SPX или NSPROTOJPXII Если тип сокета — SOCKJSTREAM, данные передаются в виде непрерывно-го потока байт, без разделения сообщений, подобно действию сокета в TCP/IP Если тип сокета — SOCK_SEQPACKET, данные передаются с разделителя-ми сообщений Например, передатчик отправляет 2000 байт — приемник не сможет ответить, пока не получит все 2000 байт В SPX и SPXII для этого нуж-но указать бит конца сообщения в заголовке SPX Для сокетов типа SOCK_SEQPACKET подразумевается, что этот бит указан, и выполнение Winsock-функции recv и WSARecv не завершится, пока пакет не будет получен Для поточных сокетов бит конца сообщения не требуется, и выполнение функ-ции recv будет завершено сразу же по получении любых данных, независимо от наличия бита конца сообщения С точки зрения отправителя при ис-пользовании типа SOCK_SEQPACKET сообщения размером меньше пакета, всегда передаются с указанным битом конца При отправке нескольких па-кетов этот бит задается только в последнем пакете Привязка сокета Когда IPX-приложение при помощи функции bind создаст привязку локаль-ного адреса к сокету, указывать номер сети и адрес узла в структуре SOCKA-DDR IPX не нужно Функция bind заполнит эти поля при помощи первого же сетевого интерфейса IPX, доступного в системе Если на компьютере ус-тановлено несколько сетевых адаптеров, привязка к конкретному интерфей-су также не требуется В Windows 95, Windows 98, Windows NT и Windows 2000 реализована виртуальная внутренняя сеть, в которой к каждому сете вому адаптеру можно обратиться, независимо от того, к какой физической сети он подключен (Внутренние номера сети подробно рассмотрены далее в этой главе ) После успешной привязки приложения к локальному интер-фейсу с помощью функции getsockname вы можете узнать номер локальной сети и номер узла следующим образом SOCKET sdServer, SOCKADOR.IPX IPXAddr, int addrlen = sizeof(SOCKADDR_IPX), if ((sdServer = socket (AF_IPX, SOCK_DGRAM, NSPROTO.IPX)) == INVALID_SOCKET) { printf( socket failed with error Xd\n , WSAGetLastErrorO), return, ZeroMemory(&IPXAddr, sizeof(SOCKADDR_IPX)), IPXAddr sa_family = AF_IPX, IPXAddr sa_socket = htons(5150), if (bind(sdServer, (PSOCKADDR) «JPXAddr, sizeof(SOCKADDR_IPX)) == SOCKET_ERROR) { pnntf( bind failed with error Xd\n , WSAGetLastErrorO), return, (getsockname((unsigned) sdServer, (PSOCKADDR) &IPXAddr, &addrlen) == SOCKET_ERR0R) pnntf( getsockname failed with error Xd1, * ' WSAGetLastErrorO), return |
