RakNet网络框架简单入门

        raknet是采用c++语言编写的一套基于UDP协议的高性能网络框架,主要使用在游戏中,当然了,其他项目也可以使用。还有,他是跨平台的。

        由于目前的手游项目的网络框架使用的就是它,花了点时间看了看,这里写个比较简单的例子来说明该如何使用它。要是在项目中使用,只需要扩展这个代码即可。

        raknet官网上下载资料包,使用vs2008及以上的版本打开里面的项目工程,编译里面的DLL项目,生成RakNet_DLL_Debug_Win32.lib和RakNet_DLL_Debug_Win32.dll文件,当然这是Debug模式下。

        

        现在我们实现这样一个效果,server接受client发送过来的数据,并添加一些字符串否再发给client,client显示发送和接受的数据。发送的内容使用一个结构体来存放。

我们新建一个文件夹,将刚刚生成的lib文件拷贝进去,并且将解压出来的source文件夹一起复制过去,里面含有raknet的头文件。


在vs中创建win32控制台程序工程,放到新建的文件夹中,工程属性中配置属性C/C++常规附加库包含目录中添加source文件夹,连接器输入中添加RakNet_DLL_Debug_Win32.lib,连接器常规附加库目录协商lib文件所在的目录。


首先看看server端代码:NetMgr.h

#ifndef __NETMGR_H__
#define __NETMGR_H__

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

	//初始化网络
	void init_net_work() const;

	//接收网络消息
	void net_work_update() const;

	//发送数据给client
	void send_data_to_client() const;

	//接收client的数据
	void receive_data_from_client(const unsigned char *msg_data) const;

private:
	//处理server发送过来的消息
	void _process_client_message() const;

public:
	unsigned short server_port;
	int max_client;
};

#endif


再看看NetMgr.cpp:

#include "NetMgr.h"
#include "../../Source/RakPeerInterface.h"
#include "../../Source/RakNetTypes.h"
#include "../../Source/MessageIdentifiers.h"
#include "../../Source/BitStream.h"

#include <iostream>
#include "ServerClientMessage.h"
#include "ServerClientStruct.h"


RakNet::RakPeerInterface *net_peer;			//网络消息
RakNet::Packet *net_packet;					//网络消息包


NetMgr::NetMgr()
	: server_port(10001)
	, max_client(1000)
{

}

NetMgr::~NetMgr()
{

}

//初始化网络
void NetMgr::init_net_work() const
{
	//初始化网络消息
	net_peer = RakNet::RakPeerInterface::GetInstance();
	if (NULL == net_peer)
	{
		std::cout << "GetInstance failed" << std::endl;
		return;
	}

	std::cout << "Start Game Server ......" << server_port << std::endl;

	//开启网络线程来监听相应的端口
	//Startup函数的第一个参数是接受的最大连接数,客户端一般设置成1
	//第二个参数就是要监听的端口。SocketDescriptor监听制定的端口,他是一种套接字,服务器一般设置成SocketDescriptor(server_port, 0)
	//第三个参数是SocketDescriptor数组的大小,传1表明不确定。
	int start_up = net_peer->Startup(1, &RakNet::SocketDescriptor(server_port, 0), 1);
	if (start_up > 0)
	{
		std::cout << "Startup failed" << std::endl;
		return;
	}

	//SetMaximumIncomingConnections容许最多的连接数量
	net_peer->SetMaximumIncomingConnections(max_client);
}

//接收网络消息
void NetMgr::net_work_update() const
{
	//Receive从消息队列中获取消息
	for (net_packet = net_peer->Receive(); net_packet; net_peer->DeallocatePacket(net_packet), net_packet = net_peer->Receive())
	{
		switch (net_packet->data[0])
		{
		case ID_REMOTE_DISCONNECTION_NOTIFICATION:
			std::cout << "Another client has disconnected" << std::endl;
			break;
		case ID_REMOTE_CONNECTION_LOST:
			std::cout << "Another client has lost the connection" << std::endl;
			break;
		case ID_REMOTE_NEW_INCOMING_CONNECTION:
			std::cout << "Another client has connected" << std::endl;
			break;
		case ID_CONNECTION_REQUEST_ACCEPTED:
			std::cout << "Our connection request has been accepted for server" << std::endl;
			break;
		case ID_NEW_INCOMING_CONNECTION:
			std::cout << "A connection is incoming  " << std::endl;
			break;
		case ID_NO_FREE_INCOMING_CONNECTIONS:
			std::cout << "The server is full" << std::endl;
			break;
		case ID_DISCONNECTION_NOTIFICATION:
			std::cout << "A client has disconnected" << std::endl;
			break;
		case ID_CONNECTION_LOST:
			std::cout << "A client lost the connection  " << std::endl;
			break;
		case ID_USER_PACKET_ENUM:
			_process_client_message();				//用户自定义消息入口
			break;
		default:
			std::cout << "Message with identifier %d has arrived" << net_packet->data[0] << std::endl;
			break;
		}
	}
}

