window下c/c++异步发送udp和非阻塞的方式接收udp的类封装

2023-05-16

以下代码对udp发送和接收都做了封装,在发送和接收前都需要去注册使用的功能,从而做到需要哪个模块才启动哪个模块的功能,避免资源的浪费。

udp发送功能:使用列表和信号量的方式实现异步发送数据,避免主线程发送数据时出现阻塞的情况

udp接收功能:使用select函数可以实现非阻塞方式接收,避免主线程接收数据需要阻塞等待消息的到来

更详细的说明在代码的注释中,如发现问题欢迎批评指正~

udp.h

#pragma once

#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <string>
#include <list>
#include <string>

#pragma comment(lib, "WS2_32")
#define CACHE_LENGTH 1024			//接收区大小

#define UDP_SERVER 0x02
#define UDP_SEND 0x01


namespace UDP_SERVE {

	typedef struct {
		u_short port;		//端口
		std::string ip;		//IP地址
	}network_config;


	class udp_server
	{
	public:
		udp_server();
		~udp_server();

		// desc: 注册服务
		// param: 参数一/说明要注册的服务的类型(UDP_SERVER为UDP接收,UDP_SEND为UDP发送) 参数二/配置的网络信息 
		// return: 注册成功或者已注册返回true,注册失败返回false
		bool Register_Service(size_t, network_config);

		// desc: 注销服务
		// param: 参数一/说明要注销的服务的类型(UDP_SERVER为UDP接收,UDP_SEND为UDP发送)
		// return: 注销成功或者已注册返回true,注销失败返回false
		bool Cancel_Service(size_t);

		// desc: 发送数据
		// param: 参数一/要发送的数据
		// return: 发送成功返回true,发送失败返回false
		bool send_data(const char *);

		// desc: 非阻塞接收数据
		// param: NULL
		// return: 没接收到数据返回-1,接收服务未注册返回-2,监听失败返回-3,recvfrom接收失败返回-4,成功则返回接收到的字符数
		long receive_data();

	public:
		char UDP_receive_buffer[CACHE_LENGTH];		//接收缓冲区

	private:
		// desc: 注册接收服务
		// param: 参数一/配置的网络信息 
		// return: 注册成功返回true,注册失败返回false
		bool Register_Server(network_config);

		// desc: 注册发送服务
		// param: 参数一/配置的网络信息 
		// return: 注册成功返回true,注册失败返回false
		bool Register_Sender(network_config);

		// desc: 启动WinSock2
		// param: NULL
		// return: 启动成功返回true,启动失败返回false
		bool InitWinsock();

		// desc: 注销发送服务
		// param: NULL
		// return: 取消成功返回true,取消失败返回false
		bool Cancel_Sender_Service();

		// desc: 注销接收服务
		// param: NULL
		// return: 取消成功返回true,取消失败返回false
		bool Cancel_Server_Service();

		// desc: 发送端多线程函数
		// param: udp_server类指针
		// return: NULL
		static DWORD WINAPI Udp_Send_Fun(LPVOID lpParamter);

	private:
		volatile bool is_register_Server;		//Server是否初始化标识位
		volatile bool is_register_Sender;		//Sender是否初始化标识位

		fd_set rfd;								//描述符集 这个将用来测试有没有一个可用的连接
		struct timeval timeout;					//定时		
		int rev;
		SOCKET sockListen;						//接收者socket
		SOCKADDR_IN addrServer;					//接收端网络信息

		SOCKET sockClient;						//发送端socket
		SOCKADDR_IN addrSender;					//发送端网络信息
		HANDLE hThread_sender;					//发送端多线程句柄
		std::list<std::string> udp_send_list;	//发送数据的列表
		HANDLE hSemaphore;						//定义信号量句柄

	};
}

udp.cpp

#include "udp_server.h"

namespace UDP_SERVE {

	udp_server::udp_server() :is_register_Server(false), is_register_Sender(false), hThread_sender(NULL)
	{
		//开启Winsock
		InitWinsock();
	}


	udp_server::~udp_server()
	{
		//关闭Winsock的使用
		WSACleanup();
	}

	bool udp_server::Register_Service(size_t type, network_config config)
	{
		if (type & UDP_SERVER) {
			if (!is_register_Server) {
				if (!Register_Server(config))
					return false;
				else
					is_register_Server = true;
			}
			else
				printf("UDP_SERVER have already registered\n");
		}

		if (type & UDP_SEND) {
			if (!is_register_Sender) {
				if (!Register_Sender(config))
					return false;
				else
					is_register_Sender = true;
			}
			else {
				printf("UDP_SEND have already registered\n");
			}
		}

		return true;
	}

