- •57 Програмне забезпечення мереж еом. Частина 3. Відання 2. Програмування інтерфейсу «WinSocket»
- •Лабораторна робота № 1. Основні теоретичні положення (інтерфейс windows sockets)
- •1.1. Сокети, датаграми й канали зв'язку
- •1.1.1. Ініціалізація додатка й завершення його роботи
- •1.1.2. Створення й ініціалізація сокету
- •1.3. Індивідуальні завдання на роботу №1
- •Лабораторна робота № 2. Створення каналу зв'язку
- •2.1. Сторона сервера
- •2.3. Передача й прийом даних
- •2.2. Сторона клієнта
- •2.4. Додаток ServerSocket
- •2.5. Додаток ClientSocket
- •2.6. Індивідуальні завдання на роботу №2
- •Лабораторна робота № 3. Датаграммный протокол
- •3.1. Додаток ServerDSocket
- •3.2. Додаток ClientDSocket
- •3.3. Індивідуальні завдання на роботу №3
2.2. Сторона клієнта
Розглянемо процедуру установки каналу зв'язку з боку клієнта, використану в додатку CLIENT, вихідні тексти якого будуть наведені нижче.
Для установки з'єднання в додатку використаються функції InitLib і SetConnection:
BOOL CClientSocketDlg::InitLib()
{
int rc;
WSADATA WSAData;
char szTemp[128];
rc = WSAStartup(MAKEWORD(2, 2), &WSAData);
if(rc != 0)
{
MessageBox("WSAStartup Error", "Error", MB_OK);
return FALSE;
}
wsprintf(szTemp, "Server use %s %s",
WSAData.szDescription,WSAData.szSystemStatus);
SetWindowText( szTemp );
return TRUE;
}
void CClientSocketDlg::SetConnection( char *ipAddress, int port )
{
// Створюємо сокет
clt_socket = socket(AF_INET, SOCK_STREAM, 0);
if(clt_socket == INVALID_SOCKET)
{
return;
}
PHOSTENT phe;
SOCKADDR_IN destAddr;
// Установлюємо адресу ip номер порту
destAddr.sin_family = AF_INET;
// Визначаємо адреса вузла
phe = gethostbyname( ipAddress );
if(phe == NULL)
{
closesocket(clt_socket);
MessageBox("Gethostbyname Error", "Error", MB_OK);
return;
}
// Копіюємо адресу вузла
memcpy((char FAR *)&(destAddr.sin_addr), phe->h_addr, phe->h_length);
// Копіюємо номер порту
destAddr.sin_port = htons(port);
// Установлюємо з'єднання
if(connect(clt_socket, (PSOCKADDR)&destAddr, sizeof(destAddr)) < 0)
{
MessageBox("Connect Error", "Error", MB_OK);
return;
}
}
Спочатку за допомогою функції socket наведена функція створює сокет. Потім виконується заповнення адресною інформацією структури destaddr.
Для одержання адреси IP використалася функція gethostbyname із вказівкою їй імені
Після заповнення структури з адресною інформацією функція connect створює канал зв'язку із сервером.
2.3. Передача й прийом даних
Після того як канал створений, можна починати передачу даних. Для передачі даних за допомогою протоколу гарантованої доставки TCP можна скористатися функціями send і recv, які входять у програмний інтерфейс Windows Sockets.
Функція передачі даних send має чотири параметри - дескриптор сокету sock, на якому виконується передача, адреса буфера buf, що містить передане повідомлення, розмір цього буфера bufsize і прапори flags:
int send(SOCKET sock, const char FAR *buf, int bufsize, int flags);
У додатку CLIENT дані передаються серверу в такий спосіб:
void CClientSocketDlg::SendData( char *szBuf )
{
int len = send(clt_socket, szBuf, lstrlen(szBuf), 0);
}
Параметри функції recv, призначеної для прийому даних, аналогічні параметрам функції send:
int recv(SOCKET sock, char FAR *buf, int bufsize, int flags);
Функції recv і send повертають кількість відповідно прийнятих і переданих байт даних. Додаток, що приймає дані, повинне викликати функцію recv у циклі доти, поки не будуть прийняті всі передані дані. При цьому на один виклик функції send може доводитися кілька викликів функції recv.
У випадку помилки обидві ці функції повертають значення SOCKET_ERROR. Для аналізу причин виникнення помилки варто скористатися функцією WSAGetLastError.
Приведемо список кодів помилок, які можуть виникати при виклику команди send:
Код помилки Опис
WSANOTINITIALISED Перед використанням функції необхідно викликати функцію WSAStartup
WSAENETDOWN Збій у мережі
WSAEACCES Зазначена адреса є широкомовним (broadcast), однак перед викликом функції не був установлений відповідний прапор
WSAEINTR Робота функції була скасована за допомогою функції WSACancelBlockingCall
WSAEINPROGRESS Виконується функція, що блокує, інтерфейсу Windows Sockets
WSAEFAULT Параметр buf зазначений неправильно (він не вказує на адресний простір, що належить додатку)
WSAENETRESET Необхідно скинути з'єднання
WSAENOBUFS Виникло блокування буфера
WSAENOTCONN Сокет не приєднаний
WSAENOTSOCK Зазначений у параметрі дескриптор не є сокетом
WSAESHUTDOWN Сокет був закритий функцією shutdown
WSAEWOULDBLOCK Сокет відзначений як що не блокує, але запитана операція приведе до блокування
WSAEMSGSIZE Був використаний сокет типу SOCK_DGRAM (призначений для передачі датаграмм). При цьому розмір пакета даних перевищує максимально припустимий для даної реалізації інтерфейсу Windows Sockets
WSAEINVAL Сокет не був підключений функцією bind
WSAECONNABORTED Збій через занадто велику затримку або з іншої причини
WSAECONNRESET Скидання з'єднання вилученим вузлом
При виконанні функції recv можуть виникати наступні помилки:
Код помилки Опис
WSANOTINITIALISED Перед використанням функції необхідно викликати функцію WSAStartup
WSAENETDOWN Збій у мережі
WSAENOTCONN Сокет не приєднаний
WSAEINTR Робота функції була скасована за допомогою функції WSACancelBlockingCall
WSAEINPROGRESS Виконується функція, що блокує, інтерфейсу Windows Sockets
WSAENOTSOCK Зазначений у параметрі дескриптор не є сокетом
WSAESHUTDOWN Сокет був закритий функцією shutdown
WSAEWOULDBLOCK Сокет відзначений як що не блокує, але запитана операція приведе до блокування
WSAEMSGSIZE Розмір пакета даних перевищує розмір буфера, у результаті чого прийнятий пакет був обрізаний
WSAEINVAL Сокет не був підключений функцією bind
WSAECONNABORTED Збій через занадто велику затримку або з іншої причини
WSAECONNRESET Скидання з'єднання вилученим вузлом
Передача й прийом даних у циклі може привести до блокування роботи додатка. Якщо це неприйнятно, варто скористатися асинхронним розширенням інтерфейсу Windows Sockets.
Додаток SERVER демонструє асинхронний прийом даних. Після установки каналу зв'язку воно викликає функцію WSAAsyncSelect, указуючи їй як останній параметр комбінацію констант FD_READ і FD_CLOSE. При цьому функція головного вікна додатка буде одержувати повідомлення WSA_NETEVENT у той момент, коли читання даних не викличе блокування додатка:
#define WSA_NETEVENT (WM_USER +2)
rc = WSAAsyncSelect(srv_socket, hWnd, WSA_NETEVENT, FD_READ | FD_CLOSE);
При необхідності виконання асинхронної посилки даних можна вказати функції WSAAsyncSelect ще й параметр FD_WRITE.
Якщо функція WSAAsyncSelect виконалася успішно, вона повертає нульове значення, при помилці - значення SOCKET_ERROR.
Залежно від значення останнього параметра можуть виникати різні коди помилки, які можна одержати за допомогою функції WSAGetLastError. Наступні помилки можуть виникнути при будь-якім значенні параметра:
Код помилки Опис
WSANOTINITLALISED Перед використанням функції необхідно викликати функцію WSAStanup
WSAENETDOWN Збій у мережі
WSAEINVAL Сокет не був підключений функцією bind
WSAEINPROGRESS Виконується функція, що блокує, інтерфейсу Windows Sockets
Дополнительный код помилки можна одержати з параметра lParam за допомогою макрокоманди WSAGETSELECTERROR.
При використанні параметра FD_CONNECT можлива поява наступних помилок:
Код помилки Опис
WSAEADDRINUSE Зазначена адреса вже використається
WSAEADDRNOTAVAIL Зазначена адреса недоступна
WSAEAFNOSUPPORT Для даного сокету не можна використати зазначене сімейство адрес
WSAECONNREFUSED Спроба встановлення каналу зв'язки була відкинута
WSAEDESTADDRREQ Необхідно вказати адресу одержувача пакета
WSAEFAULT Неправильно зазначений параметр namelen
WSAEINVAL Сокет уже підключений до адреси
WSAEISCONN Сокет уже приєднаний
WSAEMFILE Більше немає доступних дескрипторів
WSAENETUNREACH З даного вузла й тепер неможливо одержати доступ до мережі
WSAENOBUFS Немає місця для розміщення буфера
WSAENOTCONN Сокет не підключений
WSAENOTSOCK Зазначений дескриптор файлу, а не сокету
WSAETIMEDOUT При спробі встановлення каналу зв'язку виникла затримка в часі
Якщо використається параметр FD_CLOSE, може виникнути одна з наступних помилок:
Код помилки Опис
WSAENETDOWN Збій у мережі
WSAECONNRESET Скидання з'єднання вилученим вузлом
WSAECONNABORTED Збій через занадто велику затримку або з іншої причини
У тому випадку, коли зазначені параметри FD_READ, FD_WRITE, FD_OOB або FD_ACCEPT, може виникнути помилка з кодом WSAENETDOWN.
Оброблювач повідомлення WSA_NETEVENT повинен виконати аналіз причини, по якій він був викликаний, тому що за один виклик функції WSAAsyncSelect можна задати кілька подій, що викликають генерацію повідомлення. Цей аналіз проводиться, наприклад, у такий спосіб:
LRESULT CServerSocketDlg::OnWSANetEvent(WPARAM wParam, LPARAM lParam)
{
char szTemp[256];
int rc;
// Якщо на сокете виконується передача даних,
// приймаємо й відображаємо ці дані у вигляді
// текстового рядка
if(WSAGETSELECTEVENT(lParam) == FD_READ)
{
rc = recv((SOCKET)wParam, szTemp, 256, 0);
if(rc)
{
szTemp[rc] = '\0';
ShowData( szTemp );
}
}
// Якщо з'єднання завершене, виводимо про це повідомлення
else if(WSAGETSELECTEVENT(lParam) == FD_CLOSE)
{
MessageBox("Connection closed", "Server", MB_OK);
closesocket( clientSocket );
}
return 0L;
}
Параметр wParam містить дескриптор сокету, на якому виконується передача даних, а параметр lParam - код події, що відбулося в мережі.
