vc udp客户端异步通讯类封装

本章讲简单共享UDP异步通讯例程,udp是一种无连接、不保证数据是否传递成功,比起tcp通讯,它优势是传输数据快,因为它忽略了可靠传输机制。

所谓异步就是接受数据的时候不会产生阻塞效果,函数要不采用回调机制,要不就是直接返回。

工程是用基于VS2010 MFC下的。

新建通讯类UDPClientClass.h

#include <WinSock2.h>
#define UDP_READ	WM_USER + 3
#define READ_MAX_BUFFER	32 * 1024
class UDPClientClass
{
private:
	SOCKADDR_IN addrTo;
	SOCKET sckClient;
	WSABUF writeWsabuf;
	WSABUF readWsabuf;
	HWND curHwnd;
public:
	UDPClientClass()
	{
		sckClient = INVALID_SOCKET;
	}

	~UDPClientClass()
	{
		Close();
	}

	BOOL Init()
	{
		WORD wVersionRequested;
		WSADATA wsaData;
		int err;
		wVersionRequested = MAKEWORD(2, 2);
		err = WSAStartup(wVersionRequested, &wsaData);
		if (err != 0)
		{
			return false;
		}
		if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
		{
			WSACleanup();
			return FALSE;
		}
		return true;
	}

	/**
	打开UDP
	返回值为true则打开,否则无法发送数据
	*/
	BOOL Open(const char *ip, UINT16 port, HWND hwnd)
	{
		if (!Init())
		{
			return FALSE;
		}
		sckClient = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0, 0);
		if (sckClient == INVALID_SOCKET)
		{
			return FALSE;
		}
		addrTo.sin_addr.S_un.S_addr = inet_addr(ip);
		addrTo.sin_family = AF_INET;
		addrTo.sin_port = htons(port);
		curHwnd = hwnd;					//绑定句柄和UDP_READ消息
		if (WSAAsyncSelect(sckClient, curHwnd, UDP_READ, FD_READ) == SOCKET_ERROR)
		{
			return FALSE;
		}
		int nRecvBuf =READ_MAX_BUFFER;	//接受缓冲区设置为32K
		setsockopt(sckClient, SOL_SOCKET, SO_RCVBUF, (const char*)&nRecvBuf,sizeof(int));
		return TRUE;
	}

	/**
	接受数据
	data:是接受数据的首指针
	addrFrom:服务端的IP地址端口结构指针
	*/
	DWORD read(UINT8 *data, SOCKADDR_IN *addrFrom, WPARAM w, LPARAM l)
	{
		switch (LOWORD(l))
		{
		case FD_READ:
			DWORD dwRead;
			DWORD dwFlag = 0;
			readWsabuf.len = READ_MAX_BUFFER;
			readWsabuf.buf = (char *)data;
			int len = sizeof(SOCKADDR);
			if (WSARecvFrom(sckClient, &readWsabuf, 1, &dwRead, &dwFlag, (SOCKADDR *)addrFrom, &len, NULL, NULL) == SOCKET_ERROR)
			{
				return 0;
			}
			return dwRead;
		}
		return 0;
	}

	/**
	发送数据
	*/
	int write(UINT8 *data, UINT32 len)
	{
		DWORD dwSend;
		writeWsabuf.buf = (char *)data;
		writeWsabuf.len = len;
		if (sckClient == INVALID_SOCKET)//如果没有打开UDP,则无法发送数据,直接返回0						
		{
			return 0;
		}
		if (WSASendTo(sckClient, &writeWsabuf, 1, &dwSend, 0, (SOCKADDR *)&addrTo, sizeof(SOCKADDR), NULL, NULL) == SOCKET_ERROR)
		{
			return 0;
		}
		return dwSend;
	}

	/**
	判断是否打开UDP
	*/
	BOOL IsOpen()
	{
		if (sckClient != INVALID_SOCKET)
		{
			return TRUE;
		}
		return FALSE;
	}

	/**
	关闭UDP
	*/
	void Close()
	{
		if (sckClient != INVALID_SOCKET)
		{
			WSACleanup();
			sckClient = INVALID_SOCKET;
		}
	}
};
在stdafx.h添加代码,将出现调试窗口

#define MY_DEBUG 1  
#if MY_DEBUG      
#pragma comment( linker, "/subsystem:console /entry:wWinMainCRTStartup" )      
#endif    