	bool udp_server::Cancel_Service(size_t type)
	{
		if (type & UDP_SERVER) {
			if (is_register_Server) {
				if (!Cancel_Server_Service())
					return false;
				else
					is_register_Server = false;
			}
			else
				printf("UDP_SERVER have already logout\n");
		}


		if (type & UDP_SEND) {
			if (is_register_Sender) {
				is_register_Sender = false;
				ReleaseSemaphore(hSemaphore, 1, NULL);
				if (!Cancel_Sender_Service())
					return false;
				else
					is_register_Sender = false;
			}
			else
				printf("UDP_SEND have already logout\n");
		}

		return true;
	}

	bool udp_server::send_data(const char *data)
	{

		if (is_register_Sender) {
			udp_send_list.push_back(std::string(data));
			ReleaseSemaphore(hSemaphore, 1, NULL);				//增加信号量
		}
		else {
			printf("The Sender service is not registered\n");
			return false;
		}

		return true;
	}

	long udp_server::receive_data()
	{
		if (is_register_Server) {
			int fromlen = sizeof(struct sockaddr_in);
			FD_ZERO(&rfd);           //总是这样先清空一个描述符集
			FD_SET(sockListen, &rfd); //把sock放入要测试的描述符集
			int SelectRcv = select(FD_SETSIZE, &rfd, 0, 0, &timeout); //检查该套接字是否可读
			if (SelectRcv == 0) {
				//没接收到数据
				return -1;
			}
			else if (SelectRcv < 0) {
				std::cout << "监听失败" << GetLastError() << std::endl;
				return -3;
			}
			else
			{
				memset(UDP_receive_buffer, '\0', (CACHE_LENGTH) * sizeof(char));
				rev = 0;
				rev = recvfrom(sockListen, UDP_receive_buffer, (CACHE_LENGTH) * sizeof(char), 0, (struct sockaddr*)&addrServer, &fromlen);
				if (rev != SOCKET_ERROR)
				{
					//数据接收成功
					return rev;
				}
				else {
					return -4;
				}
			}
		}
		else {
			printf("The Server service is not registered\n");
			return -2;
		}


	}

	bool udp_server::Register_Server(network_config  config)
	{
		timeout.tv_sec = 0;               //等下select用到这个
		timeout.tv_usec = 0;              //timeout设置为0,可以理解为非阻塞
		rev = 0;

		sockListen = socket(AF_INET, SOCK_DGRAM, 0);
		if (sockListen == -1)
		{
			std::cout << "Socket error" << std::endl;
			return false;
		}
		int recvbuf = 1;
		setsockopt(sockListen, SOL_SOCKET, SO_RCVBUF, (char*)&recvbuf, sizeof(int));
		//设置为非阻塞模式  
		u_long imode = 1;
		rev = ioctlsocket(sockListen, FIONBIO, &imode);
		if (rev == SOCKET_ERROR)
		{
			printf("ioctlsocket failed!");
			//closesocket(sockListen);
			//WSACleanup();
			return false;
		}
		memset(&addrServer, 0, sizeof(sockaddr_in));
		addrServer.sin_family = AF_INET;
		addrServer.sin_port = htons(config.port);             		//监听端口
		addrServer.sin_addr.s_addr = inet_addr(config.ip.c_str());  //INADDR_ANY
		if (0 != ::bind(sockListen, (struct sockaddr*)&addrServer, sizeof(struct sockaddr)))
		{
			std::cout << "recv_bind()失败,error: " << GetLastError() << std::endl;
			return false;
		}

		return true;
	}

	bool udp_server::Register_Sender(network_config config)
	{
		sockClient = socket(AF_INET, SOCK_DGRAM, 0);
		addrSender.sin_addr.S_un.S_addr = inet_addr(config.ip.c_str());
		addrSender.sin_family = AF_INET;
		addrSender.sin_port = htons(config.port);
		hThread_sender = CreateThread(NULL, 0, Udp_Send_Fun, this, 0, NULL);		//创建发送子线程
		hSemaphore = CreateSemaphore(NULL, 0, 100, NULL);							//创建信号量
		return true;
	}

	bool udp_server::InitWinsock()
	{
		int Error;
		WORD VersionRequested;
		WSADATA WsaData;
		VersionRequested = MAKEWORD(2, 2);
		Error = WSAStartup(VersionRequested, &WsaData);
		if (Error != 0)
		{
			return false;
		}
		else
		{
			if (LOBYTE(WsaData.wVersion) != 2 || HIBYTE(WsaData.wHighVersion) != 2)
			{
				WSACleanup();
				return false;
			}
		}
		return true;
	}

