C++操作Smgp协议的相关教程

news2024/9/19 10:35:55

SGIP是中国网通为实现短信业务而制定的一种通信协议,用于在短消息网关(SMG)和服务提供商(SP)之间、短消息网关(SMG)和短消息网关(SMG)之间通信。

  • Perl的IO::Async模块提供了一套简洁的异步IO编程模型。
  • SGIP 协议基于客户端/服务端模型工作。由客户端(短信应用,如手机,应用程序等)先和短信网关(SMG Short Message Gateway)建立起 TCP 长连接,并使用 SGIP 命令与SMG进行交互,实现短信的发送和接收。在SGIP协议中,无需同步等待响应就可以发送下一个指令,实现者可以根据自己的需要,实现同步、异步两种消息传输模式,满足不同场景下的性能要求。

时序图

连接成功,发送短信

连接成功,从SMGW接收到短信

 

协议帧介绍

image.png

SGIP Header

  • Message Length:长度为4字节,整个PDU的长度,包括Header和Body。
  • Command ID:长度为4字节,用于标识PDU的类型(例如,Login、Submit等)。
  • Sequence Number:长度为8字节,序列号,用来匹配请求和响应。

使用C++实现SMGP协议栈里的建立连接


├── CMakeLists.txt
├── examples
│   └── smgp_client_login_example.cpp
└── include
    └── sgipcpp
        ├── BoundAtomic.h
        ├── Client.h
        ├── Protocol.h
        └── impl
            ├── BoundAtomic.cpp
            ├── Client.cpp
            └── Protocol.cpp

  • CMakeLists.txt:用来生成Makefile和编译项目
  • examples:存放示例代码
    • smgp_client_login_example.cpp:存放Smgp的login样例
  • include/sgipcpp:包含所有的C++头文件和实现文件
    • BoundAtomic.h:递增工具类,用来生成SequenceId
    • Client.h:Smgp定义,负责与Smgp服务进行通信,例如建立连接、发送短信等
    • Protocol.h:存放PDU,编解码等
    • impl/BoundAtomic.cpp:BoundAtomic类的实现
    • impl/Client.cpp:Client类的实现
    • impl/Protocol.cpp:Protocol中相关函数的实现

实现SequenceId递增

SequenceId是从1到0x7FFFFFFF的值,使用**BoundAtomic**类实现递增:

头文件

#ifndef BOUNDATOMIC_H
#define BOUNDATOMIC_H

#include <atomic>
#include <cassert>

class BoundAtomic {
public:
    BoundAtomic(int min, int max);
    int next_val();

private:
    int min_;
    int max_;
    std::atomic<int> integer_;
};

#endif //BOUNDATOMIC_H

内容

#include "sgipcpp/BoundAtomic.h"

BoundAtomic::BoundAtomic(int min, int max) : min_(min), max_(max), integer_(min) {
    assert(min <= max);
}

int BoundAtomic::next_val() {
    int current = integer_.load();
    int next;
    do {
        next = current >= max_ ? min_ : current + 1;
    } while (!integer_.compare_exchange_strong(current, next));

    return next;
}

实现SMGP PDU以及编解码函数

在**Protocol.h**中定义SMGP PDU以及编解码函数:

头文件

#ifndef PROTOCOL_H
#define PROTOCOL_H

#include <cstdint>
#include <vector>

