一、说明
在前面介绍了Rust的基础环境,今天就开始学习RUST的一个基本的网络客户端架子(不是框架),并从这个基本架子开始,开始学习RUST的基本知识和各种用法。鉴于RUST现在区块链领域的应用趋势是越来越广泛,搞一搞RUST也是势在必行。
二、应用
照老样子,先看一个基本的例子:
mod datacontrol;
mod server;
mod databuild;
use std::net::{TcpListener, TcpStream};
use std::thread;
use std::io::{Read, Write, Error};
use std::str;
use std::io::{self, BufRead, BufReader};
use std::time::Duration;
use crate::databuild::{build_beat, build_signer, build_spacepw};
use crate::datacontrol::dp;
const BEAT:u16 = 0x00;
const REPLY_ID:u16 = 0x605;
fn client_main(){
let dc = datacontrol::dp::new();
//172.16.110.241:18888
let mut stream = TcpStream::connect("127.0.0.1:18888")
.expect("connect server error!");
//let data:[u8;15] = [0x53,0x53,0x05,0x00,0x5e,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x0d];
//处理更新自己ID
//写入交互协议代码
let mut arr:[u8;12] = [0x00;12];
let mut beat: [u8; 10] = [0x00;10];
let mut signer:[u8;213] = [0x00;213];
let mut spacepw:[u8;300] = [0x00;300];//74
let mut sendlen = 0;
let mut ok_send = false;
//let mut beat1;
unsafe {
//build beat
build_beat(&mut beat);
//build singer
let md = "12345678901234567890123456789012";
let recvmd = "12345678901234567890123456789013";
let datapw= "hijk[]lmn{}";
build_signer(md,recvmd,&mut signer);
//build spacepw
sendlen = build_spacepw(md,recvmd,datapw,&mut spacepw);
}
match stream.write(&signer[..213]){
Ok(e)=>println!("send ok {}",e),
Err(e)=>println!("send err!")
}
let fn_cb = |buf:&[u8],len:usize|{
};
loop{
//读控制变量
let mut buffer: Vec<u8> = Vec::new();
let mut dst:Vec<u8> = Vec::new();
let mut reader = BufReader::new(&stream);
let n = reader.read_until(b'\n', &mut buffer)
.expect("Could not read into buffer");
match dc.data_parse(&buffer,&dst,n,&fn_cb) {
Ok(_0)=>{
//type case
let typebuf: [u8;2] = [dst[2],dst[3]];
let ptr :*const u8 = typebuf.as_ptr();
let ptr :*const u16 = ptr as *const u16;
let td =unsafe{ *ptr};
match td {
BEAT=>{
//回复心跳
match stream.write(&beat[0..10]){
Ok(e)=>println!("send ok {}",e),
Err(e)=>println!("send err!")
}
},
REPLY_ID=>{
//处理消息回复
ok_send = true;
continue;
},
_=>{}
}
println!("parse ok!")
},
Err(..)=>println!("parse err!")
}
if !ok_send{
thread::sleep(std::time::Duration::new(2,0));
}else {
match stream.write(&spacepw[0..10]){
Ok(e)=>println!("send ok {}",e),
Err(e)=>println!("send err!")
}
}
println!("recv data len:{}",n);
print!("{}", str::from_utf8(&buffer)
.expect("Could not write buffer as string"));
}
}
fn call_client(){
let mut data:u32 = 0x12345678;
let mut arr:[u8;4];
unsafe { arr = std::mem::transmute::<u32, [u8;4]>(data.to_le()); };// == [42, 0, 0, 0]
let handler = thread::spawn(move || {
thread::sleep(Duration::from_secs(6));
client_main();
//thread::sleep_ms(2000);
});
handler.join().unwrap();
}
fn main(){
client_main();
//server::server_main();
}
这是一个最简单的入门的网络客户端的例子,从这里可以看得出来,相对于用C++编写的客户端,确实是简单了不少。这里先忽略掉对数据解析和控制的问题,这样可能会更方便学习一些。
但是这里面,也有很多的不习惯,特别是对于二进制数据的拷贝和转换,一直都十分的挠头。
三、代码分析
看一下代码,和几乎所有的语言一样,都是从main函数开始,而在这里为了以后写服务端方便,把它们拆成了两个函数。在call_client()函数中,先生成一个小头的数据转换,然后通过线程启动真正的数据接收和发送,来保证整个客户端的稳定运行。
在这里使用了move||,一个无参半包,可以强制通过move获取当前的值。
在client_main()函数中,就执行了两个动作,连接服务端,读写数据。虽然还有第三个动作,数据的解析和分发,但这里不是重点,可以暂时忽略。通过unsafe来组建心跳,这块比较简单。在获得Socket的流操作后,可以直接把心跳发送,再使用match判断结果。在下一篇里会重点分析各个用到的技术。
下面就是通过循环不断的读取Socket缓冲区的数据,并对数据进行分析处理。
四、总结
在2017年就开始学习和初步应用RUST,但一直没有广泛的在实际工程中实践,后续的学习和应用也是断断续续,做为对这三年多来对RUST学习的一个回顾和总结,抱着实际应用的想法,开始这个学习应用的系列。