Глобальные сети компьютеров. Практическое введение в Internet, E-Mail, FTP, WWW и HTML, программирование для Windows Sockets© Александр Фролов, Григорий ФроловТом 23, М.: Диалог-МИФИ, 1993, 283 стр. 5.3. Создание и инициализация сокетаПосле инициализации интерфейса Windows Sockets ваше приложение должно создать один или несколько сокетов, которые будут использованы для передачи данных. Создание сокетаСокет создается с помощью функции socket , имеющей следующий прототип: SOCKET socket (int af, int type, int protocol); Параметр af определяет формат адреса. Для этого параметра вы должны указывать значение AF_INET , что соответствует формату адреса, принятому в Internet. Параметры type и protocol определяют, сооветственно, тип сокета и протокол, который будет использован для данного сокета. Можно указывать сокеты следующих двух типов:
Что же касается параметра protocol, то вы можете указать для него нулевое значение. В случае успеха функция socket возвращает
дескриптор, который нужно использовать для
выполнения всех операций над данным сокетом.
Если же произошла ошибка, эта функция возвращает
значение INVALID_SOCKET . Для анализа
причины ошибки вы должны вызвать функцию
WSAGetLastError , которая в данном случае может вернуть
один из следующих кодов ошибки:
Ниже мы привели фрагмент кода, в котором создается сокет для передачи данных с использованием протокола TCP: srv_socket = socket(AF_INET , SOCK_STREAM, 0); if(srv_socket == INVALID_SOCKET) { MessageBox(NULL, "socket Error", "Error", MB_OK); return; } Удаление сокетаДля освобождения ресурсов приложение должно закрывать сокеты, которые ему больше не нужны, вызывая функцию closesocket : int closesocket (SOCKET sock); Ниже мы перечислили коды ошибок для этой
функции :
Параметры сокетаПеред использованием вы должны задать параметры сокета. Для этого вы должны подготовить структуру типа sockaddr , определение которой показано ниже: struct sockaddr { u_short sa_family; char sa_data[14]; }; typedef struct sockaddr SOCKADDR ; typedef struct sockaddr *PSOCKADDR ; typedef struct sockaddr FAR *LPSOCKADDR ; Для работы с адресами в формате Internet используется другой вариант этой структуры, в котором детализируется формат поля sa_data: struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; typedef struct sockaddr_in SOCKADDR _IN; typedef struct sockaddr_in *PSOCKADDR _IN; typedef struct sockaddr_in FAR *LPSOCKADDR _IN; Поле sin_family определяет тип адреса. Вы должны записать в это поле значение AF_INET , которое соответствует типу адреса, принятому в Internet: srv_address.sin_family = AF_INET ; Поле sin_port определяет номер порта, который будет использоваться для передачи данных. Порт - это просто идентификатор программы, выполняющей обмен на сети. На одном узле может одновременно работать несколько программ, использующих разные порты. Особенностью поля sin_port является использование так называемого сетевого формата данных. Этот формат отличается от того, что принят в процессорах с архитектурой Intel, а именно, младшие байты данных хранятся по старшим адресам памяти. Напомним, что архитектура процессоров Intel подразумевает хранение старщих байтов данных по младшим адресам. Универсальный сетевой формат данных удобен при организации глобальных сетей, так как в узлах такой сети могут использоваться компьютеры с различной архитектурой. Для выполнения преобразований из обычного формат в сетевой и обратно в интерфейсе Windows Sockets предусмотрен специальный набор функций. В частности, для заполнения поля sin_port нужно использовать функцию htons, выполняющую преобразование 16-разрядных данных из формата Intel в сетевой формат. Ниже мы показали, как инициализируется поле sin_port в приложении SERVER, описанном далее: #define SERV_PORT 5000 srv_address.sin_port = htons(SERV_PORT); Вернемся снова к структуре sockaddr_in . Поле sin_addr этой структуры представляет собой структуру in_addr: struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; }; #define s_addr S_un.S_addr #define s_host S_un.S_un_b.s_b2 #define s_net S_un.S_un_b.s_b1 #define s_imp S_un.S_un_w.s_w2 #define s_impno S_un.S_un_b.s_b4 #define s_lh S_un.S_un_b.s_b3 При инициализации сокета в этой структуре вы должны указать адрес IP, с которым будет работать данный сокет. Если сокет будет работать с любым адресом (например, вы создаете сервер, который будет доступен из узлов с любым адресом), адрес для сокета можно указать следующим образом: srv_address.sin_addr .s_addr = INADDR_ANY ; В том случае, если сокет будет работать с определенным адресом IP (например, вы создаете приложение-клиент, которое будет обращаться к серверу с конкретным адресом IP), в указанную структуру необходимо записать реальный адрес. Датаграммный протокол UDP позволяет посылать пакеты данных одновременно всем рабочим станциям в широковещательном режиме. Для этого вы должны указать адрес как INADDR_BROADCAST. Если вам известен адрес в виде четырех десятичных чисел, разделенных точкой (именно так его вводит пользователь), то вы можете заполнить поле адреса при помощи функции inet_addr : dest_sin.sin_addr .s_addr = inet_addr ("200.200.200.201"); В случае ошибки функция возвращает значение INADDR_NONE , что можно использовать для проверки. Обратное преобразование адреса IP в текстовую строку можно при необходимости легко выполнить с помощью функции inet_ntoa , имеющей следующий прототип: char FAR * inet_ntoa (struct in_addr in); При ошибке эта функция возвращает значение NULL. Однако чаще всего пользователь работает с доменными именами, используя сервер DNS или файл HOSTS . В этом случае вначале вы должны воспользоваться функцией gethostbyname , возвращающей адрес IP, а затем записать полученный адрес в структуру sin_addr : PHOSTENT phe; phe = gethostbyname ("ftp.microsoft.com"); if(phe == NULL) { closesocket (srv_socket); MessageBox(NULL, "gethostbyname Error", "Error", MB_OK); return; } memcpy((char FAR *)&(dest_sin.sin_addr ), phe->h_addr , phe->h_length); В случае ошибки функция gethostbyname возвращает NULL. При этом причину ошибки можно выяснить, проверив код возврата функции WSAGetLastError . Если же указанный узел найден в базе DNS или в файле HOSTS , функция gethostbyname возвращает указатель на структуру hostent , описанную ниже: struct hostent { char FAR * h_name; // имя узла char FAR * FAR * h_aliases; // список альтернативных имен short h_addr type; // тип адреса узла short h_length; // длина адреса char FAR * FAR * h_addr _list; // список адресов #define h_addr h_addr_list[0] // адрес }; typedef struct hostent *PHOSTENT ; typedef struct hostent FAR *LPHOSTENT ; Искомый адрес находится в первом элемента списка h_addr _list[0], на который можно также ссылаться при помощи h_addr. Длина поля адреса находится в поле h_length. Привязка адреса к сокетуПосле того как вы подготовили структуру SOCKADDR , записав в нее параметры сокета (в частности, адрес), следует выполнить привязку адреса к сокету при помощи функции bind : int bind ( SOCKET sock, const struct sockaddr FAR * addr, int namelen); Параметр sock должен содержать дескриптор сокета, созданного функцией socket . В поле addr следует записать указатель на подготовленную структуру SOCKADDR , а в поле namelen - размер этой структуры. В случае ошибки функция bind возвращает значение
SOCKET_ERROR . Дальнейший анализ причин ошибки следует
выполнять при помощи функции WSAGetLastError . Возможные
коды ошибок перечислены ниже:
Пример вызова функции bind показан ниже: if(bind (srv_socket , (LPSOCKADDR )&srv_address, sizeof(srv_address)) == SOCKET_ERROR ) { closesocket (srv_socket); MessageBox(NULL, "bind Error", "Error", MB_OK); return; } |