Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Библиотека функций Windows Socket



Для реализации протоколов обмена информацией существует библиотека функций Windows Socket (сокращенно winsock). В языках программирования С и С++ для использования библиотеки следует подключить в проект библиотечный файл WS2_32.Lib и использовать include оператор: #include " winsock.h"

 

 

Рассмотрим набор функций библиотеки winsock. Функция WSAStartup предназначена для инициализация библиотеки Windows Socket. Она имеет два параметра. Первый параметр – версия библиотеки, она может быть 0x0101  или 0x0200. Второй параметр – указатель на структуру WSADATA, в которую будут записаны различные параметры библиотеки. В случае успеха функция возвращает нулевое значение, иначе код ошибки. В качестве примера использования функции можно привести следующий текст:

WSADATA WSAData;

int rc = WSAStartup(MAKEWORD(2, 0), & WSAData);

if (rc! = 0) {

// Ошибка инициализации WSAStartup

} // if

Функция WSA Cleanup предназначена для освобождения библиотеки. Данная функция не имеет параметров. В случае успеха функция возвращает нулевое значение, иначе код ошибки.

Функция WSA GetLastError возвращает код ошибки последней операции. Данная функция не имеет параметров.

Функция socket предназначена для создание сокета – логической единицы, для приема и отправки сообщений. Она имеет три параметра. Первый параметр – тип адреса, для протоколов UDP и TCP он должен принимать значение AF_INET. Второй параметр – тип передачи данных, для протокола UDP он должен быть SOCK_DGRAM, а для протокола TCP – SOCK_STREAM. Третий параметр – протокол, обычно он определяется первыми двумя параметрами и может принимать нулевое значение. В случае успеха функция возвращает целое полжительное значение, имеющее тип SOCKET, это значение нужно использовать в последующих функциях. В случае ошибки функция возвращает значение INVALID_SOCKET. В качестве примера использования функции можно привести следующий текст:

SOCKET UDPSocket = socket(AF_INET, SOCK_DGRAM, 0);

if (UDPSocket == INVALID_SOCKET) {

// Протокол UDP не установлен.

} // if

Функция closesocket предназначена для удаление сокета. Она имеем один параметр – идентификатор сокета, который получен от функции socket.

Функция bind предназначена для привязки адреса к сокету. Она имеет 3 параметра. Первый параметр – идентификатор сокета. Второй параметр – привязываемый адрес. Для протоколов UDP и TCP он должен быть в виде структуры:

struct sockaddr_in {

  short sin_family;        // Должно быть AF_INET

  u_short sin_port;        // Задается программистом

   struct in_addr sin_addr; // IP адрес с которым будем работать

  char sin_zero[8];        // должно быть нулевым

};

где struct in_addr sin структура следующего вида:

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;

}.

Третий параметр – размер адресной структуры. Рекомендуется использовать функцию sizeof(struct sockaddr_in).  В случае успешного выполнения, функция bind возвращает ноль, в противном случае – SOCKET_ERROR. В качестве примера использования функции можно привести следующий текст:

sockaddr_in OurAddress;

memset(& OurAddress, 0, sizeof(OurAddress));

OurAddress.sin_family = AF_INET;

OurAddress.sin_port = 2001;

rc =bind(UDPSocket, (LPSOCKADDR)& OurAddress, sizeof(sockaddr_in));

if (rc == SOCKET_ERROR) {

// Ошибка в функции bind

} // if

Функция inet_addr преобразует IP-адреса из символьного вида в числовой, например так inet_addr(" 200.200.200.201" ). Возвращаемое значение – целое беззнаковое число содержащее IP-адрес.

Функция gethostbyname позволяет по имени компьютера или доменному имени определить IP-адрес. В качестве параметра используется имя компьютера или доменное имя. Функция возвращает указатель на структуру HOSTENT, поле h_addr_list которой содержит массив IP-адресов, поле h_addr является нулевым элементом массива h_addr_list и содержит первый адрес. Если IP-адресов нет, то функция возвращает NULL. В качестве примера использования функции можно привести следующий текст:

