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

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 - код події, що відбулося в мережі.