上面三个按钮,ID号分别为IDC_BUTTON_OPEN,IDC_BUTTON_CLOSE,IDC_BUTTON_SEND

对应Resources.h内容如下:

#define IDC_BUTTON_SEND                 1000
#define IDC_BUTTON_OPEN                 1001
#define IDC_BUTTON_CLOSE                1002
在UDPClientDlg.h头文件内容如下:

// UDPClientDlg.h : 头文件
//
#pragma once
#include "UDPClientClass.h"
// CUDPClientDlg 对话框
class CUDPClientDlg : public CDialogEx
{
// 构造
private:
	UDPClientClass udpCliCls;
public:
	CUDPClientDlg(CWnd* pParent = NULL);	// 标准构造函数
// 对话框数据
	enum { IDD = IDD_UDPCLIENT_DIALOG };
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持
// 实现
protected:
	HICON m_hIcon;
	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnBnClickedButtonSend();
	afx_msg void OnBnClickedButtonClose();
	afx_msg void OnBnClickedButtonOpen();
	afx_msg LRESULT OnSock(WPARAM, LPARAM);	//和UDP_READ绑定的函数
};
在UDPCleintDlg.cpp里面内容如下

// UDPClientDlg.cpp : 实现文件
//

#include "stdafx.h"
#include "UDPClient.h"
#include "UDPClientDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CUDPClientDlg 对话框
CUDPClientDlg::CUDPClientDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CUDPClientDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CUDPClientDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CUDPClientDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON_SEND, &CUDPClientDlg::OnBnClickedButtonSend)
	ON_BN_CLICKED(IDC_BUTTON_CLOSE, &CUDPClientDlg::OnBnClickedButtonClose)
	ON_BN_CLICKED(IDC_BUTTON_OPEN, &CUDPClientDlg::OnBnClickedButtonOpen)
	ON_MESSAGE(UDP_READ, &CUDPClientDlg::OnSock)	//消息绑定UDP_READ
END_MESSAGE_MAP()

// CUDPClientDlg 消息处理程序
BOOL CUDPClientDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码
	GetDlgItem(IDC_BUTTON_OPEN)->EnableWindow(true);
	GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(false);
	GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(false);
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CUDPClientDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		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;
		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CUDPClientDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

LRESULT CUDPClientDlg::OnSock(WPARAM wParam, LPARAM lParam)
{
	UINT8 *data = new UINT8[READ_MAX_BUFFER];
	SOCKADDR_IN addrFrom;
	UINT32 len = udpCliCls.read(data, &addrFrom, wParam, lParam);
	if (len > 0)
	{
		printf("***UDP client recv\r\n", len);
		printf("ip = %08x port = %5d\r\n", addrFrom.sin_addr.S_un.S_addr, ntohs(addrFrom.sin_port));
		for (DWORD i = 0; i < len; i++)
		{
			printf("%d=%02x\r\n", i, data[i]);
		}
	}
	delete data;
	data = NULL;
	return 0;
}

void CUDPClientDlg::OnBnClickedButtonOpen()
{
	// TODO: 在此添加控件通知处理程序代码
	if (!udpCliCls.IsOpen())
	{
		if (udpCliCls.Open("127.0.0.1", 1111, this->m_hWnd))
		{
			GetDlgItem(IDC_BUTTON_OPEN)->EnableWindow(false);
			GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(true);
			GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(true);
		}
	}
}

void CUDPClientDlg::OnBnClickedButtonClose()
{
	// TODO: 在此添加控件通知处理程序代码
	if (udpCliCls.IsOpen())
	{
		udpCliCls.Close();
		GetDlgItem(IDC_BUTTON_OPEN)->EnableWindow(true);
		GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(false);
		GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(false);
	}
}

void CUDPClientDlg::OnBnClickedButtonSend()
{
	// TODO: 在此添加控件通知处理程序代码
	UINT8 a[10] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
	if (udpCliCls.IsOpen())
	{
		if (udpCliCls.write(a, 10))
		{
			printf("***UDP client send\r\n", a);
			for (DWORD i = 0; i < 10; i++)
			{
				printf("%d=%02x\r\n", i, a[i]);
			}
		}
	}
}
编译运行,并且打开网口调试工具,开UDP Server,监听1111端口,运行示意图如下:

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值