HOSTENT *ph;

sockaddr_in dest_sin;

ph = gethostbyname(имя_компьютера);

if (ph! = NULL) {

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

}

Функция gethostname применяется для определения имени своего компьютера. Она имеет 2 параметра. Первый параметр – указатель на символьный массив, в который будет занесено имя компьютера. Второй параметр – целое число, размер массива. В качестве примера использования функции можно привести следующий текст:

char OurCompName[100];

int gethostname(OurCompName, sizeof(OurCompName));

Функция sendto применяется для передачи данных без канала, по протоколу UDP. Она имеет 6 параметров. Первый параметр – идентификатор сокета. Второй параметр – указатель на область передаваемых данных. Тип второго параметра – char *, однако функция может передавать не только символьные, но и любые данные, однако указатель должен быть преобразован к char *. Третий параметр – размер передаваемых данных, максимальный размер для этой функции и для протокола UDP в целом 64К. Четвертый параметр – флаг, он может быть нулевым. Пятый параметр – указатель на структуру sockaddr_in, содержащую адрес получателя. Шестой параметр – размер структуры. В случае успеха, функция возвращает количество отправленных байт, в случае ошибки SOCKET_ERROR. В качестве примера использования функции можно привести следующий текст:

#define SERVER_PORT 2001

sockaddr_in CallAddress;

memset(& CallAddress, 0, sizeof(CallAddress));

CallAddress.sin_family = AF_INET;

CallAddress.sin_port = SERVER_PORT;

CallAddress.sin_addr.s_addr = inet_addr(" 192.168.0.112" );

char *Buf = " Проба пера";

rc = sendto(UDPSocket, Buf, strlen(Buf)+1, 0, (LPSOCKADDR)

& CallAddress, sizeof(CallAddress));

if (rc == SOCKET_ERROR) {

rc = WSAGetLastError();

// Ошибка sendto с кодом rc

} // if

Для приема данных без канала по протоколу UDP используется функция recvfrom. Также как и функция sendto, она имеет 6 параметров. Первый параметр – идентификатор сокета. Второй параметр – указатель на область, в которую будут записаны полученные данные. Третий параметр – размер области для получения данных. Четвертый параметр – флаг, он может быть нулевым. Пятый параметр – указатель на структуру sockaddr_in, содержащую будет записан адрес отправителя. Шестой параметр – указатель на целочисленную беззнаковую переменную, куда заносится размер структуры. В случае успеха, функция возвращает количество полученных байт, в случае ошибки SOCKET_ERROR. В качестве примера использования функции можно привести следующий текст:

char Buf[64*1024+1];

sockaddr_in CallAddress;

unsigned int Len = sizeof(CallAddress);

int rc = recvfrom(UDPSocket, Buf, sizeof(Buf)-1, 0,  (LPSOCKADDR)

& CallAddress, & Len);

if (rc == SOCKET_ERROR) {

rc = WSAGetLastError();

// Ошибка recvfrom с кодом rc

} // if

Сложность использования функции recvfrom заключается в том, что необходимо знать момент вызова этой функции, т.к. ее нужно вызвать, тогда, когда пришли данные. Для определения этого момента нужно использовать функцию WSAAsyncSelect. Эта функция нужна для преобразования сетевых сообщений в Windows сообщения. Она имеет 4 параметра. Первый параметр – идентификатор сокета. Второй параметр –идентификатор окна, в приложениях Windows Form среды разработки Microsoft Visual Studio этот идентификатор можно получить из свойства Handle формы, преобразовав его к типу Int32, а затем к типу HWND. Третий параметр – идентификатор Windows сообщения, например WSA_NETEVENT, он должен быть равным числу, большему WM_USER, например WM_USER+2. Четвертые параметр – идентификатор сетевого сообщения, сообщение  о получении данных равно FD_READ. В качестве примера использования функции можно привести следующий текст:

#define WSA_NETEVENT (WM_USER+2)

WSAAsyncSelect(UDPSocket, (HWND)(this-> Handle.ToInt32()),

WSA_NETEVENT, FD_READ);

При использовании протокола UDP функцию WSAAsyncSelect нужно вызвать после вызова функции bind. После вызова этой функции в момент прихода сетевого сообщения будет генерироваться Windows сообщение WSA_NETEVENT с lParam равным FD_READ. Следует перехватить и обработать это сообщение, для этого нужно переопределить функцию обработки сообщений WndProc. Она может быть такой:

protected: virtual void WndProc (Message% m) override

{

int rc, l=sizeof(CallAddress);

char Buf[64*1024+1];

if (m.Msg == WSA_NETEVENT) {

         if (m.LParam.ToInt32() == FD_READ) {

                  rc = recvfrom((SOCKET)m.WParam.ToInt32(), Buf,

                            sizeof(Buf)-1, 0, (PSOCKADDR)& CallAddress, & l);

                  if (rc == SOCKET_ERROR) {

                  rc = WSAGetLastError();

                       // Ошибка recvfrom с кодом rc

                      return;

                  } // if

                  if (rc > = 1) {

                            // Получены данные в кол-ве rc байт, они находятся

                            // в массиве Buf

                  } // if

         } // if

} // if

Form:: WndProc( m );

} // WndProc

Рассмотренные выше функции полностью позволяют работать с протоколом UDP. Для работы с протоколом TCP требуется создать канал связи и по нему передавать и принимать данные. Рассмотрим необходимые для этого функции.

Функция listen устанавливает сокет в режим ожидания подключения. Она имеет 2 параметра. Первый параметр – идентификатор сокета. Второй параметр – максимальный размер очереди на подключение, рекомендуемое значение 5. В качестве примера использования функции можно привести следующий текст:

listen(TCPSocket, 5);

Функция connect инициирует создание канала связи. Она имеет 3 параметра. Первый параметр – идентификатор сокета. Второй параметр – указатель на структуру sockaddr_in, содержащую адрес точки подключения с которой устанавливаем канал. Третий параметр – размер структуры. В случае успеха, функция возвращает нулевое значение, в случае ошибки SOCKET_ERROR. В качестве примера использования функции можно привести следующий текст:

rc=connect(TCPSocket, (PSOCKADDR)& CallAddress, sizeof(CallAddress));

if (rc == SOCKET_ERROR) {

rc = WSAGetLastError();

// Ошибка connect с кодом rc

} // if

Функция accept выполняет отклик на функцию connect на другой стороне канала. Она имеет 3 параметра. Первый параметр – идентификатор сокета. Второй параметр – указатель на структуру sockaddr_in, в которую будет записан адрес подключаемого. Третий параметр – указатель на целочисленную беззнаковую переменную, куда заносится размер структуры. В случае успеха, функция создает виртуальный сокет и возвращает его идентификатор, который следует использовать в последующих функциях, в случае ошибки INVALID_SOCKET. Для определения момента вызова функции accept следует использовать функцию WSAAsyncSelect с параметрами: WSAAsyncSelect(TCPSocket, (HWND)(this-> Handle.ToInt32( )), WSA_NETACCEPT, FD_ACCEPT). Тогда в момент подключения будет генерироваться Windows сообщение WSA_ ACCEPT с lParam равным FD_ ACCEPT. Следует перехватить и обработать это сообщение, для этого нужно переопределить функцию обработки сообщений WndProc. Она может быть такой:

protected: virtual void WndProc (Message% m) override

