Глобальные сети компьютеров. Практическое введение в Internet

Приложение CLIENTD


Исходные тексты приложения CLIENTD, предназначенного для совместной работы с приложением SERVERD, представлены в листинге 5.8. Так как это приложение очень похоже на приложение CLIENT, мы опишем только отличия.

Листинг 5.8. Файл clientd/clientd.c

#include <windows.h> #include <windowsx.h> #include <winsock.h> #include <commctrl.h> #include "resource.h"

// ----------------------------------------------------- // Описание функций // -----------------------------------------------------

// Функция главного окна LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Функция для обработки сообщения WM_CREATE BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct);

// Функция для обработки сообщения WM_DESTROY void WndProc_OnDestroy(HWND hWnd);

// Функция для обработки сообщения WM_COMMAND void WndProc_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify);

// Функция для обработки сообщения WM_SIZE void WndProc_OnSize(HWND hWnd, UINT state, int cx, int cy);

// Установка соединения void SetConnection(HWND hWnd);

// Передача сообщения void SendMsg(HWND hWnd);

// Порт сервера #define SERV_PORT 5000

#define IDS_STATUSBAR 802



// ----------------------------------------------------- // Глобальные переменные // -----------------------------------------------------

// Идентификатор приложения HINSTANCE hInst;

// Название приложения char szAppName[] = "WClientUDP ";

// Заголовок главного окна приложения char szAppTitle[] = "Windows Socket UDP Client Demo";

// Идентификатор органа управления Statusbar HWND hwndSb;

// Сокет клиента SOCKET srv_socket ;

// Адрес сервера SOCKADDR _IN dest_sin;

// ----------------------------------------------------- // Функция WinMain // -----------------------------------------------------

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; HWND hWnd; MSG msg;

hInst = hInstance;


// Преверяем, не было ли это приложение запущено ранее hWnd = FindWindow(szAppName, NULL); if(hWnd) { // Если окно приложения было свернуто в пиктограмму, // восстанавливаем его if(IsIconic(hWnd)) ShowWindow(hWnd, SW_RESTORE);

// Выдвигаем окно приложения на передний план SetForegroundWindow(hWnd); return FALSE; }

// Регистрируем класс окна memset(&wc, 0, sizeof(wc)); wc.cbSize = sizeof(WNDCLASSEX); wc.hIconSm = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICON_SM), IMAGE_ICON, 16, 16, 0); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0);

wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1); wc.lpszClassName = szAppName;

// Вызываем функцию RegisterClassEx, которая выполняет // регистрацию окна if(!RegisterClassEx(&wc)) if(!RegisterClass((LPWNDCLASS)&wc.style)) return FALSE;

InitCommonControls();

// Создаем главное окно приложения hWnd = CreateWindow(szAppName, szAppTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL); if(!hWnd) return(FALSE);

// Отображаем окно ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);

// Запускаем цикл обработки сообщений while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }

// ----------------------------------------------------- // Функция WndProc // -----------------------------------------------------

LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hWnd, WM_CREATE , WndProc_OnCreate); HANDLE_MSG(hWnd, WM_COMMAND , WndProc_OnCommand); HANDLE_MSG(hWnd, WM_SIZE , WndProc_OnSize); HANDLE_MSG(hWnd, WM_DESTROY , WndProc_OnDestroy);

default: return(DefWindowProc(hWnd, msg, wParam, lParam)); } }

// ----------------------------------------------------- // Функция WndProc_OnCreate // -----------------------------------------------------



BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { int rc; WSADATA WSAData; char szTemp[128];

// Инициализация и проверка версии Windows Sockets rc = WSAStartup (MAKEWORD(1, 1), &WSAData); if(rc != 0) { MessageBox(NULL, "WSAStartup Error", "Error", MB_OK); return FALSE; }

// Отображаем описание и версию системы Windows Sockets // в окне органа управления Statusbar wsprintf(szTemp, "Server use %s %s", WSAData.szDescription,WSAData.szSystemStatus);

hwndSb = CreateStatusWindow(WS_CHILD | WS_VISIBLE | WS_BORDER | SBARS_SIZEGRIP, szTemp, hWnd, IDS_STATUSBAR);

return TRUE; }

// ----------------------------------------------------- // Функция WndProc_OnDestroy // -----------------------------------------------------

#pragma warning(disable: 4098) void WndProc_OnDestroy(HWND hWnd) { // Освобождение ресурсов, полученных для // работы с Windows Sockets WSACleanup ();

// Завершение цикла обработки сообщений PostQuitMessage(0); return FORWARD_WM_DESTROY (hWnd, DefWindowProc); }

// ----------------------------------------------------- // Функция WndProc_OnSize // -----------------------------------------------------

#pragma warning(disable: 4098) void WndProc_OnSize(HWND hWnd, UINT state, int cx, int cy) { SendMessage(hwndSb, WM_SIZE , cx, cy); return FORWARD_WM_SIZE (hWnd, state, cx, cy, DefWindowProc); }

// ----------------------------------------------------- // Функция WndProc_OnCommand // -----------------------------------------------------