	bool udp_server::Cancel_Sender_Service()
	{
		WaitForMultipleObjects(1, &hThread_sender, TRUE, INFINITE);
		CloseHandle(hSemaphore);
		CloseHandle(hThread_sender);
		closesocket(sockClient);
		return true;
	}

	bool udp_server::Cancel_Server_Service()
	{
		closesocket(sockListen);
		return false;
	}

	DWORD udp_server::Udp_Send_Fun(LPVOID lpParamter)
	{
		udp_server *p = (udp_server *)lpParamter;
		while (1) {
			WaitForSingleObject(p->hSemaphore, INFINITE);						//阻塞等待信号,直到信号量为signal状态。函数执行后信号量自动减1
			if (!p->is_register_Sender) {
				break;
			}
			if (!(p->udp_send_list).empty()) {
				if (-1 == sendto(p->sockClient, (p->udp_send_list).front().c_str(), (p->udp_send_list).front().size(), 0, (SOCKADDR*)&(p->addrSender), sizeof(SOCKADDR))) {		//发送数据
					printf("UDP SendTo error -> %s\n", strerror(errno));
				}
				(p->udp_send_list).pop_front();									//数据出列
			}
			else {
				printf("udp_send_list is empty\n");
			}
			Sleep(100);
		}
		printf("Udp_Send_Fun() exit\n");
		return 0;
	}
}

main.cpp

#include "udp_server.h"

//轮询查看有没有消息到来
DWORD WINAPI test_Func(LPVOID lpParamter) {
	UDP_SERVE::udp_server *p = (UDP_SERVE::udp_server *)lpParamter;
	while (1)
	{
		long l_buf = p->receive_data();
		//接收错误
		if (-1 > l_buf) {
			break;
		}
		//未接收到数据
		else if (l_buf == -1) {
			continue;
		}
		//接收到数据
		else {
			printf("len %ld, ", l_buf);
			printf("data %s\n", p->UDP_receive_buffer);
		}
		Sleep(100);
	}
	return 0;
}



