RakNet网络框架简单入门

来源:互联网 发布:萌娘百科知乎 编辑:程序博客网 时间:2024/06/16 03:45

        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;//发送数据给clientvoid 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 数据 并回应clientvoid 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();//发送数据给servervoid 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_STARTEDbool 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的大小,再偏移掉自定义的枚举消息类型,这里其实是整形,就可以获得实际的传输数据。发送的封包和解包是一个逆向的过程。

0 0
原创粉丝点击