最近跟新了许多代码,目前代码框架如下:
gsky
├── crypto
│ ├── pe.cc
│ ├── pe.hh
│ ├── pmd5.cc
│ └── pmd5.hh
├── gsky.cc
├── gsky.hh
├── log
│ ├── log.cc
│ ├── log.hh
│ ├── log_thread.cc
│ └── log_thread.hh
├── net
│ ├── channel.cc
│ ├── channel.hh
│ ├── epoll.cc
│ ├── epoll.hh
│ ├── eventloop.cc
│ ├── eventloop.hh
│ ├── eventloop_thread.cc
│ ├── eventloop_thread.hh
│ ├── eventloop_threadpool.cc
│ ├── eventloop_threadpool.hh
│ ├── http
│ ├── net.cc
│ ├── net.hh
│ ├── pp
│ │ ├── pp.hh
│ │ ├── request.cc
│ │ ├── request.hh
│ │ ├── response.cc
│ │ ├── response.hh
│ │ ├── socket.cc
│ │ └── socket.hh
│ ├── socket.cc
│ ├── socket.hh
│ ├── util.cc
│ └── util.hh
├── server.cc
├── server.hh
├── thread
│ ├── condition.hh
│ ├── count_down_latch.hh
│ ├── mutex_lock.hh
│ ├── noncopyable.hh
│ ├── thread.cc
│ └── thread.hh
└── util
├── firewall.cc
├── firewall.hh
├── json.hh
├── url.hh
├── util.cc
├── util.hh
└── vessel.hh
这几天主要是完善 pp (pwnsky protocol)二进制加密传输协议,还有该框架的拓展性。
pp 协议,全称为 pwnsky protocol, 是一款吸收http部分特性的一款二进制传输协议,主要用于游戏长连接交互协议,目前基于tcp来实现。
该协议头部只占16字节,相对与http更小,由于协议字段都在固定位置,解析起来更快速。
pp协议中定义有状态码,数据类型,数据长度,请求路由。
采用 pwnsky encryption进行数据加密,由服务端随机生成8字节密钥返回给客户端,客户端接收到之后,在断开之前传输数据都采用该密钥进行加解密。
pp协议是我自己根据http特点来压缩而来的,头部大小只有16字节,目前头部定义字段如下:
--------------------------------------------------------------------------
| magic 2字节 | status 1字节| type 1字节 | length 4 字节 |
--------------------------------------------------------------------------
| route 6 字节 | code 2字节 |
--------------------------------------------------------------------------
magic: 协议标识,两字节为 "\x50\x50"
status: 状态码,包含客户端请求状态码与服务端响应状态码。
type: 传输数据类型,类似与http中的Content-Type
length: 数据长度
route: 请求路由,类似于http url中的path
code: 校验码,用于检测传输内容是否符合加密规范。
pp协议目前 c++ 定义如下,后面不断完善协议:
namespace pp {
enum class status {
// 客户端请求码
connect = 0x10, // 建立连接,请求密钥的过程
data_transfer = 0x11, // 传输数据
// 服务端响应码
protocol_error = 0x20, // 协议解析错误
too_big = 0x21, // 传输数据过长
invalid_transfer = 0x22, // 无效传输
ok = 0x30, // 请求成功
send_key = 0x31, // 发送密钥
redirct = 0x40, // 重置路由
};
// 数据类型
enum class data_type {
binary_stream = 0x00, // 二进制数据流
image = 0x01, // 图片
video = 0x02, // 视频
music = 0x03, // 音乐
text = 0x10, // 文本
json = 0x11, // json 数据
xml = 0x12, // xml 数据
};
// 协议头,只占16 字节
struct header {
unsigned short magic; // 协议标识,"PP" 值为0x5050,
unsigned char status; // 客户端请求码与服务端响应码
unsigned char type; // 数据类型
unsigned int length; // 数据长度
unsigned char route[6]; // 请求路由,代替http url中的path
unsigned char code[2]; // 数据校验码
};
}
采用pp协议的gsky服务器连接与客户端过程:
1. 客户端发起获取密钥连接请求
2. 服务端随机生成8字节密钥和2字节code (校验码),并采用PE (Pwnsky Encryption)以全0的8字节的密钥对内容部分进行加密,也对pp协议头部后8字节也进行单独加密。
3. 客户端收到数据,采用全0 的8字节密钥分别解密协议头部后8字节与内容密钥部分,将其code与密钥储存。
4. 客户端发送数据,在协议头部的code值设置为之前服务端发送过来的code,再分别对内容与头部后8字节采用服服务端发送过来的密钥进行加密,再发送给服务端。
5. 服务端接收数据,采用自己的密钥先进行协议头部后8字节解密,检验code值是否正确,正确之后再根据长度接收数据内容与解密数据内容。
大体上连接与传输过程就是这么回事,客户端若不进行密钥获取的话,服务端接收到数据后是直接断开连接的。
那至于为什么要对协议头部后8字节进行加密,协议头部后8字节包含了 6字节的route与2字节的code,route相当于请求路径,也不希望攻击者通过抓包看到的,所以route有必要进行加盟,code是校验值,有一定程度检测数据与密钥的正确性。
上面提到了PE加密,PE加密是自己先暂时写的一个对称加密算法,比较简单,采用密钥轮加变换单字节单字节的异或数据,密码算法简单的目的也是处于服务器的处理效率考虑,目前加解密c++实现如下:
namespace gsky {
namespace crypto {
class pe {
public:
pe();
~pe();
void encode(unsigned char key[8], void *raw_data, size_t length);
void decode(unsigned char key[8], void *raw_data, size_t length);
unsigned char xor_table_[256] = {
0xbe, 0xd1, 0x90, 0x88, 0x57, 0x00, 0xe9, 0x53, 0x10, 0xbd, 0x2a, 0x34, 0x51, 0x84, 0x07, 0xc4,
0x33, 0xc5, 0x3b, 0x53, 0x5f, 0xa8, 0x5d, 0x4b, 0x6d, 0x22, 0x63, 0x5d, 0x3c, 0xbd, 0x47, 0x6d,
0x22, 0x3f, 0x38, 0x4b, 0x7a, 0x4c, 0xb8, 0xcc, 0xb8, 0x37, 0x78, 0x17, 0x73, 0x23, 0x27, 0x71,
0xb1, 0xc7, 0xa6, 0xd1, 0xa0, 0x48, 0x21, 0xc4, 0x1b, 0x0a, 0xad, 0xc9, 0xa5, 0xe6, 0x14, 0x18,
0xfc, 0x7b, 0x53, 0x59, 0x8b, 0x0d, 0x07, 0xcd, 0x07, 0xcc, 0xbc, 0xa5, 0xe0, 0x28, 0x0e, 0xf9,
0x31, 0xc8, 0xed, 0x78, 0xf4, 0x75, 0x60, 0x65, 0x52, 0xb4, 0xfb, 0xbf, 0xac, 0x6e, 0xea, 0x5d,
0xca, 0x0d, 0xb5, 0x66, 0xac, 0xba, 0x06, 0x30, 0x95, 0xf4, 0x96, 0x42, 0x7a, 0x7f, 0x58, 0x6d,
0x83, 0x8e, 0xf6, 0x61, 0x7c, 0x0e, 0xfd, 0x09, 0x6e, 0x42, 0x6b, 0x1e, 0xb9, 0x14, 0x22, 0xf6,
0x16, 0xd2, 0xd2, 0x60, 0x29, 0x23, 0x32, 0x9e, 0xb4, 0x82, 0xee, 0x58, 0x3a, 0x7d, 0x1f, 0x74,
0x98, 0x5d, 0x17, 0x64, 0xe4, 0x6f, 0xf5, 0xad, 0x94, 0xaa, 0x89, 0xe3, 0xbe, 0x98, 0x91, 0x38,
0x70, 0xec, 0x2f, 0x5e, 0x9f, 0xc9, 0xb1, 0x26, 0x3a, 0x64, 0x48, 0x13, 0xf1, 0x1a, 0xc5, 0xd5,
0xe5, 0x66, 0x11, 0x11, 0x3a, 0xaa, 0x79, 0x45, 0x42, 0xb4, 0x57, 0x9d, 0x3f, 0xbc, 0xa3, 0xaa,
0x98, 0x4e, 0x6b, 0x7a, 0x4a, 0x2f, 0x3e, 0x10, 0x7a, 0xc5, 0x33, 0x8d, 0xac, 0x0b, 0x79, 0x33,
0x5d, 0x09, 0xfc, 0x9d, 0x9b, 0xe5, 0x18, 0xcd, 0x1c, 0x7c, 0x8b, 0x0a, 0xa8, 0x95, 0x56, 0xcc,
0x4e, 0x34, 0x31, 0x33, 0xf5, 0xc1, 0xf5, 0x03, 0x0a, 0x4a, 0xb4, 0xd1, 0x90, 0xf1, 0x8f, 0x57,
0x20, 0x05, 0x0d, 0xa0, 0xcd, 0x82, 0xb3, 0x25, 0xd8, 0xd2, 0x20, 0xf3, 0xc5, 0x96, 0x35, 0x35,
};
};
}
}
#include <gsky/crypto/pe.hh>
gsky::crypto::pe::pe() {
}
gsky::crypto::pe::~pe() {
}
// key length is 8 bytes
// 加密概述
// 采用密钥重叠循环,查表来进行异或。
//
void gsky::crypto::pe::encode(unsigned char key[8], void *raw_data, size_t length) {
unsigned char keys[8];
memcpy(keys, key, 8);
char *data = (char *)raw_data;
for(int i = 0; i < length; i ++) {
data[i] ^= keys[i % 8];
unsigned char n = ((keys[i % 8] + keys[(i + 1) % 8]) * keys[(i + 2) % 8]) & 0xff;
data[i] ^= n ^ xor_table_[n];
keys[i % 8] = (n * 2 + 3) % 0x100;
}
}
// 解密
void gsky::crypto::pe::decode(unsigned char key[8], void *raw_data, size_t length) {
unsigned char keys[8];
memcpy(keys, key, 8);
char *data = (char *)raw_data;
for(int i = 0; i < length; i ++) {
char t_key = keys[i % 8];
unsigned char n = ((keys[i % 8] + keys[(i + 1) % 8]) * keys[(i + 2) % 8]) & 0xff;
data[i] ^= n ^ xor_table_[n];
data[i] ^= t_key;
keys[i % 8] = (n * 2 + 3) % 0x100;
}
}
协议拓展部分,为了更方便的自定义传输协议,我对框架进行了比较大的整改,在net模块目前有:
├── net
│ ├── channel.cc
│ ├── channel.hh
│ ├── epoll.cc
│ ├── epoll.hh
│ ├── eventloop.cc
│ ├── eventloop.hh
│ ├── eventloop_thread.cc
│ ├── eventloop_thread.hh
│ ├── eventloop_threadpool.cc
│ ├── eventloop_threadpool.hh
│ ├── http // http解析,有待实现
│ ├── net.cc
│ ├── net.hh
│ ├── pp // pp解析
│ │ ├── pp.hh
│ │ ├── request.cc
│ │ ├── request.hh
│ │ ├── response.cc
│ │ ├── response.hh
│ │ ├── socket.cc
│ │ └── socket.hh
│ ├── socket.cc
│ ├── socket.hh
│ ├── util.cc
│ └── util.hh
上面部分主要是从net::socket类进行协议的分支,该类主要是相当与一个epoll架构的单纯tcp套接子处理,基于tcp之上,再对数据进行协议解析,为了让库更好的拓展,我特意模仿了golang语言中的http库写了两个类request类和response类,request类是存储客户端请求信息,response类是让数据发送给客户端的封装接口。
目前来说pp协议服务端已经基本差不多了,只是pp协议客户端还有待实现一下sdk,方便接入gsky服务器。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。