#pragma warning(disable: 4098) void WndProc_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case IDM_EXIT:

// Уничтожение главного окна прилоджения DestroyWindow(hWnd); break;

case IDM_CONNECT:

// Установка соединения с сервером SetConnection(hWnd); break;

case IDM_SEND:

// Посылка сообщения серверу SendMsg(hWnd); break;

default: MessageBox(NULL, "Unknown command", "Error", MB_OK); }

return FORWARD_WM_COMMAND (hWnd, id, hwndCtl, codeNotify, DefWindowProc); }



// ----------------------------------------------------- // Функция SetConnection // -----------------------------------------------------

void SetConnection(HWND hWnd) { PHOSTENT phe;

// Создаем сокет srv_socket = socket(AF_INET , SOCK_DGRAM, 0); if(srv_socket == INVALID_SOCKET) { MessageBox(NULL, "socket Error", "Error", MB_OK); return; }

// Связываем адрес IP с сокетом dest_sin.sin_family = AF_INET ; dest_sin.sin_addr .s_addr = INADDR_ANY ; dest_sin.sin_port = 0;

if(bind (srv_socket , (LPSOCKADDR )&dest_sin, sizeof(dest_sin)) == SOCKET_ERROR ) { // При ошибке закрываем сокет closesocket (srv_socket); MessageBox(NULL, "bind Error", "Error", MB_OK); return; }

// Устанавливаем адрес IP и номер порта dest_sin.sin_family = AF_INET ;

// Определяем адрес узла

// Адрес локального узла для отладки phe = gethostbyname ("localhost ");

// Адрес удаленного узла // phe = gethostbyname ("maxsinev");

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);

// Другой способ указания адреса узла // dest_sin.sin_addr .s_addr = inet_addr ("200.200.200.201");

// Копируем номер порта dest_sin.sin_port = htons(SERV_PORT);

// В случае успеха выводим сообщение об установке // соединения с узлом SendMessage(hwndSb, SB_SETTEXT, 0, (LPARAM)"Connected"); }

// ----------------------------------------------------- // Функция SendMsg // -----------------------------------------------------

void SendMsg(HWND hWnd) { char szBuf[80]; lstrcpy(szBuf, "Test string");

// Посылаем сообщение send to(srv_socket , szBuf, lstrlen(szBuf), 0, (PSOCKADDR )&dest_sin, sizeof(dest_sin)); }

Функция SetConnection создает сокет типа SOCK_DGRAM, так как передача данных будет выполняться с использованием протокола UDP :

srv_socket = socket(AF_INET , SOCK_DGRAM, 0);



Далее выполняется привязка сокета к адресу с помощью функции bind . При этом указывается нулевое значение порта и адрес INADDR_ANY , так как на данном этапе эти параметры не имеют значения.

Затем функция SetConnection записывает адрес сервера в структуру dest_sin. Этот адрес потребуется для передачи сообщений серверу.

При использовании протокола UDP и если не создан канал между приложениями, для передачи данных следует использовать функцию send to:

send to(srv_socket , szBuf, lstrlen(szBuf), 0, (PSOCKADDR )&dest_sin, sizeof(dest_sin));

В качестве предпоследнего параметра этой фукнции нужно передать адрес заполненной структуры, содержащей адрес узла, куда будет посылаться пакет данных. Через последний параметр функции send to необходимо передать размер указанной структуры.

Привдедем список возможных кодов ошибок для функции send to:

Код ошибки Описание
WSANOTINITIALISED Перед использованием функции необходимо вызвать функцию WSAStartup
WSAENETDOWN Сбой в сети
WSAEACCES Не был установлен флаг широковещательного адреса
WSAEINTR Работа функции была отменена при помощи функции WSACancelBlockingCall
WSAEINPROGRESS Выполняется блокирующая функция интерфейса Windows Sockets
WSAEFAULT Неправильно указан адрес буфера, содержащего передаваемые данные
WSAENETRESET Необходимо сбросить соединение
WSAENOBUFS Произошло зацикливание буферов
WSAENOTSOCK Указанный дескриптор не является дескриптором сокета
WSAESHUTDOWN Сокет был закрыт функцией shutdown
WSAEWOULDBLOCK Сокет отмечен как неблокирующий, но запрошенная операция приведет к блокировке
WSAEMSGSIZE Размер датаграммы больше, чем это допускается данной реализацией интерфейса Windows Sockets
WSAECONNABORTED Сбой из-за слишком большой задержки или по другой причине
WSAECONNRESET Сброс соединения удаленным узлом
WSAEADDRNOTAVAIL Указанный адрес недоступен
WSAEAFNOSUPPORT Данный тип сокета не может работать с указанным семейством адресов
WSAEDESTADDRREQ Необходимо указать адрес получателя датаграммы
WSAENETUNREACH В данное время и из данного узла невозможно получить доступ к сети
Заметим, что клиент может создать канал связи с сервером, вызвав функцию connect, и передавать по этому каналу пакеты UDP , пользуясь функциями send и recv . Этот способ удобен тем, что при передаче пакета не нужно каждый раз указывать адрес получателя.


Содержание раздела