constexpr uint32_t SGIP_BIND = 0x00000001;
constexpr uint32_t SGIP_BIND_RESP = 0x80000001;
constexpr uint32_t SGIP_UNBIND = 0x00000002;
constexpr uint32_t SGIP_UNBIND_RESP = 0x80000002;
constexpr uint32_t SGIP_SUBMIT = 0x00000003;
constexpr uint32_t SGIP_SUBMIT_RESP = 0x80000003;
constexpr uint32_t SGIP_DELIVER = 0x00000004;
constexpr uint32_t SGIP_DELIVER_RESP = 0x80000004;
constexpr uint32_t SGIP_REPORT = 0x00000005;
constexpr uint32_t SGIP_REPORT_RESP = 0x80000005;
constexpr uint32_t SGIP_ADDSP = 0x00000006;
constexpr uint32_t SGIP_ADDSP_RESP = 0x80000006;
constexpr uint32_t SGIP_MODIFYSP = 0x00000007;
constexpr uint32_t SGIP_MODIFYSP_RESP = 0x80000007;
constexpr uint32_t SGIP_DELETESP = 0x00000008;
constexpr uint32_t SGIP_DELETESP_RESP = 0x80000008;
constexpr uint32_t SGIP_QUERYROUTE = 0x00000009;
constexpr uint32_t SGIP_QUERYROUTE_RESP = 0x80000009;
constexpr uint32_t SGIP_ADDTELESEG = 0x0000000A;
constexpr uint32_t SGIP_ADDTELESEG_RESP = 0x8000000A;
constexpr uint32_t SGIP_MODIFYTELESEG = 0x0000000B;
constexpr uint32_t SGIP_MODIFYTELESEG_RESP = 0x8000000B;
constexpr uint32_t SGIP_DELETETELESEG = 0x0000000C;
constexpr uint32_t SGIP_DELETETELESEG_RESP = 0x8000000C;
constexpr uint32_t SGIP_ADDSMG = 0x0000000D;
constexpr uint32_t SGIP_ADDSMG_RESP = 0x8000000D;
constexpr uint32_t SGIP_MODIFYSMG = 0x0000000E;
constexpr uint32_t SGIP_MODIFYSMG_RESP = 0x8000000E;
constexpr uint32_t SGIP_DELETESMG = 0x0000000F;
constexpr uint32_t SGIP_DELETESMG_RESP = 0x8000000F;
constexpr uint32_t SGIP_CHECKUSER = 0x00000010;
constexpr uint32_t SGIP_CHECKUSER_RESP = 0x80000010;
constexpr uint32_t SGIP_USERRPT = 0x00000011;
constexpr uint32_t SGIP_USERRPT_RESP = 0x80000011;
constexpr uint32_t SGIP_TRACE = 0x00001000;
constexpr uint32_t SGIP_TRACE_RESP = 0x80001000;

struct Header {
    uint32_t total_length;
    uint32_t command_id;
    uint64_t sequence_number;
};

struct Bind {
    char login_type;
    char login_name[16];
    char login_passwd[16];
    char reserve[8];
};

struct BindResp {
    char result;
    char reserve[8];
};

struct Pdu {
    Header header;
    union {
        Bind bind;
        BindResp bind_resp;
    };
};

size_t lengthBind();
std::vector<uint8_t> encodePdu(const Pdu& pdu);
Pdu decodePdu(const std::vector<uint8_t>& buffer);

#endif //PROTOCOL_H

内容

#include "sgipcpp/Protocol.h"
#include <cstring>
#include <ostream>
#include <stdexcept>
#include <sys/_endian.h>

size_t lengthBind(const Bind& bind) {
    return 1 + 16 + 16 + 8;
}

void encodeBind(const Bind& bind, std::vector<uint8_t>& buffer) {
    size_t offset = 16;

    buffer[offset++] = bind.login_type;
    std::memcpy(buffer.data() + offset, bind.login_name, 16);
    offset += 16;
    std::memcpy(buffer.data() + offset, bind.login_passwd, 16);
    offset += 16;
    std::memcpy(buffer.data() + offset, bind.reserve, 8);
}

BindResp decodeBindResp(const std::vector<uint8_t>& buffer) {
    BindResp bindResp;

    size_t offset = 0;

    offset += sizeof(uint32_t);
    offset += sizeof(uint32_t);

    bindResp.result = buffer[offset++];
    std::memcpy(bindResp.reserve, buffer.data() + offset, sizeof(bindResp.reserve));

    return bindResp;
}

