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

以下代码对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;
}
  • 10
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值