{

int rc, l=sizeof(CallAddress);

char Buf[64*1024+1];

if (m.Msg == WSA_NETACCEPT) {

    if (m.LParam.ToInt32() == FD_ACCEPT) {

             TmpSocket = accept((SOCKET)m.WParam.ToInt32(),

                            (PSOCKADDR)& CallAddress, (int *)& l);

                  if (TmpSocket == INVALID_SOCKET) {

                       rc = WSAGetLastError();

                       // Ошибка accept с кодом rc

            return;

                  } // if

                  // Канал создан

         } // if

} // if

Form:: WndProc( m );

} // WndProc

Для передачи данных по каналу используется функция send. Она имеет 4 параметра, которые полностью совпадают с первыми четырьмя параметрами функции sendto.

Для приема данных по каналу используется функция recv. Она имеет 4 параметра, которые полностью совпадают с первыми четырьмя параметрами функции recvfrom. Так же, как и для функции recvfrom, нужно знать момент вызова этой функции. Для этого следует воспользоваться функцией WSAAsyncSelect.

Пример программ, использующих протокол UDP

В качестве примера рассмотрим две программы, обменивающиеся сообщениями с использованием протокола UDP. Программу, инициирующую передачу данных, назовем клиентом, а программу, ожидающую соединения, сервером. Дизайн программ приведен на рис. 2.1 и 2.2.

Рис. 2.1. Дизайн программы клиент

 

Рис. 2.2. Дизайн программы сервер

 

 

Рассмотрим основные действия программ. При старте программ (событие Load формы) выполняются следующие действия:

̶ инициализация библиотеки WindowsSocket с помощью функции WSAStartup,

̶ создание сокета (socket),

̶ привязка адреса к сокету (bind),

̶ инициализация преобразования сетевых сообщений в Windows сообщения (WSAAsyngSelect, )

̶ определение IP-адреса и имени компьютера (gethostname и gethostbyname).

При завершении программ (событие FormClosed):

̶ закрытие сокета (closesocket),

̶ освобождение библиотеки WindowsSocket (WSACleanup).

При нажатии кнопки «Отправить» программы «Клиент»:

̶ преобразование содержимого полей ввода из типа String в массив типа char,

̶ если заполнено поле IP-адрес, то преобразуем адрес в 4-х байтное число (inet_addr),

̶ если поле IP-адрес не заполнено, но заполнено поле имя компьютера, то определим адрес по имени компьютера (gethostbyname),

̶ отправка сообщения (sendto).

При нажатии кнопки «Отправить» программы «Сервер»:

̶ преобразование содержимого поля ввода из типа String в массив типа char,

̶ отправка сообщения клиенту, который последним отправил сообщение серверу (sendto).

Для приема сообщений в программах переопределена функция обработки Windows сообщений. Если код сообщения равен WSA_NETEVENT, а параметр сообщения FD_READ, то получаем текст пришедшего сообщения с помощью функции recvfrom. В конце переопределенной функции вызывается старая функция обработки сообщений Windows.

После вызова функции проверяется код возврата, если он окажется ошибочным, то выдается соответствующее сообщение. Текст программы «Клиент» приведен в приложении 1, «Сервер» в приложении 2.

Пример программ, использующих протокол TCP

Аналогично предыдущему примеру, данный пример состоит из двух программ «Клиент» и «Сервер». Дизайн программы «Клиент» приведен на рис. 2.3, дизайн программы «Сервер» полностью совпадает с дизайном программы, использующим протокол UDP и приведенным ранее на рис. 2.2.

Рис. 2.3. Дизайн программы клиент, использующей протокол ТСР

Рассмотрим основные действия программ. При старте программ «Клиент» (событие Load формы) выполняются следующие действия:

̶ инициализация библиотеки WindowsSocket с помощью функции WSAStartup,

̶ создание сокета (socket),

̶ привязка адреса к сокету (bind),

̶ определение IP-адреса и имени компьютера (gethostname и gethostbyname).

При старте программ «Сервер»:

̶ инициализация библиотеки WindowsSocket с помощью функции WSAStartup,

̶ создание сокета (socket),

̶ привязка адреса к сокету (bind),