std::vector<uint8_t> encodePdu(const Pdu& pdu) {
    size_t body_length;
    switch (pdu.header.command_id) {
        case SGIP_BIND:
            body_length = lengthBind(pdu.bind);
            break;
        default:
            throw std::runtime_error("Unsupported command ID for encoding");
    }

    std::vector<uint8_t> buffer(body_length + 16);
    uint32_t total_length = htonl(body_length + 16);
    std::memcpy(buffer.data(), &total_length, 4);

    uint32_t command_id = htonl(pdu.header.command_id);
    std::memcpy(buffer.data() + 4, &command_id, 4);

    uint32_t sequence_number = htonl(pdu.header.sequence_number);
    std::memcpy(buffer.data() + 8, &sequence_number, 8);

    switch (pdu.header.command_id) {
        case SGIP_BIND:
            encodeBind(pdu.bind, buffer);
        break;
        default:
            throw std::runtime_error("Unsupported command ID for encoding");
    }

    return buffer;
}

Pdu decodePdu(const std::vector<uint8_t>& buffer) {
    Pdu pdu;

    uint32_t command_id;
    std::memcpy(&command_id, buffer.data(), 4);
    pdu.header.command_id = ntohl(command_id);

    uint64_t sequence_number;
    std::memcpy(&sequence_number, buffer.data() + 8, 8);
    pdu.header.sequence_number = ntohl(sequence_number);

    switch (pdu.header.command_id) {
        case SGIP_BIND_RESP:
            pdu.bind_resp = decodeBindResp(buffer);
            break;
        default:
            throw std::runtime_error("Unsupported command ID for decoding");
    }

    return pdu;
}

实现客户端和登录方法

在**Client**中实现客户端和登录方法:

头文件

#ifndef CLIENT_H
#define CLIENT_H

#include "BoundAtomic.h"
#include "Protocol.h"
#include "asio.hpp"
#include <string>

class Client {
public:
    Client(const std::string& host, uint16_t port);
    ~Client();

    void connect();
    BindResp bind(const Bind& bind_request);
    void close();

private:
    std::string host_;
    uint16_t port_;
    asio::io_context io_context_;
    asio::ip::tcp::socket socket_;
    BoundAtomic* sequence_number_;

    void send(const std::vector<uint8_t>& data);
    std::vector<uint8_t> receive(size_t length);
};

#endif //CLIENT_H

内容

#include "sgipcpp/Client.h"
#include <iostream>

Client::Client(const std::string& host, uint16_t port)
    : host_(host), port_(port), socket_(io_context_) {
    sequence_number_ = new BoundAtomic(1, 0x7FFFFFFF);
}

Client::~Client() {
    close();
    delete sequence_number_;
}

void Client::connect() {
    asio::ip::tcp::resolver resolver(io_context_);
    asio::connect(socket_, resolver.resolve(host_, std::to_string(port_)));
}

BindResp Client::bind(const Bind& bind_request) {
    Pdu pdu;
    pdu.header.total_length = sizeof(Bind) + sizeof(Header);
    pdu.header.command_id = SGIP_BIND;
    pdu.header.sequence_number = sequence_number_->next_val();
    pdu.bind = bind_request;

    send(encodePdu(pdu));

    auto length_data = receive(4);
    uint32_t total_length = ntohl(*reinterpret_cast<uint32_t*>(length_data.data()));

    auto resp_data = receive(total_length - 4);
    Pdu resp_pdu = decodePdu(resp_data);
    return resp_pdu.bind_resp;
}

void Client::close() {
    socket_.close();
}

void Client::send(const std::vector<uint8_t>& data) {
    asio::write(socket_, asio::buffer(data));
}

std::vector<uint8_t> Client::receive(size_t length) {
    std::vector<uint8_t> buffer(length);
    asio::read(socket_, asio::buffer(buffer));
    return buffer;
}

运行example,验证连接成功

#include "sgipcpp/Client.h"
#include <iostream>

