- •57 Програмне забезпечення мереж еом. Частина 3. Відання 2. Програмування інтерфейсу «WinSocket»
- •Лабораторна робота № 1. Основні теоретичні положення (інтерфейс windows sockets)
- •1.1. Сокети, датаграми й канали зв'язку
- •1.1.1. Ініціалізація додатка й завершення його роботи
- •1.1.2. Створення й ініціалізація сокету
- •1.3. Індивідуальні завдання на роботу №1
- •Лабораторна робота № 2. Створення каналу зв'язку
- •2.1. Сторона сервера
- •2.2. Сторона клієнта
- •2.3. Передача й прийом даних
- •2.4. Додаток ServerSocket
- •2.5. Додаток ClientSocket
- •2.6. Індивідуальні завдання на роботу №2
- •Лабораторна робота № 3. Датаграммный протокол
- •3.1. Додаток ServerDSocket
- •3.2. Додаток ClientDSocket
- •3.3. Індивідуальні завдання на роботу №3
3.2. Додаток ClientDSocket
Вихідні тексти додатка CLIENTD, призначеного для спільної роботи з додатком SERVERD, представлені в листинге 5.8. Тому що цей додаток дуже схоже на додаток CLIENT, будуть описані тільки відмінності.
Листинг 3.2.
Файл ClientDSocket/ClientDSocketDlg.h
// ClientDSocketDlg.h : header file
//
#if !defined(AFX_CLIENTDSOCKETDLG_H__B847DAF5_06EC_424A_B9EE_F8986CC36220__INCLUDED_)
#define AFX_CLIENTDSOCKETDLG_H__B847DAF5_06EC_424A_B9EE_F8986CC36220__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <winsock2.h>
/////////////////////////////////////////////////////////////////////////////
// CClientDSocketDlg dialog
class CClientDSocketDlg : public CDialog
{
private:
void SendData( char * ipAddress, int port, char *szBuf );
void SetConnection( char *ipAddress, int port );
void ShowData(char * szBuf );
public:
CClientDSocketDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CClientDSocketDlg)
enum { IDD = IDD_CLIENTDSOCKET_DIALOG };
CIPAddressCtrl m_IpAddress;
CString m_edtData;
int m_portNum;
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CClientDSocketDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
void CloseSocket();
HICON m_hIcon;
// Методи для роботи із сокетуми
BOOL InitLib();
// Змінні для роботи із сокетом
SOCKET clt_socket; // Дескриптор клієнтського сокету
// Generated message map functions
//{{AFX_MSG(CClientDSocketDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnBtnSend();
afx_msg void OnBtnConnect();
afx_msg void OnBtnDisconnect();
afx_msg void OnClose();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_CLIENTDSOCKETDLG_H__B847DAF5_06EC_424A_B9EE_F8986CC36220__INCLUDED_)
Файл ClientDSocket/ClientDSocketDlg.cpp
// ClientDSocketDlg.cpp : implementation file
//
#include "stdafx.h"
#include "ClientDSocket.h"
#include "ClientDSocketDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CClientDSocketDlg dialog
CClientDSocketDlg::CClientDSocketDlg(CWnd* pParent /*=NULL*/)
: CDialog(CClientDSocketDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CClientDSocketDlg)
m_edtData = _T("");
m_portNum = 0;
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CClientDSocketDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CClientDSocketDlg)
DDX_Control(pDX, IDC_IPADDRESS1, m_IpAddress);
DDX_Text(pDX, IDC_EDIT1, m_edtData);
DDX_Text(pDX, IDC_EDT_PORT, m_portNum);
DDV_MinMaxInt(pDX, m_portNum, 0, 65535);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CClientDSocketDlg, CDialog)
//{{AFX_MSG_MAP(CClientDSocketDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BTN_SEND, OnBtnSend)
ON_BN_CLICKED(IDC_BTN_CONNECT, OnBtnConnect)
ON_BN_CLICKED(IDC_BTN_DISCONNECT, OnBtnDisconnect)
ON_WM_CLOSE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CClientDSocketDlg message handlers
BOOL CClientDSocketDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0x000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
m_IpAddress.SetWindowText("127.0.0.1");
m_portNum = 8888;
UpdateData(FALSE);
if ( !InitLib() )
{
MessageBox("Init error", "Error", MB_OK);
return FALSE;
}
return TRUE; // return TRUE unless you set the focus to a control
}
void CClientDSocketDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CClientDSocketDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CClientDSocketDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
BOOL CClientDSocketDlg::InitLib()
{
int rc;
WSADATA WSAData;
char szTemp[128];
// Ініціалізація й перевірка версії Windows Sockets
rc = WSAStartup(MAKEWORD(2, 2), &WSAData);
if(rc != 0)
{
MessageBox("WSAStartup Error", "Error", MB_OK);
return FALSE;
}
// Відображаємо опис і версію системи Windows Sockets
// у заголовку вікна
wsprintf(szTemp, "Client use %s %s",
WSAData.szDescription,WSAData.szSystemStatus);
SetWindowText( szTemp );
return TRUE;
}
void CClientDSocketDlg::SetConnection( char *ipAddress, int port )
{
// Створюємо сокет
SOCKET old_socket = clt_socket;
clt_socket = socket(AF_INET, SOCK_DGRAM, 0);
if(clt_socket == INVALID_SOCKET)
{
clt_socket = old_socket;
return;
}
}
void CClientDSocketDlg::SendData( char * ipAddress, int port, char *szBuf )
{
PHOSTENT phe;
SOCKADDR_IN destAddr;
memset( &destAddr, 0, sizeof(destAddr) );
// Установлюємо адресу IP і номер порту
// Визначаємо адреса вузла
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);
destAddr.sin_family = AF_INET;
// Установлюємо з'єднання
int len = strlen(szBuf);
sendto(clt_socket, szBuf, len, 0, (PSOCKADDR)&destAddr, sizeof(destAddr));
}
void CClientDSocketDlg::CloseSocket()
{
closesocket( clt_socket );
clt_socket = 0;
}
void CClientDSocketDlg::ShowData(char * szBuf )
{
UpdateData();
CString strData;
m_edtData += szBuf;
m_edtData += "\r\n";
UpdateData( FALSE );
}
void CClientDSocketDlg::OnBtnSend()
{
UpdateData();
CString strIpAddress;
m_IpAddress.GetWindowText(strIpAddress);
char *szData = (char*)(LPCSTR)m_edtData;
char *szIpAddress = (char*)(LPCSTR)strIpAddress;
SendData( szIpAddress, m_portNum, szData );
}
void CClientDSocketDlg::OnBtnConnect()
{
UpdateData();
CString strIpAddress;
m_IpAddress.GetWindowText( strIpAddress );
SetConnection( (char*)(LPCSTR)strIpAddress, m_portNum );
ShowData("Client start...");
}
void CClientDSocketDlg::OnBtnDisconnect()
{
CloseSocket();
ShowData("Client stop");
}
void CClientDSocketDlg::OnClose()
{
CloseSocket();
CDialog::OnClose();
}
Функція SetConnection створює сокет типу SOCK_DGRAM, тому що передача даних буде виконуватися з використанням протоколу UDP:
srv_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Далі виконується прив'язка сокету до адреси за допомогою функції bind. При цьому вказується нульове значення порту й адреса INADDR_ANY, тому що на даному етапі ці параметри не мають значення.
Потім функція SetConnection записує адресу сервера в структуру dest_sin. Ця адреса буде потрібно для передачі повідомлень серверу.
При використанні протоколу UDP і якщо не створений канал між додатками, для передачі даних варто використати функцію sendto:
sendto(srv_socket, szBuf, lstrlen(szBuf), 0, (PSOCKADDR)&dest_sin, sizeof(dest_sin));
Як передостанній параметр цієї функції потрібно передати адресу заповненої структури, що містить адреса вузла, куди буде посилати пакет даних. Через останній параметр функції sendto необхідно передати розмір зазначеної структури.
Приведемо список можливих кодів помилок для функції sendio:
Код помилки Опис
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 і гесv. Цей спосіб зручний тим, що при передачі пакета не потрібно щораз указувати адресу одержувача.
