rust连接ctp的数据交互处理
生成绑定
rust调用ctp的步骤:
- 利用python自动生成
wrapper.cpp
和wrapper.hpp
文件; - 用rust的bindgen 生成rust可调用的接口;
- 引入生成后的文件,引入的方式有两种:
pub use crate::ctp::generated::*;
导入和
include!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/src/ctp/generated/mod.rs"
));
include 模式,vscode代码导航只能到引入的文件,所以本人更推荐用use
的形式
ctp生成rust绑定可以参考这个开源项目:ctp-rs
c的char
类型与rust的i8
类型
ctp头文件中所有定义为char
类型的,通过rust
的bindgen
将自动转为i8
类型,比如下图的登录参数中的MacAddress
在CTP头文件定义为char
的21个字节定长类型,转为rust则变成[i8;21]
定长数组:
rust 调用ctp时定长数据的处理
rust与ctp进行交互比较不好处理的是大量参数必须转换成[i8;N]
定长数组类型。
在rust中,字符串转bytes
后就是 &[u8]
类型,通过map就可以很容易就转成 Vec<i8>
类型:
let b: Vec<i8> = "Hello, world!"
.as_bytes()
.iter()
.map(|x| *x as i8)
.collect();
Vec<i8>
类型作为引用参数传入时,系统将自动转换为&[i8]
类型。如下代码:
fn main() {
let b: Vec<i8> = "Hello, world!"
.as_bytes()
.iter()
.map(|x| *x as i8)
.collect();
print_sice(&b)
}
fn print_sice(s: &[i8]) {
println!("{:?}", s)
}
输出:
================ STANDARD OUTPUT ================
[72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33]
因此不管是[u8]
还是[i8]
,slice
类型的数据都很容易转换获得。但是如果大量的定长数组就比较不好处理。有人可能觉得写一个方法将 String
转[i8;N]
即可。要知道在rust中[i8;1]
,[i8;2]
...[i8;N]
都是不同的类型。因此必须实现一个 String
-> [i8;N]
的转换函数。在rust1.51之前基本不可能实现这个功能,除非你从String->[i8;1]
到String->[i8;N]
每个手写一遍。rust的Default::default()
看提示应该是用宏实现了数组,数组的最大长度为32(截止rustc 1.57.0),不信试试let b: [i8; 33] = Default::default();
:
rust1.51的版本引入了很重要的一个特性:const泛型
,相关的知识点可以通过Rust语言圣经了解:const泛型
下列给出&str
-> [i8;N]
的转换函数:
/// 将给定的字符串转换为定长的cstr
/// 注意:如果长度超过将被截断,不足部分用`�`补足
pub fn str_to_cstr<const N: usize>(text: &str) -> [i8; N] {
if N < 1 {
panic!("数组长度必须大于0!")
}
let buffer = &mut [0u8; N];
for (place, data) in buffer[..N - 1].iter_mut().zip(text.as_bytes().iter()) {
*place = *data;
}
let d: Vec<i8> = (*buffer).iter().map(|x| *x as i8).collect();
let mut arr: [i8; N] = [0i8; N];
arr.copy_from_slice(&d);
arr
}
Example:
// "password" -> [i8;41]
let cs = to_cstr::<41>("password");
println!("{:?}",cs);
其中 to_cstr::<41>
尖括号中的数字代表转换后的数组长度
ctp返回数据处理
rust
默认采用的是utf-8
字符,特别对于非windows平台一般不使用gbk
相关的字符集。
但是,ctp返回的是gbk
字符集,如果不进行处理,将出现乱码。下列提供rust
版本的gbk->utf-8
use encoding::{all::GB18030, DecoderTrap, Encoding};
/// ctp发送过来的中文信息是gbk格式的,这里需要转换为utf-8格式
/// rust的字符串是存储为`[u8]`格式,但是ctp包装后,接口过来的数据是 `[i8]`格式,因此
/// 在数据转换之前需先将 `[u8]`转为`[i8]`
/// 如果数据转换错误,将直接返回 `""` 空字符串`String`
pub fn gbk_cstr_to_str(v: &[i8]) -> String {
let vi: Vec<u8> = v.iter().map(|x| *x as u8).collect();
let slice = vi.split(|c| *c == 0u8).next().unwrap();
if slice.is_ascii() {
unsafe {
return std::str::from_utf8_unchecked(slice).to_owned();
};
}
match GB18030.decode(slice, DecoderTrap::Replace) {
Ok(s) => s,
Err(e) => {
log::error!("GBK转UTF-8错误:{}", e.to_string());
return "".to_owned();
}
}
}
调用方法前须先引入依赖包
[dependencies]
encoding = "0.2"
博主