您的位置:首页 > 理论基础 > 计算机网络

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
#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的大小,再偏移掉自定义的枚举消息类型,这里其实是整形,就可以获得实际的传输数据。发送的封包和解包是一个逆向的过程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: