以下代码对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;
}