Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Глобальные сети компьютеров. Практическое введение в Internet, E-Mail, FTP, WWW и HTML, программирование для Windows Sockets

© Александр Фролов, Григорий Фролов
Том 23, М.: Диалог-МИФИ, 1993, 283 стр.

[Назад] [Содеожание] [Дальше]

5.9. Приложение 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 . Этот способ удобен тем, что при передаче пакета не нужно каждый раз указывать адрес получателя.

[Назад] [Содеожание] [Дальше]