int main() {
    try {
        Client client("127.0.0.1", 8801);

        client.connect();
        std::cout << "Connected to the server." << std::endl;

        Bind bindRequest;
        bindRequest.login_type = 1;
        std::string login_name = "1234567890123456";
        std::string login_password = "1234567890123456";
        std::string reserve = "12345678";
        std::copy(login_name.begin(), login_name.end(), bindRequest.login_name);
        std::copy(login_password.begin(), login_password.end(), bindRequest.login_passwd);
        std::copy(reserve.begin(), reserve.end(), bindRequest.reserve);

        BindResp response = client.bind(bindRequest);
        if (response.result == 0) {
            std::cout << "Login successful." << std::endl;
        } else {
            std::cout << "Login failed with result code: " << static_cast<int>(response.result) << std::endl;
        }

        client.close();
        std::cout << "Connection closed." << std::endl;

    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }

    return 0;
}

image.png

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1947746.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SAP PP学习笔记31 - 计划运行的步骤2 - Scheduling(日程计算),BOM Explosion(BOM展开)

上一章讲了计划运行的5大步骤中的前两步&#xff0c;计算净需求和计算批量大小。 SAP PP学习笔记30 - 计划运行的步骤1 - Net requirements calculation 计算净需求(主要讲了安全库存要素)&#xff0c;Lot-size calculation 计算批量大小-CSDN博客 本章继续讲计划运行的后面几…

Q380 O(1)时间获取插入删除元素

思路 insert 其实用得到search&#xff0c;remove也是&#xff0c;当时o(1)想到的是hash set&#xff0c;但是对于random取,随机数相当于获得的是index,根据index获取元素 Set 数据结构不符合。 随机获取应该是数组&#xff0c;但是数组搜索和删除不是o(1) 最后的思路是 用h…

怎么防止文件在通讯软件外泄

为了防止文件在通讯软件外泄&#xff0c;企业可以采取一系列综合性的措施来加强管理和防护。 1. 使用高级别软件 企业级聊天软件&#xff1a;使用企业级聊天软件&#xff0c;这类软件通常具备更强大的安全管理功能&#xff0c;如文件传输加密、访问权限控制、审计日志记录等。…

【C++】深度解析:用 C++ 模拟实现 list 类,探索其底层实现细节

目录 list介绍 list模拟实现 list 节点类 list 的迭代器 定义 构造函数 解引用 operator前置和--与后置和-- operator与operator! list 类 构造函数 begin()和end() 拷贝构造 erase() clear() 析构函数 insert push_back 和 push_front pop_back 和 pop_front…

CTF-pwn-虚拟化-vmmware 前置

文章目录 参考vmware逃逸简介虚拟机和主机通信机制(guest to host)共享内存&#xff08;弃用&#xff09;backdoor机制Message_Send和Message_RecvGuestRPC实例RpcOutSendOneRawWork实例 vmware-rpctool info-get guestinfo.ip各个步骤对应的backdoor操作Open RPC channelSend …

数据结构初阶(c语言)-双向链表

这里首先纠正上篇文章一个错误&#xff0c;链表的一个有效数据点应该称为结点而不是节点。 一&#xff0c;双向链表的概念与结构 1.1概念与结构示意图 我们所说的双向链表全称为带头双向循环链表&#xff0c;也就是说此链表带有哨兵位结点(不存放任何数据的结点&#xff0c;且…

Oauth2协议的四种模式

B站视频 概念 Oauth2.0&#xff08;Open Authorization&#xff09; 一个关于授权的开放网络标准 允许用户授权第三方应用访问用户存储在其他服务提供者上的信息 不需要将用户名和密码提供给第三方应用 Oauth2中的各个角色 授权码模式 第一步 获取授权码 以上流程中的授…

产品经理NPDP好考吗?

NPDP是新产品开发专业人员的资格认证&#xff0c;对于希望在产品管理领域取得认可的专业人士来说&#xff0c;NPDP认证是一项重要的资格。 那么&#xff0c;产品经理考取NPDP资格认证究竟难不难呢&#xff1f; 首先&#xff0c;NPDP考试的难易程度取决于考生的背景和准备情况…

通信类IEEE会议——第四届通信技术与信息科技国际学术会议(ICCTIT 2024)