int main()
{
	UDP_SERVE::udp_server *p = new UDP_SERVE::udp_server();

	//设置接收udp模块的信息
	UDP_SERVE::network_config config;
	config.ip = "0.0.0.0";
	config.port = 5600;

	//注册接收udp模块
	if (!p->Register_Service(UDP_SERVER, config)) {
		printf("Register_Service error\n");
	}

	//设置发送udp模块的信息
	config.ip = "127.0.0.1";
	config.port = 5600;
	//注册发送udp模块
	if (!p->Register_Service(UDP_SEND, config)) {
		printf("Register_Service error\n");
	}

	//创建测试线程
	CreateThread(NULL, 0, test_Func, p, 0, NULL);

	Sleep(1000);

	//发送数据
	std::string data = "ggrerrrrr";
	p->send_data(data.c_str());
	data = "gg1";
	p->send_data(data.c_str());
	data = "gg2";
	p->send_data(data.c_str());
	data = "gg3";
	p->send_data(data.c_str());
	data = "gg4";
	p->send_data(data.c_str());
	data = "gg5";
	p->send_data(data.c_str());
	data = "gg6";
	p->send_data(data.c_str());
	data = "gg7";
	p->send_data(data.c_str());
	data = "gg8";
	p->send_data(data.c_str());

	Sleep(1000);
	
	//注销发送udp模块
	p->Cancel_Service(UDP_SEND);
	//注销接收udp模块
	p->Cancel_Service(UDP_SERVER);
	delete p;

	system("pause");
	return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

window下c/c++异步发送udp和非阻塞的方式接收udp的类封装 的相关文章

  • 5G+北斗RTK定位:高精度定位技术发展更进一步

    5G 43 北斗RTK定位采用5G定位与北斗RTK定位技术 xff0c 在信号 信息 设施 应用等层面深度融合 xff0c 5G自身可实现亚米级的定位能力与北斗形成信号覆盖互补 xff0c 实现从室内到室外无缝隙衔接与定位 5G通信技术的到
  • 室外定位:高精度北斗RTK定位技术

    北斗RTK定位技术 xff0c 也称北斗差分定位技术 xff0c 利用我国自主研发的北斗卫星定位系统实现精确定位功能 定位精度可根据需要 xff0c 通过选择不同精度的人员定位终端来实现 在科技强国的战略驱动下 xff0c 北斗RTK定位技
  • 【高精度定位】RTK定位与RTD定位知识科普

    高精度定位一般指亚米级别或厘米级别的定位 xff0c 常见的室内有蓝牙AoA和UWB两种技术 xff0c 室外有北斗地基增强技术 xff0c 这些技术都是采用算法进行定位 工业4 0时代 xff0c 在资源和环境约束不断强化的背景下 xff
  • curl http或https上传下载

    curl的使用 注意如果是https请求需要带上ssl的库和curl库如下图 上传文件 回调函数 span class token keyword static span size t span class token function h
  • 层次遍历构建二叉树

    span class hljs keyword struct span TreeNode span class hljs keyword int span val span class hljs keyword struct span Tr
  • Jetson tx2 使用 jetpack 4.3刷机全过程

    1 首先准备一台安装有ubuntu 18 04 ubuntu16 04 系统的主机 xff08 host也就是笔记本 xff09 2 在nvidia官网注册一个nvidia的账号用于下载jetpack 4 3时使用 xff08 刷机过程中需
  • 2020/2/21 Linux Socket编程 高级篇——广播

    广播 xff1a 实现一对多的通信 SO BROADCAST选项 它控制了UDP套接字是否能发广播数据报 xff0c 选项类型是int xff0c 非零表示 是 只有UDP能用 xff0c TCP不能 如果是一个广播地址 xff0c 但SO
  • 大小端字节序(Big Endian和Little Endian)

    那么何为字节序 xff08 Endia xff09 呢 xff1f Big Endian是指低地址存放最高有效字节 xff08 MSB xff09 xff0c 而Little Endian则是低地址存放最低有效字节 xff08 LSB xf
  • GPS定位及通信协议解析

    1 简介 上图为某公司的GPS定位模块 xff0c 自己在使用其进行GPS定位过程中学习到的知识分享给大家 主要参数如下 半双工 UART 3 3V TTL 电平 RS232 IIC CAN 接口输出 xff0c 更快速的应用 xff1b
  • JVM系列(八):堆(Heap)的相关知识介绍

    目录 1 JVM堆的概念 2 JVM堆的特点 3 JVM堆的内部结构 3 1 组成 3 2 堆内存内部空间所占比例 3 3 永久代和元空间区别 4 堆空间的大小设置 5 堆空间垃圾回收 1 JVM堆的概念 JVM中的堆是用来存放对象的内存空
  • C++读写TIF格式

    参考文章 xff1a xff08 1 xff09 https www cnblogs com gywei p 3393816 html xff08 2 xff09 https blog csdn net han jiang xue arti
  • matlab Fourier变换--方波信号的分解与重建

    N 61 200 采用的数据点数 dt 61 4 N 采样的间隔 for n 61 1 N 生成一个方波 if n dt gt 61 2 x n 61 0 8 else x n 61 0 8 end end figure subplot 2
  • matlab 小波分析--高通滤波器和低通滤波器

    xff08 一 xff09 加载一个尺度滤波器 load db8 w 61 db8 figure subplot 4 2 1 stem w title 39 原尺度滤波器 39 计算4个滤波器 Lo D Hi D Lo R Hi R 61
  • C/C++中rand() 函数产生随机数与srand()设置随机数种子

    xff08 1 xff09 rand 产生随机数 xff0c 输出的随机数序列是确定的 xff0c 即每次运行结果一致 include lt iostream gt int main int i j for i 61 0 i lt 10 i
  • matlab与数字图像处理--膨胀和腐蚀

    对于初学者 xff0c 参考一篇很好的说明 xff1a https blog csdn net alw 123 article details 83868878 左边是一个二值化的测试图像 xff0c 白色为图像元素 xff0c 黑色为背景
  • Emmet语法

    Emmet语法 1 1 快速生成HTML结构语法1 2 快速生成CSS样式语法1 3 快速格式化代码 Emmet语法的前身是Zen coding 它使用缩写 来提高html css的编写速度 Vscode内部已经集成该语法 快速生成HTML
  • CString,int,string,char*之间的转换

    C 43 43 标准函数库 中说的 有三个函数可以将字符串的内容转换为字符数组和C string 1 data 返回没有 0 的字符串数组 2 c str xff0c 返回有 0 的字符串数组 3 xff0c copy int 转 CStr
  • C++ TCP网络编程--服务器端多线程处理会话连接

    客户端程序 xff1a include lt winsock h gt include lt iostream gt pragma comment lib 34 ws2 32 lib 34 include lt Windows h gt i
  • matlab复杂函数多元函数拟合

    简介 本文介绍了基于matlab实现的复杂函数以及多元函数的拟合 在工程和研究中偶尔会遇到要用一个非常复杂的数学公式来拟合实验测量数据 xff0c 对这些复杂的数学公式拟合时 xff0c 采用常见的拟合方法往往会失败 xff0c 或者得不到
  • 基于模板替换的word文档自动生成

    简介 word文档自动生成程序是一个根据用户提供的模板word文档以及一些必要的数据文件来渲染生成所要的word文档的工具 关键词 xff1a 批量word文档生成 word文档渲染 自动生成word 使用场景 xff1a 几种典型的可以采

随机推荐