Главная Работа с сокетами Работа с сокетами 2 |
Работа с сокетами
Прежде чем воспользоваться функциями, находящихся в Wsock32.dll, ws2_32.dll нам необходимо создать обычный модуль(*.bas) в котором разместятся все объявления этих функций. Запишем описание необходимых функций:
Public Declare Function WSAStartup Lib "ws2_32.dll" (ByVal wVR As Long, lpWSAD As WSA_Data) As Long
Для инициализации библиотеки и для проверки ее версии нам необходима будет эта функция, где wVR-это необходимая минимальная версия библиотеки, при присутствии которой ваше приложение будет корректно работать, как правило в качестве этого параметра передают 1. Объявим эту константу
Public Const WINSOCK_VERSION = 1 'эта константа применяется при вызове WSAStartup ' в качестве первого параметра
Второй параметр LpWSAD-это указатель на структуру WSA_Data. Ее необходимо объявить перед объявлением функции в следующем виде: Public Type WSA_Data wVersion As Integer wHighVersion As Integer szDescription As String * WSADESCRIPTION_LEN szSystemStatus As String * WSASYS_STATUS_LEN iMaxSockets As Integer iMaxUdpDg As Integer lpVendorInfo As Long End Type где WSADESCRIPTION_LEN, WSASYS_STATUS_LEN это константы которые тоже необходимо обьявить будет выше: Public Const WSADESCRIPTION_LEN = 257 Public Const WSASYS_STATUS_LEN = 129
Поля iMaxSockets и iMaxUdpDg в версии 2.0 не используются и остались только для совместимости с версией 1.1.
Следующая функция - наверное самая главная функция, которую надо обьявить Public Declare Function socket Lib "wsock32.dll" (ByVal _ af As Long, ByVal s_type As Long, ByVal protocol_ As Long) As Long af – семейство протоколов (AF_INET, AF_IPX), type – тип протокола (SOCK_STREAM, SOCK_DGRAM) и протокол – указывает конкретный протокол (обычно указывается в 0 для TCP/IP или в NSPROTO_IPX или NSPROTO_SPX)
Перед этой функцией обьявим следующие константы, необходимые нам для дальнейшей работы, значение их будет описано позже, хотя и без описания по-моему все и так ясно
'---------------Address families------------
Public Enum ADDRESS_FAMILIES
AF_INET = 2 AF_NS = 6 AF_IPX = AF_NS PF_INET = 2 End Enum
'---------------Socket Types----------------
Public Enum SOCKET_TYPES
SOCK_STREAM = 1 SOCK_DGRAM = 2 End Enum
'---------------Protocols-------------------
Public Enum PROTOCOLS
IPPROTO_TCP = 6 IPPROTO_IP = 0 End Enum
Чтобы работать дальше с созданным сокетом его нужно привязать к какому-нибудь локальному адресу и порту. Эта процедура выполняется функцией: Public Declare Function bind Lib "wsock32" (ByVal socket As Long, addr As sockaddr, ByVal namelen As Long) As Long В общем виде структура sockaddr имеет следующий вид: Type sockaddr sin_family As Integer sin_port As Integer sin_addr As Long sin_zero As String * 8 End Type Поле sin_family определяет используемый формат адреса (набор протоколов), в нашем случае (для TCP/IP) оно должно иметь значение AF_INET. Поле sin_addr содержит адрес (номер) узла сети. Поле sin_port содержит номер порта на узле сети. Поле sin_zero не используется.
Для установления связи "клиент-сервер" используются системные вызовы listen и accept (на стороне сервера), а также connect (на стороне клиента). Для заполнения полей структуры socaddr_in, используемой в вызове connect, обычно используется библиотечная функция gethostbyname, транслирующая символическое имя узла сети в его номер (адрес). Public Declare Function listen Lib "wsock32.dll" _ (ByVal s As Long, ByVal backlog As Long) As Long Аргумент s задает дескриптор socket'а, через который программа будет ожидать запросы к ней от клиентов backlog – это максимальный размер очереди входящих сообщений на соединение. В качестве backlog будем передавать следующую константу, объявим ее: Public Const QUEUE_SIZE = 5 Если получен запрос на соединение, то мы можем подтвердить и установить соединение при помощи функции accept: Public Declare Function Accept Lib "wsock32.dll" _ Alias "accept" (ByVal s As Long, addr As sockaddr, _ addrlen As Long) As Long
Аргумент s задает дескриптор socket'а, через который программа-сервер получила запрос на соединение (посредством системного запроса listen ). Аргумент addr должен указывать на область памяти, размер которой позволял бы разместить в ней структуру данных, содержащую адрес socket'а программы-клиента, сделавшей запрос на соединение. Никакой инициализации этой области не требуется. Аргумент p_addrlen должен указывать на область памяти в виде целого числа, задающего размер (в байтах) области памяти, указываемой аргументом addr. Системный вызов accept извлекает из очереди, организованной системным вызовом listen, первый запрос на соединение и возвращает дескриптор нового (автоматически созданного) socket'а с теми же свойствами, что и socket, задаваемый аргументом s. Этот новый дескриптор необходимо использовать во всех последующих операциях обмена данными.
Кроме того после удачного завершения accept: 1. область памяти, указываемая аргументом addr, будет содержать структуру данных (для сетей TCP/IP это sockaddr_in), описывающую адрес socket'а программы-клиента, через который она сделала свой запрос на соединение; 2. целое число, на которое указывает аргумент p_addrlen, будет равно размеру этой структуры данных. Если очередь запросов на момент выполнения accept пуста, то программа переходит в состояние ожидания поступления запросов от клиентов на неопределенное время (хотя такое поведение accept можно и изменить). Признаком неудачного завершения accept служит отрицательное возвращенное значение (дескриптор socket'а отрицательным быть не может). Примечание. Системный вызов accept используется в программах-серверах, функционирующих только в режиме с установлением соединения Для получения данных от партнера по сетевому взаимодействию используется системный вызов recv, имеющий следующий вид
Public Declare Function recv Lib "wsock32.dll" _ (ByVal s As Long, buf As Any, ByVal buflen As Long, ByVal flags _ As Long) As Long Аргумент s задает дескриптор socket'а, через который принимаются данные. Аргумент buf указывает на область памяти, предназначенную для размещения принимаемых данных. Аргумент buflen задает длину (в байтах) этой области. Аргумент flags модифицирует исполнение системного вызова recv. При нулевом значении этого аргумента вызов recv полностью аналогичен системному вызову read. При успешном завершении send возвращает количество переданных из области, указанной аргументом buf, байт данных. Если канал данных, определяемый дескриптором s, оказывается "переполненным", то send переводит программу в состояние ожидания до момента его освобождения. Public Declare Function send Lib "wsock32.dll" _ (ByVal s As Long, buf As Any, ByVal buflen As Long, ByVal _ flags As Long) As Long
Для закрытия ранее созданного socket'а используется обычный системный вызов closesocket, применяемый в ОС UNIX для закрытия ранее открытых файлов и имеющий следующий вид Примечание: Все функции приема/передачи потоковых данных являются блокирующими. Т.е. в периоды ожиданий ваше приложение будет просто висеть. Поэтому рекомендуется создавать для этих комманд отдкльные потоки: Threads. Public Declare Function CreateThread Lib "kernel32" (lpThreadAttributes As Any, _ ByVal dwStackSize As Long, ByVal lpStartAddress As Long, _ lpParameter As Any, ByVal dwCreationFlags As Long, _ lpThreadID As Long) As Long Порядок байт на машинах PC отличается от порядка используемого в сетях, поэтому необходимы некоторые преобразования определенных данных, например номера порта, чтобы он был правильным при использовании функций библиотеки Winsock. Вот функция преобразования порядка байт: Public Declare Function htons _ Lib "ws2_32.dll" (ByVal hostshort As Integer) As Integer
Для преобразования строки с IP-адресом в формате десятичное с точкой в 32-разрядное двоичное число (с сетевым порядком байтов). Public Declare Function inet_addr _ Lib "wsock32" (ByVal cp As String) As Long Функция WSAAsyncSelect назначает сообщение, которое будет генерироваться при событиях на сокете Public Declare Function WSAAsyncSelect Lib "wsock32" (ByVal socket As Long, _ ByVal hwnd As Long, ByVal iMsg As Long, ByVal lEvent As_ Long) As Long socket идентификатор сокета, для которого требуется уведомление о произошедшем событии. iMsg сообщение, посылаемое по событию. lEvent битовая маска, определяющая комбинацию сетевых событий, в которых заинтересовано приложение. Public Const FD_ACCEPT = &H8 Возвращаемое значение В случае удачного завершения WSAAsyncSelect возвращает 0. В противном случае возвращается SOCKET_ERROR и устанавливается конкретный код ошибки, который может быть получен функцией WSAGetLastError.
Функция WSAAsyncSelect используется для того, чтобы указать Wsock32.dll на необходимость посылки сообщения окну hWnd всякий раз, когда она обнаруживает на сокете любое из сетевых событий, указанных параметром lEvent. Сообщение, которое должно быть послано, определяется параметром iMsg. Сокет, для которого требуется уведомление, идентифицируется параметром socket. Функция WSAAsyncSelect автоматически устанавливает сокет s в неблокируемый режим, независимо от значения lEvent. См. функцию ioctlsocket для получения информации о том, как установить неблокируемый сокет обратно в блокируемый режим. Public Declare Function closesocket _ Lib "wsock32.dll" (ByVal s As Long) As Long