[IEEE 独立出版&#xff0c;中山大学主办&#xff0c;往届均已见刊检索] 第四届通信技术与信息科技国际学术会议&#xff08;ICCTIT 2024&#xff09; 2024 4th International Conference on Communication Technology and Information Technology 重要信息 大会官网&#xf…

C#调用OpenCvSharp实现图像的角点检测

角点检测用于获取图像特征&#xff0c;以支撑运动检测、目标识别、图像匹配等方面的应用。常用的角点检测算法包括Kitchen-Rosenfeld算法、Harris算法、KLT算法、SUSAN算法等&#xff0c;本文学习并测试Harris角点检测算法。   关于Harris算法的数学原理请见参考文献1的第18、…

解开基于大模型的Text2SQL的神秘面纱

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

【Unity国产化信创平台】虚拟机VMware Workstation Pro虚拟机下载安装

目录 一、虚拟机软件VMware Workstation Pro下载 二、虚拟机安装流程 1.傻瓜式安装 2.是否自动安装WHP 一、虚拟机软件VMware Workstation Pro下载 https://www.vmware.com/products/desktop-hypervisor/workstation-and-fusion 官网各种访问出错&#xff0c;下载界面总是…

linux禁用root

linux禁用root 1. 禁止普通用户切换到root1.1 sudo -i和sudo -s的区别1.2 sudo -i和直接登录root账号的区别1.3 禁止sudo -i切换root1.4 禁止su - root切换root 2. 禁止root远程登录2.1 ssh禁止root登录2.2 禁止远程桌面登录 本文主要介绍&#xff1a; 如何禁止普通用户切换到r…

划重点!PMP报考条件、报考步骤、考试内容、适合人群

参加PMP认证的好处&#xff0c;可以从几个方面来认识&#xff1a; 一、参加PMP认证与考试的过程&#xff0c;同时是一个系统学习和巩固项目管理知识的过程 二、参加PMP认证&#xff0c;您可以获得由PMI颁发的PMP证书 而拥有PMP认证表示你已经成为一个项目管理方面的专业人员…

基于微信小程序的健康饮食系统/健康饮食管理系统

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的小程序应运而生&#xff0c;各行各业相继进入信息管理时代&…

ST Stellar-E SR5E1 22KW OBC combo 3KW DC-DC汽车充电器解决方案

对于全球的环境保护意识抬头&#xff0c;全球的汽车产业慢慢步入电动化的时代&#xff0c;以减少碳排放。整车系统主要是由电池、电驱、电控的三电所构成&#xff0c;其中电池系统是整车的动力来源&#xff0c;而对电池充电的OBC系统更甚重要。一具高度安全性且高效的OBC系统&a…

a bean of type ‘org.redisson.api.RedissonClient Springboot 集成Redisson 配置

Springboot 集成 在导入redisson坐标之后 <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.17.5</version></dependency> 使用注解注入示例 Resource private RedissonClient re…

QML鼠标右键菜单

原理非常简单&#xff0c;先写个Menu&#xff0c;通过MouseArea来填充需要右键菜单的区域。然后在onClicked信号中&#xff0c;让Menu的x、y等于当前鼠标的x、y&#xff0c;最后用调用Menu的open()方法将菜单在该位置显示出来。 import QtQuick import QtQuick.Controls.Mater…

【JDY-10M】蓝牙MESH

目录 一、实物图 二、原理图 引脚定义 三、简介 基本原理 产品特点 产品应用范围 出厂默认配置 应用 1&#xff0c;可直接与手机进行连接&#xff0c;进行数据的传输和控制 2&#xff0c;可蓝牙与蓝牙之间进行连接&#xff0c;可进行MESH组网 四、尺寸 五、注意 源文件下载 可访…

基于Jeecgboot3.6.3的vue3版本的流程ProcessViewer的修改

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、因为之前ProcessViewer是vue2的组件版本&#xff0c;平时显示也还正常&#xff0c;但在历史记录的时候老是出现下面的问题。 就是第一次进去在panel点击流程图的时候不会出现&#xff0…