// client 发送过来的消息
void NetMgr::_process_client_message() const
{
	int message_id = 0;
	//将消息解析出来,使用BitStream
	//IgnoreBytes忽略掉最外层的raknet的消息类型
	RakNet::BitStream bs_in(net_packet->data, net_packet->length, false);
	bs_in.IgnoreBytes(sizeof(RakNet::MessageID));
	bs_in.Read(message_id);

	//偏移掉自定义的消息,获取实际数据,raknet的消息类型大小是一个字节,自定义的结构体是4个字节(32位机器)
	unsigned char *msg_data = net_packet->data;
	msg_data += sizeof(unsigned long) + sizeof(unsigned char);

	switch (message_id)
	{
	case msg_connect_server:
		this->receive_data_from_client(msg_data);
		break;

	default:
		break;
	}
}

// 接收 client 数据 并回应client
void NetMgr::receive_data_from_client(const unsigned char *msg_data) const
{
	connect_server *receive_data = (connect_server *)msg_data;

	std::cout << "receive from client data is :" << receive_data->content << std::endl;

	connect_server_return send_data;
	memset(&send_data, 0, sizeof(connect_server_return));

	std::string content = receive_data->content;
	content += ". server already receive client data, this is server data";

	sprintf_s(send_data.content_return, "%s", content.c_str());

	std::cout << "server data is :" << send_data.content_return << std::endl;

	//使用BitStream 对象来封装数据
	RakNet::BitStream stream;
	//先写入raknet的消息类型(raknet中自定义类型(ID_USER_PACKET_ENUM))
	stream.Write(( RakNet::MessageID )ID_USER_PACKET_ENUM);
	//再写入我们自己定义的消息类型
	stream.Write(msg_connect_server_return);
	//将数据写到stream中,第一个参数是要写入的数据字节数组,第二个是数据的位数
	stream.WriteBits((unsigned char *)&send_data , sizeof(connect_server_return) * 8);
	//将数据发送到指定的地方
	//第一个参数是要发送的bitstream对象
	//第二个参数是发送的优先级
	//第三个参数是发送的可靠性,这里使用RELIABLE_ORDERED,具体在PacketPriority.h枚举的PacketReliability中有具体说明
	//自己看api,这里设置成0
	//接收方地址
	//是否广播,注意下,要是用那个true,上一个参数就是不需要接受数据的地址
	net_peer->Send(&stream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, net_packet->systemAddress, false);
}



最后看看Main.cpp:

#include "NetMgr.h"
#include <iostream>

int main()
{
	NetMgr *p = new NetMgr;

	p->init_net_work();

	while (true)
	{
		p->net_work_update();
	}
	
	delete p;
	p = NULL;

	return 0;
}


这里server 和client通信传输的数据我们使用结构体来保存,传输的消息类型在枚举变量中列出。下面这两个头文件client和server工程都都需要。
看看消息类型的头文件:ServerClientMessage.h

#ifndef __SERVERCLIENTMESSAGE_H__
#define __SERVERCLIENTMESSAGE_H__

enum message_type
{
	msg_connect_server = 101,
	msg_connect_server_return,
};

#endif


保存数据的结构体文件:ServerClientStruct.h

#ifndef __SERVERCLIENTSTRUCT_H__
#define  __SERVERCLIENTSTRUCT_H__

#pragma once


#pragma pack(push, 1)
struct connect_server
{
	char content[500];
};
#pragma pack(pop)

#pragma pack(push, 1)
struct connect_server_return
{
	char content_return[1000];
};
#pragma pack(pop)


#endif


上面的是server端,来看看client端。

NetMgr.h

#ifndef __NETSERVERMGR_H__
#define __NETSERVERMGR_H__

#include <string>

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

	//初始化网络
	void init_net_work() const;

	//接收网络消息
	void net_work_update();

	//发送数据给server
	void send_data_to_server() const;

	//接收server的数据
	void receive_data_from_server(const unsigned char *msg_data) const;

private:
	//处理server发送过来的消息
	void _process_server_message() const;

public:
	std::string server_ip;
	unsigned short server_port;
};

#endif


NetMrg.cpp

#include "NetServerMgr.h"
#include "../../Source/RakPeerInterface.h"
#include "../../Source/RakNetTypes.h"
#include "../../Source/MessageIdentifiers.h"
#include "../../Source/BitStream.h"

#include <iostream>
#include "ServerClientMessage.h"
#include "ServerClientStruct.h"


RakNet::RakPeerInterface *net_peer;			//网络消息
RakNet::Packet *net_packet;					//网络消息包
RakNet::SystemAddress server_address;		//server 地址

NetServerMgr::NetServerMgr()
	: server_ip("localhost")
	, server_port(10001)
{

}

NetServerMgr::~NetServerMgr()
{

}

