RakNet网络框架简单入门
2015-01-01 15:47
537 查看
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
再看看NetMgr.cpp:
最后看看Main.cpp:
这里server 和client通信传输的数据我们使用结构体来保存,传输的消息类型在枚举变量中列出。下面这两个头文件client和server工程都都需要。
看看消息类型的头文件:ServerClientMessage.h
保存数据的结构体文件:ServerClientStruct.h
上面的是server端,来看看client端。
NetMgr.h
NetMrg.cpp
Main.cpp
运行程序的时候,需要将RakNet_DLL_Debug_Win32.dll放到exe所在的目录下面。开启server,再开启client测试下:
由于server和client其实代码都差不过,注释就写了server的,感觉还是比较详细了,要是想深入了解,可以使用的时候直接查看里面的api文档。
发送数据的时候,先添加raknet的消息类型(就是ID_USER_PACKET_ENUM),再添加我们自定义的(这里是struct),最后添加实际数据。
接受的时候,要先偏移掉raknet的消息类型,这里其实一个char的大小,再偏移掉自定义的枚举消息类型,这里其实是整形,就可以获得实际的传输数据。发送的封包和解包是一个逆向的过程。
由于目前的手游项目的网络框架使用的就是它,花了点时间看了看,这里写个比较简单的例子来说明该如何使用它。要是在项目中使用,只需要扩展这个代码即可。
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的大小,再偏移掉自定义的枚举消息类型,这里其实是整形,就可以获得实际的传输数据。发送的封包和解包是一个逆向的过程。
相关文章推荐
- 一个简单的linux下网络程序实例-网络编程入门
- .net下的AjaxPro框架,简单入门
- acl_cpp 简单实用、功能强大的C++网络应用与服务器框架库发布
- 简单的三层框架以及使用dbutils进行数据库操作(入门)
- 简单的三层框架以及使用dbutils进行数据库操作(入门)
- VB Api简单入门(5) -3721网络实名SDK的开发例子
- 入门级最简单的python网络编程源码socket,仅供参考
- Flex快速入门系列之三:Parsley框架简单使用(IOC以及message机制)
- 简单的前端js+ajax 购物车框架(入门篇)
- Android 网络通信框架Volley的简单使用
- Linux网络编程-简单的客户端和服务器通讯程序开发入门(2)
- Socket网络框架 MINA 入门
- 一个简单的linux下网络程序实例-网络编程入门 收藏
- Java实现的网络爬虫程序,简单易懂无框架(我的网络大作业)
- 简单网络系统框架Gearman
- 流行Java开源数据库框架项目 - Hibernate快速简单入门
- Dojo QuickStart 快速入门教程 (4) 简单的测试框架
- 一个非常简单的.net网络通信框架XNetFramework(符源码与测试Demo)
- 游戏编程入门学习笔记20——网络篇——理清框架、获取字符串输入
- 最简单的CI框架入门示例--数据库取数据