̶ инициализация преобразования сетевых сообщений в Windows сообщения (WSAAsyngSelect),

̶ Установка сокета в режим ожидания подключения (listen),

̶ определение IP-адреса и имени компьютера (gethostname и gethostbyname).

 

 

При завершении программ (событие FormClosed):

̶ закрытие сокета (closesocket),

̶ освобождение библиотеки WindowsSocket (WSACleanup).

При нажатии кнопки «Подключиться» программы «Клиент»:

̶ преобразование содержимого полей ввода из типа String в массив типа char,

̶ если заполнено поле IP-адрес, то преобразуем адрес в 4-х байтное число (inet_addr),

̶ если поле IP-адрес не заполнено, но заполнено поле имя компьютера, то определим адрес по имени компьютера (gethostbyname),

̶ подключение к серверу (connect),

̶ инициализация преобразования сетевых сообщений в Windows сообщения (WSAAsyngSelect).

̶ При нажатии кнопки «Отправить» обеих программ:

̶ преобразование содержимого поля ввода из типа String в массив типа char.

̶ отправка сообщения (send).

При нажатии кнопки «Отключиться» программы «Клиент»: отключение от сервера (shutdown).

Для определения момента подключения или отключения и для приема сообщений в программах переопределена функция обработки Windows сообщений. Если код сообщения равен WSA_ACCEPT, а параметр сообщения FD_ACCEPT, то это означает, что к серверу пытается подключиться клиент. В этом случае для создание канала вызываем функцию accept, в качестве кода возврата будет указан виртуальный сокет, все последующие операции производятся именно с этим сокетом. В качестве первой операции выполняется инициализация преобразования сетевых сообщений в Windows сообщения (WSAAsyngSelect). Если код сообщения равен WSA_NETEVENT, а параметр сообщения FD_READ, то получаем текст пришедшего сообщения с помощью функции recv. В конце переопределенной функции вызывается старая функция обработки сообщений Windows.

После вызова функции проверяется код возврата, если он окажется ошибочным, то выдается соответствующее сообщение. Текст программы «Клиент», использующей протокол ТСР, приведен в приложении 3, «Сервер» в приложении 4.

 

2.5. Контрольные вопросы.

1. Для чего используется функция WSAStartup.

2. Для чего используется функция WSACleanup.

3. Для чего используется функция WSAGetLastError.

4. Для чего используется функция socket.

5. Для чего используется функция bind.

6. Для чего используется функция gethostname.

7. Для чего используется функция gethostbyname.

8. Для чего используется функция inet_addr.

9. Для чего используется функция sendto.

10. Для чего используется функция recvfrom.

11. Для чего используется функция listen.

12. Для чего используется функция accept.

13. Для чего используется функция connect.

14. Для чего используется функция send.

15. Для чего используется функция recv.

16. Для чего используется функция WSAAsyncSelect.

17. С помощью какой функции происходит инициализация библиотеки Windows Socket.

18. С помощью какой функции можно получить код ошибки последней операции.

19. С помощью какой функции создается сокет.

20. С помощью какой функции осуществляется привязка адреса к сокету.

21. С помощью какой функции осуществляется определение имени собственного компьтера.

22. С помощью какой функции осуществляется определение IP-адреса компьютера по доменному имени.

23. С помощью какой функции осуществляется передача данных без канала с использованием сокетов.

24. С помощью какой функции осуществляется прием данных без канала с использованием сокетов.

25. С помощью какой функции создается канал со стороны клиента с использованием сокетов.

26. С помощью какой функции создается канал со стороны сервера с использованием сокетов.

27. С помощью какой функции осуществляется передача данных с каналом с использованием сокетов.

28. С помощью какой функции осуществляется прием данных с каналом с использованием сокетов.


Поделиться:



Последнее изменение этой страницы: 2020-02-16; Просмотров: 142; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.065 с.)
Главная | Случайная страница | Обратная связь