💻 C++ | Rapidjson 的基本用法

本文主要描述了在 Cocos2dx 中,使用 Rapidjson这一Json解析库在完成网络请求。

🚀 代码: Github

参考资料

Rapidjson 官方文档

网络请求

这一次的作业使用 Cocos2dx 内置的HttpClient 网络库还有rapidjsonJSON 库

引入头文件

首先,我们需要引入对应的头文件

1
2
3
4
5
6
#include "network/HttpClient.h"
#include "json/rapidjson.h"
#include "json/document.h"
#include "json/writer.h"
#include "json/stringbuffer.h"
using namespace cocos2d::network;

网络请求

下面使一个简单的GET请求的实现

1
2
3
4
5
6
HttpRequest* request = new HttpRequest();
request->setRequestType(HttpRequest::Type::GET);
request->setUrl("http://127.0.0.1:8000/users");
request->setResponseCallback(CC_CALLBACK_2(LoginRegisterScene::onHttpRegisterComplete, this));
cocos2d::network::HttpClient::getInstance()->send(request);
request->release();

设置好一系列的类型、URL 等参数之后,设置好回调函数,就可以将这个请求加入请求队列中。

然后是回调函数:

1
void LoginRegisterScene::onHttpRegisterComplete(HttpClient *sender, HttpResponse *response);

回调函数会在这次请求收到返回值的时候被调用

JSON 的构建

对于大多数的请求,我们都需要提交一个JSON信息给服务器

这里我们使用rapidjson 来构建 JSON 字符串,将 DOM 形式的数据转换为字符串然后发送给服务器

1
2
3
4
5
6
7
8
9
10
11
rapidjson::Document document;
document.SetObject();
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();

rapidjson::Value username;
username.SetString(rapidjson::StringRef(usernameInput->getString().c_str()));
document.AddMember("username", username, allocator);

StringBuffer buffer;
rapidjson::Writer<StringBuffer> writer(buffer);
document.Accept(writer);

根据官方文档,对于字符串的创建

RapidJSON 提供两个 String 的存储策略。

  1. copy-string: 分配缓冲区,然后把来源数据复制至它。
  2. const-string: 简单地储存字符串的指针。

copy-string总是安全的,因为它拥有数据的克隆。const-string可用于存储字符串字面量。

由于这里的数据是从对话框中获取的

需要通过StringRef创建一个copy-string,然后使用SetString方法把这个数据传入一个Value里面,再通过AddMember添加到Document

当然,RapidJSON 还支持很多形式的数据,具体可以查看官方文档

到这里,我们以及获得了一个具有完整数据的DOM形式的JSON数据结构,接下就就是要输出,这里使用流来输出。

StringBuffer 是一个简单的输出流。它分配一个内存缓冲区,供写入整个 JSON。

使用 GetString() 来获取该缓冲区,然后添加到网络请求中:

1
request->setRequestData(buffer.GetString(), buffer.GetSize());

JSON 的解析

收到服务器的请求返回之后,就需要解析 JSON 信息了。

1
2
3
4
std::vector<char> *buffer = response->getResponseData();
std::string str(buffer->begin(), buffer->end());
rapidjson::Document d;
d.Parse<0>(str.c_str());

解析成Document之后,还需要使用HasParseError判断是否成功解析,以及是否存在指定的对象,然后才进行下一步的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (d.HasParseError()) {
messageBox->setString("Can't parse JSON from server.");
return;
}
if (d.IsObject() && d.HasMember("status")) {
if (d["status"].GetBool() == true) {
std::string text("Register Success.\n");
text.append(d["msg"].GetString());
messageBox->setString(text);
}
else {
std::string text("Register Failed.\n");
text.append(d["msg"].GetString());
messageBox->setString(text);
}
}
else {
messageBox->setString("Can't parse JSON from server.");
}

解析 JSON 的大多数方法和C#中十分类似,这里就不详细说明了。

启用 Cookies

1
cocos2d::network::HttpClient::getInstance()->enableCookies(nullptr);

只需要一句话就可以启用 Cookies 了。

Session 的作用

由于 HTTP 是无状态的,因此服务器需要一个手段去识别不同的用户,其中最常用的机制就是Session

Session 是存储服务器上的,一般都是存在内存中,一些大型的服务端还会做持久化处理。这个 session 存储着用户的一些状态和信息,比如说是否登陆,用户 ID 等信息。既然说他是存储在服务端的,那么就需要一个方法是匹配客户端和服务端的信息,一般都是使用Cookies来实现的(这里顺便提一下,为了避免 XSS 攻击,通常会把这个 cookies 设置为httpOnly,即不能被该网站的脚本查看和修改,只能被浏览器和服务器查看和修改,但是这只是一个协议,对于自己实现协议的客户端并没有约束)。

这个 cookies 的用途就在于跟踪会话,在 cookies 中存储一个唯一的随机字符串,然后在服务端也存储一个类似于哈希表的数据结构,根据这个随机字符串寻找对应会话的信息,就可以使得无状态的 HTTP 实现有状态的会话。对于一些需要用户权限或者数据的请求,可以从 session 中获取用户信息,可以有效阻止非法访问。

遇到的问题

基本上没有遇到什么大问题。

在使用rapidjson库的时候,由于对这个库不太熟悉,自己去直接尝试传入字符串,发现参数总是不合法,后来查阅了官方文档之后才找到了正确的做法

根据官方文档,对于字符串的创建

RapidJSON 提供两个 String 的存储策略。

  1. copy-string: 分配缓冲区,然后把来源数据复制至它。
  2. const-string: 简单地储存字符串的指针。

copy-string总是安全的,因为它拥有数据的克隆。const-string可用于存储字符串字面量。

由于这里的数据是从对话框中获取的

需要通过StringRef创建一个copy-string,然后使用SetString方法把这个数据传入一个Value里面,再通过AddMember添加到Document

思考与总结

​ 这次的作业只有网络请求这个,虽然没有什么绚丽的画面,但是在游戏之中是十分重要的,即使是单机游戏有时候也需要联网更新的,对于网络游戏,对于这些请求就更加依赖了。不过对于一些网络游戏,一般都是直接使用 TCP 连接的,这个比 HTTP 连接更加适合于游戏客户端于服务器的连接。如果是一些实时的游戏,可能就需要 UDP 加上自己设计防丢包的协议来实现了(我刚刚用golang写的一个关于帧同步的游戏服务器就是采用 UDP+队列传输加上回传确认来实现的,性能还是不错的。

​ 回到这次作业上来,这是使用 HTTP 于网络通信,其中比较常用的数据结构就是json了,大概是它可以和字符串之间轻易地互相转化吧。服务端接收到可以解析成类似于结构体的数据结构,访问就很舒服,服务端也可以返回JSON给客户端,客户端同样可以解析。然后就达到了传输数据的目的了。

​ 至于 Session 的作用,上面也说的差不多了,要做有用户状态的网络应用,不管是客户端或者是浏览器,这个都是必须的。根据 Session 来辨识用户,显示个性化信息或者用户的私有信息,或者是一些权限操作。

​ 啊,不知道说什么了,就这样吧

土豪通道
0%