Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабы Мартын 2(NPWSLab2U07).doc
Скачиваний:
9
Добавлен:
10.02.2016
Размер:
384 Кб
Скачать
☆

1.1.2. Створення й ініціалізація сокету

Сокет - це опис постачальника транспорту. В Win32 сокет відрізняється від опису файлу, а тому представлено окремим типом - SOCKET. Сокет створюється однієї із двох функцій:

SOCKET WSASocket (

int af,

int type,

int protocol,

LPWSAPROTOCOL_INFO lpProtocolInfo,

GROUP g,

DWORD dwFlags

)

SOCKET socket (

int af,

int type,

int protocol

);

Перший параметр - af, визначає сімейство адрес протоколу. Другий параметр - type, це тип сокету для даного протоколу. Він може приймати одне з наступних значень: SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET, SOCK_RAW і SOCK_RDM. Третій параметр - protocol, указує конкретний транспорт, якщо для даного сімейства адрес і типу сокету існує кілька записів. У табл. 1.2 перераховані значення, використовувані в полях сімейства адрес, типу сокету й протоколу для даного мережного транспорту.

Табл. 1.2. Параметри сокетів

Протокол

Сімейство адрес

Тип сокету

Протокол

1

2

3

4

5

Internet Protocol (IP)

AF_INET

TCP UDP Прості сокети

SOCK_STREAM SOCK_DGRAM SOCK_RAW

IPPROTO_IP IPPROTO_UDP IPPROTO_RAW 1PPROTO_ICMP

IPX/SPX

AF_NS

MSAFD nwln-kipx [IPX]

SOCK_DGRAM

NSPROTO_IPX

AF_IPX

MSAFD nwln- kspx [SPX] MSAFD nwln- kspx [SPX] [псевдопоток] MSAFD nwln- kspx [SPXII] MSAFD nwlnk spx [SPXII] -[псевдопоток]

SOCK_ SEQ- PACKET SOCK_STREAM SOCK_SEQ- PACKET SOCK_STREAM

NSPROTO_SPX NSPROTO_SPX NSPROTO_SPXII NSPROTO_SPXII

NetBIOS

AF_NETBIOS

Последова- тельні пакети

SOCK_SEQ- PACKET

Номер LANA

Дейтаграми

SOCK_DGRAM

Номер LANA

AppleTalk

AF_APPLE TALK

MSAFD Apple- Talk [ADSP]

SOCK_RDM

ATPROTO_ADSP

MSAFD Apple- Talk [ADSP] [псевдопоток]

SOCK_STREAM

ATPROTO_ADSP

MSAFD Apple- Talk [PAP]

SOCK_RDM

ATPROTO_PAP

MSAFD Apple- Talk [RTMP]

SOCK_DGRAM

DDPPROTO_ RTMP

MSAFD Apple- Talk [ZIP]

SOCK_DGRAM

DDPPROTO_ZIP

1

2

3

4

5

ATM

AF_ATM

MSAFD ATM AAL5

SOCK_RAW

ATMPROTO_AAL5

Native ATM (AAL5)

SOCK_RAW

ATMPROTO_AAL5

Infrared Sockets

AF_IRDA

MSAFD Irda [IrDA]

SOCK_STREAM

IRDA_PROTO_ SOCK_ STREAM

Початкові три параметри для створення сокету підрозділені на три рівні. Перший і найважливіший - сімейство адрес. Він указує використовуваний у цей час протокол і обмежує застосування другого й третього параметрів. Аналогічно, вибір сімейства адрес і типу сокету обмежує вибір протоколу.

Можна передати в параметрі protocol значення 0. У цьому випадку система вибирає постачальника транспорту, виходячи з інших двох параметрів - af і type. Перераховуючи запису каталогу для протоколів, варто перевірити значення поля dwProviderFlags зі структури WSAPROTOCOL_INFO. Якщо воно дорівнює PFL_ MATCHES_PROTOCOL_ZERO - це стандартний транспорт, застосовуваний, якщо в параметрі протоколу socket або WSASocket передане значення 0.

Перелічивши всі протоколи за допомогою WSAEnumProtocols, варто передати структуру WSAPROTOCOL_INFO у функцію WSASocket як параметр lpProtocolInfo. Потім треба вказати константу FROM_PROTOCOL_INFO у всіх параметрах (af, type і protocol) - для них будуть використатися значення з переданої структури WSAPROTOCOL_INFO.

У двох останніх прапорах з WSASocket параметр групи завжди дорівнює 0, тому що жодна з версій Winsock не підтримує групи сокетів, у параметрі dwFlags указують один або кілька наступних прапорів:

  • WSA_FLAG_OVERLAPPED;

  • WSA_FLAG_MULTIPOINT_C_ROOT;

  • WSA_FLAG_MULTIPOINT_C_LEAF;

  • WSA_FLAG_MULTIPOINT_D_ROOT;

  • WSA_FLAG_MULTIPOINT_D_LEAF.

Перший прапор - WSA_FLAG_OVERLAPPED, указує, що даний сокет допускає перекритий ввід-вивід. Якщо створюється сокет функцією socket, прапор WSA_FLAG_OVERLAPPED заданий за замовчуванням. Рекомендується завжди задавати цей прапор при використанні WSASocket. Останні чотири прапори ставляться до сокету багатоадресного віщання.

При створенні сокету функцією WSASocket можна передати виклику структуру WSAPROTOCOL_INFO, щоб визначити тип сокету, якому необхідно створити. Можна створювати типи сокетів, для яких немає запису в каталозі постачальників транспорту.

1.1.2.2. Видалення сокету

Для звільнення ресурсів додаток повинне закривати сокети, які йому більше не потрібні, викликаючи функцію closesocket:

int closesocket(SOCKET sock);

Нижче перераховані коди помилок для цієї функції:

Код помилки Опис

WSANOTINIT1ALISED Перед використанням функції closesocket необхідно викликати функцію WSAStartup

WSAENETDOWN Збій у мережі

WSAENOTSOCK Зазначений у параметрі дескриптор не є сокетом

WSAEINPROGRESS Виконується функція, що блокує, інтерфейсу Windows Sockets

WSAEINTR Робота функції була скасована за допомогою функції WSACancelBlockingCall

1.1.2.3. Параметри сокету

Перед використанням необхідно задати параметри сокету.

Для цього треба підготувати структуру типу 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_lNET, що відповідає типу адреси, прийнятому в Internet:

srv_address.sin_family = AF_INET;

Поле sin_port визначає номер порту, що буде використатися для передачі даних. Порт - це просто ідентифікатор програми, що виконує обмін у мережі. На одному вузлі може одночасно працювати кілька програм, що використають різні порти.

Особливістю поля sin_port є використання так званого мережного формату даних. Цей формат відрізняється від того, що прийнято в процесорах з архітектурою Intel, а саме молодші байти даних зберігаються по старших адресах пам'яті (big endian). Нагадаємо, що архітектура процесорів Intel має на увазі зберігання молодших байтів даних по молодших адресах (little endian).

Універсальний мережний формат даних зручний при організації глобальних мереж, тому що у вузлах такої мережі можуть використатися комп'ютери з різною архітектурою.

Для виконання перетворень зі звичайного формату в мережний і назад в інтерфейсі Windows Sockets передбачений спеціальний набір функцій. Зокрема, для заповнення поля sin_port потрібно використати функцію htons або WSAHtons, що виконують перетворення 16-розрядних даних з формату Intel у мережний формат.

u_short htons(u_short hostshort);

int WSAHtons(

SOCKET s,

u_short hostshort,

u_short FAR * lpnetshort

);

Нижче показано, як инициализируется поле 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_bl

#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", MВ_ОК);

return;

}

memcpy((char FAR *)&(dest_sin.sin_addr), phe->h_addr, phe->h_length);

У випадку помилки функція gethostbyname повертає null. При цьому причину помилки можна з'ясувати, перевіривши код повернення функції WSAGetLastError.

Якщо ж зазначений вузол знайдений у базі DNS або у файлі HOSTS, функція gethostbyname повертає покажчик на структуру hostent, описану нижче:

struct hogtent

{

char FAR * h_name; // ім'я вузла

char FAR *FAR *h_aliases; // список альтернативних імен

short h_addrtype; // тип адреси вузла

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_lenglh.

1.1.2.4. Прив'язка адреси до сокету

Після того як підготовлена структура SOCKADDR (записом у неї параметрів сокету, зокрема, адреси), варто виконати прив'язку адреси до сокету за допомогою функції bind:

int bind(SOCKET sock, const struct sockaddr FAR *addr, int namelen);

Параметр sock повинен містити дескриптор сокету, створеного функцією socket. У поле addr варто записати покажчик на підготовлену структуру SOCKADDR, а в поле namelen - розмір цієї структури.

У випадку помилки функція bind повертає значення SOCKET_ERROR. Подальший аналіз причин помилки варто виконувати за допомогою функції WSAGetLastError. Можливі коди помилок перераховані нижче:

Код помилки Опис

WSANOTINITIALISED Перед використанням функції необхідно викликати функцію WSAStanup

WSAENETDOWN Збій у мережі

WSAEADDRINUSE Зазначена адреса вже використається

WSAEFAULT Значення параметра namelen менше розміру структури sockaddr

WSAEINPROGRESS Виконується функція, що блокує, інтерфейсу Windows Sockets

WSAEAFNOSUPPORT Цей протокол не може працювати із зазначеним сімейством адрес

WSAEINVAL Сокет уже прив'язаний до адреси

WSAENOBUFS Установлено занадто багато з'єднань

WSAENOTSOCK Зазначений у параметрі дескриптор не є сокетом

Приклад виклику функції bind показаний нижче:

if(bind(srv_socket, (LPSOCKADDR)&srv_address, sizeof(srv_address)) == SOCKET_ERROR)

{

closesocket(srv_socket);

MessageBox(NULL, "bind Error", "Error", MB_OK);

return;

}