1. 概述
《RAKENT创建UDP实例,RAKNET简单通讯实例》是介绍Raknet的第一个例子,相当于学习任何一门编程语言所使用的“上帝的粒子”Helloworld一样,它能实时传输任何您想通讯的数据。本文的代码基于该篇文章,建议先阅读这篇文章。
但是你会发现它发送了一个字符串而已,在实际的游戏项目可没有那么好的事,诸多的结构体搞得是头昏脑胀,那么Raknet有没提供一种快捷的方式让我们管理自己的游戏应用中所需要用到的各类繁杂的结构体呢?
答案是有的,那就是Structure和Bistream的概念。
下面我们通过一个小例子介绍它们的使用方法,这个例子是对所有游戏玩家广播某个BOSS的当前位置(坐标x、y、z)
2. 结构体数据包
2.1 定义结构体
myPackets.h
#ifndef MYPACKETS_H_INCLUDED
#define MYPACKETS_H_INCLUDED
#pragma pack(push, 1)
struct structWithoutTimestamp
{
unsigned char typeId; // 自定义的包类型
// 这里是您所需要的数据
// 比如这个例子所需要用到
float x, y, z;
};
struct structWithTimestamp
{
unsigned char useTimeStamp; // 赋值ID_TIMESTAMP,告诉Raknet这是一个含时间戳结构体
RakNet::Time timeStamp; // ok, 这里自然就是时间戳啦
unsigned char typeId; // 自定义的包类型
// 这里是您所需要的数据
// 比如这个例子所需要用到
float x, y, z;
// 测试中文字符串
char ch[128];
};
#pragma pack(pop)
#endif // MYPACKETS_H_INCLUDED
我们会想平常一样定义一些结构体,根据您自己的需要定义,有一点点的不同就是需要包含一些Raknet特有的结构数据。
2.2 发送结构体
我们在服务端监听程序代码段里面补充,这次让客户成功连接服务器的时候发送上面定义的带时间戳的结构体给客户端。
case ID_NEW_INCOMING_CONNECTION:
// 新连接
printf("ID_NEW_INCOMING_CONNECTION from %s with GUID %s\n", p->systemAddress.ToString(true), p->guid.ToString());
clientID=p->systemAddress; // Record the player ID of the client
// 在新连接的时候尝试给客户端发送一个数据包
structWithTimestamp swt;
swt.useTimeStamp = ID_TIMESTAMP;
swt.timeStamp = RakNet::GetTime();
swt.typeId = 254;
swt.x = 100;
swt.y = 200;
swt.z = 3000;
strcpy(swt.ch, "中华人民共和国");
server->Send((char *)&swt, (int)sizeof(swt)+1, HIGH_PRIORITY, RELIABLE_ORDERED, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true);
break;
在服务端代码里面查找ID_NEW_INCOMING_CONNECTION,添加上面的代码,重新编译项目。
另外,记得添加头部定义哦~
#include "GetTime.h" // GetTime函数
#include "myPackets.h"
2.3 接收结构体
我们就简单的在客户端接受服务器发来的结构体数据包啦~
case 254:
printf("get it...\n");
getPosition(p);
break;
default:
// It's a client, so just show the message
printf("%s\n", p->data);
break;
Define Function getPositoin()
void getPosition(RakNet::Packet* p) {
structWithTimestamp *swt = (structWithTimestamp *) p->data;
printf("x: %f, y:%f, z:%f\n", swt->x, swt->y, swt->z);
printf("%s\n", swt->ch);
}
2.4 测试效果
未完待续。。。(待完善部分是如何使用BitStream发送无规则数据包)
3. BitStream字节流数据包
您可以先阅读官方的文档:http://www.jenkinssoftware.com/raknet/manual/bitstreams.html
全英文能看懂最好,看不懂也不用纠结。先来介绍它是如何被使用的,看完例子会所有收获的。
3.1 写入字节流
#include "BitStream.h"
RakNet::BitStream bs;
bs.Write(...);
3.2 发送字节流
服务器使用BitStream发送数据包的例子
case ID_NEW_INCOMING_CONNECTION:
// 新连接
printf("ID_NEW_INCOMING_CONNECTION from %s with GUID %s\n", p->systemAddress.ToString(true), p->guid.ToString());
clientID=p->systemAddress; // Record the player ID of the client
// 在新连接的时候尝试给客户端发送一个数据包
/* 这一段是使用Struct发送数据包的代码
structWithTimestamp swt;
swt.useTimeStamp = ID_TIMESTAMP;
swt.timeStamp = RakNet::GetTime();
swt.typeId = 254;
swt.x = 1;
swt.y = 2;
swt.z = 3;
strcpy(swt.ch, "中华人民共和国");
server->Send((char *)&swt, (int)sizeof(swt)+1, HIGH_PRIORITY, RELIABLE_ORDERED, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true);
*/
{ // BitStream发送,此处的大括号只是为了GCC编译出现错误
RakNet::BitStream bs;
bs.Write((RakNet::MessageID)ID_TIMESTAMP); // 使用时间戳,useTimeStamp
bs.Write(RakNet::GetTime()); // 时间戳,timeStamp
bs.Write((unsigned char) 254); // taypeID
structOfBitStream sbs;
sbs.x = 100;
sbs.y = 200;
sbs.z = 3000;
strcpy(sbs.ch, "中华人民共和国万岁");
//
bs.Write(sbs.x);
bs.Write(sbs.y);
bs.Write(sbs.z);
bs.Write(sbs.ch);
server->Send(&bs, HIGH_PRIORITY, RELIABLE, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true);
}
break;
3.3 接收字节流
客户端也是使用BitStream解包,跟Struct的原理基本一致
void getPosition(RakNet::Packet* p) {
RakNet::MessageID useTimeStamp;
RakNet::Time timeStamp;
unsigned char typeId;
structOfBitStream sbs;
// RakNet::RakString ch;
RakNet::BitStream bs(p->data, p->length, false);
bs.Read(useTimeStamp);
bs.Read(timeStamp);
bs.Read(typeId);
bs.Read(sbs.x);
bs.Read(sbs.y);
bs.Read(sbs.z);
bs.Read(sbs.ch);
printf("x: %f, y:%f, z:%f\n", sbs.x, sbs.y, sbs.z);
printf("%s\n", sbs.ch);
/* 此处是使用Struct的例子,见2.3
structWithTimestamp *swt = (structWithTimestamp *) p->data;
printf("x: %f, y:%f, z:%f\n", swt->x, swt->y, swt->z);
printf("%s\n", swt->ch);
*/
}
2.4 测试效果
您可以修改并重新发布本文,如果您能留下本文的参考连结,万分谢谢!
如果您对本文存在疑问,欢迎留言或者直接对本文评论,我会在看到的第一时间回复您。