//初始化网络
void NetServerMgr::init_net_work() const
{
	//初始化网络消息
	net_peer = RakNet::RakPeerInterface::GetInstance();
	if (NULL == net_peer)
	{
		std::cout << "GetInstance failed" << std::endl;
		return;
	}

	int start_up = net_peer->Startup(1, &RakNet::SocketDescriptor(), 1);
	if (start_up > 0)
	{
		std::cout << "Startup failed" << std::endl;
		return;
	}

	//Connect 连接server
	//successful 返回 CONNECTION_ATTEMPT_STARTED
	bool rs = (net_peer->Connect(server_ip.c_str(), server_port, NULL, 0, 0) == RakNet::CONNECTION_ATTEMPT_STARTED);
	if (!rs)
	{
		std::cout << "connect server failed" << std::endl;
		return;
	}
}

//接收网络消息
void NetServerMgr::net_work_update()
{
	for (net_packet = net_peer->Receive(); net_packet; net_peer->DeallocatePacket(net_packet), net_packet = net_peer->Receive())
	{
		switch (net_packet->data[0])
		{
		case ID_REMOTE_DISCONNECTION_NOTIFICATION:
			std::cout << "Another client has disconnected" << std::endl;
			break;
		case ID_REMOTE_CONNECTION_LOST:
			std::cout << "Another client has lost the connection" << std::endl;
			break;
		case ID_REMOTE_NEW_INCOMING_CONNECTION:
			std::cout << "Another client has connected" << std::endl;
			break;
		//client连接上server后会触发
		case ID_CONNECTION_REQUEST_ACCEPTED:
			std::cout << "Our connection request has been accepted for server" << std::endl;
			server_address = net_packet->systemAddress;
			this->send_data_to_server();
			break;
		case ID_NEW_INCOMING_CONNECTION:
			std::cout << "A connection is incoming  " << std::endl;
			break;
		case ID_NO_FREE_INCOMING_CONNECTIONS:
			std::cout << "The server is full" << std::endl;
			break;
		case ID_DISCONNECTION_NOTIFICATION:
			std::cout << "A client has disconnected" << std::endl;
			break;
		case ID_CONNECTION_LOST:
			std::cout << "A client lost the connection  " << std::endl;
			break;
		case ID_USER_PACKET_ENUM:
			_process_server_message();				//用户自定义消息入口
			break;
		default:
			std::cout << "Message with identifier %d has arrived" << net_packet->data[0] << std::endl;
			break;
		}
	}
}

// server 发送过来的消息
void NetServerMgr::_process_server_message() const
{
	int message_id = 0;
	RakNet::BitStream bs_in(net_packet->data, net_packet->length, false);
	bs_in.IgnoreBytes(sizeof(RakNet::MessageID));
	bs_in.Read(message_id);

	unsigned char *msg_data = net_packet->data;
	msg_data += sizeof(unsigned long) + sizeof(unsigned char);
	switch (message_id)
	{
	case msg_connect_server_return:
		this->receive_data_from_server(msg_data);
		break;

	default:
		break;
	}
}

// 向 server 发送数据
void NetServerMgr::send_data_to_server() const
{
	connect_server send_data;
	memset(&send_data, 0, sizeof(connect_server));
	sprintf_s(send_data.content, "%s", "hello, this is client send to server");

	std::cout << "send to server data is : " << send_data.content << std::endl;

	RakNet::BitStream stream;
	stream.Write( ( RakNet::MessageID )ID_USER_PACKET_ENUM );
	stream.Write( msg_connect_server );
	stream.WriteBits((unsigned char *)&send_data , sizeof(connect_server) * 8 );
	net_peer->Send( &stream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, server_address, false );
}

// 接收 server 回应的数据 并打印出来
void NetServerMgr::receive_data_from_server(const unsigned char *msg_data) const
{
	connect_server_return *receive_data = (connect_server_return *)msg_data;

	std::cout << "receive from server data is :" << receive_data->content_return << std::endl;
}

Main.cpp

#include "NetServerMgr.h"
#include <iostream>

int main()
{
	NetServerMgr *p = new NetServerMgr;

	p->init_net_work();

	while (true)
	{
		p->net_work_update();
	}
	
	delete p;
	p = NULL;

	return 0;
}


运行程序的时候,需要将RakNet_DLL_Debug_Win32.dll放到exe所在的目录下面。开启server,再开启client测试下:




由于server和client其实代码都差不过,注释就写了server的,感觉还是比较详细了,要是想深入了解,可以使用的时候直接查看里面的api文档。

发送数据的时候,先添加raknet的消息类型(就是ID_USER_PACKET_ENUM),再添加我们自定义的(这里是struct),最后添加实际数据。

接受的时候,要先偏移掉raknet的消息类型,这里其实一个char的大小,再偏移掉自定义的枚举消息类型,这里其实是整形,就可以获得实际的传输数据。发送的封包和解包是一个逆向的过程。

猜你喜欢

转载自blog.csdn.net/u014489596/article/details/42319409
今日推荐