Files
MessengerSdkDoc/doc/entry.md
2024-03-26 18:51:30 +08:00

7.5 KiB
Raw Blame History

快速入门

Messenger SDK一般是一个如下格式的压缩包

/libs
    /arm64-v8a
    /x86-64
    /Windows
    /Linux
    ...
/include
...

其中libs下放置的是一些静态连接库包含Android、Linux以及Windows。你需要将这些静态链接库链接到你的项目中 并且在你项目的头文件搜索路径中添加 include 文件夹。

然后你将可以使用Messenger的API了。

使用方法

Messenger SDK的主要class是 ling::Messenger ,它提供了服务器全部API的抽象。
它由ling::Client派生而来ling::Client实现了服务器的通讯协议包括STL握手过程。

ling::Messenger以及ling::Client都具有ling前缀在C++中,这叫命名空间(namespace)。SDK内全部的class都在ling命名空间下。

ling::Messenger将是你接触最多的class。
它有两个构造函数:

namespace ling{
    class Messenger : public Client {
    public:
         /**
         * 创建一个Messenger实例创建实例时即连接到Server。
         *
         * @param host              服务器host
         * @param port              端口号
         * @param projectId         项目id
         * @throw ConnectException  在连接失败时
         */
        Messenger(std::string host, int port, uint64_t projectId);
        
        /**
         * 此重载相比上面的定义额外需要一个deviceUid参数。
         * 这是设备识别码,上面的实现将会自动读取设备识别码,但是可能不会唯一
         * 扛攻击的能力较弱如果设备拥有root权限则很可能被修改
         * 所以,如果你有更加适合你需求的方案识别设备,将它作为第四个参数传递进来!
         *
         * @param host              服务器host
         * @param port              端口号
         * @param projectId         项目id
         * @param deviceUid         设备识别码
         * @throw ConnectException  在连接失败时
         */
        Messenger(std::string host, int port, uint64_t projectId, std::string deviceUid);
    };
}

以后您会经常看到像这样的声明,提前熟悉它!

在Messenger创建完毕后你可以通过它来和服务器交互。

范例

//别忘了导入定义!
#include <Messenger.h>
#include <iostream>
#include <memory>

//因为Messenger每次创建都会连接到服务器
//所以你不能每次和服务器交互都创建一个实例,而是必须复用!
//此处使用一个智能指针来管理Messenger的实例
//手动将没有初始值的指针赋值为nullptr这是一个良好的编程习惯。
static std::shared_ptr<ling::Messenger> api = nullptr;

int main(void){
    try{
        //这里使用make_shared自动构造智能指针
        ::api = std::make_shared<ling::Messenger>("127.0.0.1",2019,1);
        //在此时已经成功连接到服务器,可以进行交互。
        //注意,交互之前必须进行设备注册,这个后面的章节在说。
    } catch (const ling::ConnectException & e){
        //还记得上面的声明中提到连接失败时会抛出ConnectException吗
        std::cerr << "连接服务器失败" << std::endl;
    }
}
    

得到ling::Messenger的实例之后你可以通过它来调用Messenger的API。
例如,像这样来向服务器内注册当前设备。


#include <filesystem>

std::shared_ptr<ling::Messenger> api = ...

std::filesystem::path filePath = "/path/to/your/flag";
try{
    if (!std::filesystem::exists(filePath)) {
        ::api->createDevice([](std::shared_ptr<ServerDataSuper> data)->void{
            //此处的代码将会在服务器响应createDevice时被执行
            if(data->isSuccess()){
                //注册设备成功,则在磁盘上留下一个标记,下次启动时不再注册
                std::ofstream outputFile(filePath);
                outputFile.close();
                //然后可以调用其他api了。
            } else {
                //服务器拒绝注册请求,可能是因为一些异常问题导致的,一般不会出现。
                //为了程序健壮性,此处需要记录日志,这里仅仅打印一下。
                std::cout << data->getMessage() << std::endl;
            }
        });
    }
} catch (const ling::SendException & e){
    //所有需要和服务器交换数据的api都会在网络断开时抛出此异常
    //具体信息请查看异常处理一节。
    //有无需捕获异常的优雅方法,将在后续章节中介绍。
}

发生了什么?

上面的代码可以简化成如下这样


::api->createDevice([](std::shared_ptr<ServerDataSuper> data)->void{

});

简单来说就是调用了全局命名空间中api对象的createDevice成员方法并将一个lambda表达式作为参数传递给了它。

lambda即匿名函数(Anonymous Function)。

查看createDevice方法的定义

void createDevice(const std::function<void(std::shared_ptr<ServerDataSuper>)> &call);

可以看到它需要一个std::function类型的参数。std::function是C++可调用对象的包装,它可以是一个具名函数 一个lambda表达式也可以是一个class的成员方法一个仿函数等等。一切可调用对象都可以使用std::function来包装。

参数中紧跟在std::function后面的 <void(std::shared_ptr>)> 是这个std::function的模板参数, 表示此std::function包装的可调用对象没有返回值,并且只需要一个 std::shared_ptr 类型的参数。

前面已经介绍过std::shared_ptr了,它同样是一个模板类型,尖括号中的ServerDataSuper是此智能指针管理对象的指针。

ServerDataSuper是Messenger服务器响应数据的封装类型是所有响应数据的基类。换句话说 ,所有包装服务器响应数据的类型,都直接或间接的派生自ServerDataSuper

例如,如下是服务器对登陆账号请求的响应信息包装类型的声明


namespace ling {
    class LoginUserData : public ServerDataSuper {
    };   
}

可以看到它直接从ServerDataSuper派生这意味着你可以在任何服务器响应类型中调用ServerDataSuper中的方法它们是公共的。

对ServerDataSuper的介绍将在后续章节继续。

回到createDevice。

将上述简化代码继续简化,可以变成下面的样子。


/// 这段代码和上面简化后的代码是等价的!

void createDeviceResponse(std::shared_ptr<ServerDataSuper> data){
    //等到服务器响应后Messenger才会调用这个函数。
    //注意Messenger将在单独的线程中调用此函数
    //Messenger将采取一定的策略管理线程无论如何你都不应该在此类回调函数中执行需要消耗大量时间的操作。
}

::api->createDevice(createDeviceResponse);
//调用后将会立即返回,继续向下执行

createDeviceResponse的定义等价与上面代码中的lambda表达式。

Messenger SDK的API是同服务器异步通信的也就是说createDevice会立即返回并继续向下执行。
等服务器响应createDevice时Messenger SDK将会调用参数中传递进去的可调用对象 在这里是createDeviceResponse并构造ServerDataSuper对象作为参数。

这都是C++的基础语法。请百度 回调函数(callback)