|
|
@@ -0,0 +1,284 @@
|
|
|
+use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
|
|
+use url::Url;
|
|
|
+
|
|
|
+
|
|
|
+use std::time::Duration;
|
|
|
+use chrono::Utc;
|
|
|
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
|
|
+
|
|
|
+use futures_util::{future, pin_mut, StreamExt};
|
|
|
+use futures_util::stream::{SplitSink, SplitStream};
|
|
|
+use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream};
|
|
|
+use tokio_tungstenite::tungstenite::{Error, Message};
|
|
|
+use ring::hmac;
|
|
|
+use serde_json::json;
|
|
|
+use tokio::net::TcpStream;
|
|
|
+use tokio_tungstenite::tungstenite::error::UrlError;
|
|
|
+use tracing::trace;
|
|
|
+use tungstenite::client::IntoClientRequest;
|
|
|
+use crate::proxy;
|
|
|
+use crate::proxy::{ProxyEnum, ProxyResponseEnum};
|
|
|
+
|
|
|
+pub struct BinanceSwapModel {
|
|
|
+ address_url: String,
|
|
|
+}
|
|
|
+
|
|
|
+impl BinanceSwapModel {
|
|
|
+ pub fn new() -> OkxSwapModel {
|
|
|
+ OkxSwapModel {
|
|
|
+ // ws: AbstractWsMode::new(),
|
|
|
+ address_url: "wss://fstream.binance.com/stream?streams=btcusdt@depth20@100ms".to_string(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //链接
|
|
|
+ pub async fn ws_connect_async(&self, write_rx: UnboundedReceiver<Message>, read_tx: UnboundedSender<Message>) -> Result<(), Error> {
|
|
|
+ AbstractWsMode::ws_connect_async(self.address_url.clone(), write_rx, read_tx).await
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+pub struct OkxSwapModel {
|
|
|
+ // ws: AbstractWsMode,
|
|
|
+ address_url: String,
|
|
|
+}
|
|
|
+
|
|
|
+impl OkxSwapModel {
|
|
|
+ pub fn new() -> OkxSwapModel {
|
|
|
+ OkxSwapModel {
|
|
|
+ // ws: AbstractWsMode::new(),
|
|
|
+ address_url: "wss://ws.okx.com:8443/ws/v5/public".to_string(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //链接
|
|
|
+ pub async fn ws_connect_async(&self, write_rx: UnboundedReceiver<Message>, read_tx: UnboundedSender<Message>) -> Result<(), Error> {
|
|
|
+ AbstractWsMode::ws_connect_async(self.address_url.clone(), write_rx, read_tx).await
|
|
|
+ }
|
|
|
+
|
|
|
+ // //心跳包发送
|
|
|
+ // pub fn send_ping_or_pong(){
|
|
|
+ // }
|
|
|
+}
|
|
|
+
|
|
|
+// // 抽象ws 接口
|
|
|
+// pub trait AbstractWs {
|
|
|
+// fn new() -> Self;
|
|
|
+// //创建链接
|
|
|
+// fn ws_connect_async(&mut self) -> Pin<Box<dyn Future<Output=Result<bool, Error>> + Send>>;
|
|
|
+// //返回通道(发送指令通道)
|
|
|
+// fn send_message(&self, text: String);
|
|
|
+// //返回通道(数据接受通道)
|
|
|
+// // fn get_read_rx(&self) -> UnboundedReceiver<Message>;
|
|
|
+// }
|
|
|
+
|
|
|
+pub struct AbstractWsMode {}
|
|
|
+
|
|
|
+impl AbstractWsMode
|
|
|
+ where SplitSink<WebSocketStream<tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>>, tokio_tungstenite::tungstenite::Message>: futures_util::Sink<Message>,
|
|
|
+{
|
|
|
+ //创建链接
|
|
|
+ pub async fn ws_connect_async(address_url: String, write_rx: UnboundedReceiver<Message>, read_tx: UnboundedSender<Message>) -> Result<(), Error> {
|
|
|
+ //1.是否走代理
|
|
|
+ /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
|
|
|
+ let proxy = match proxy::ParsingDetail::env_proxy(ProxyEnum::WS) {
|
|
|
+ ProxyResponseEnum::NO => {
|
|
|
+ trace!("非 代理");
|
|
|
+ None
|
|
|
+ }
|
|
|
+ ProxyResponseEnum::YES(proxy) => {
|
|
|
+ trace!("代理");
|
|
|
+ Option::from(proxy)
|
|
|
+ }
|
|
|
+ };
|
|
|
+ match connect_async(address_url, proxy).await {
|
|
|
+ Ok((ws_stream, _)) => {
|
|
|
+ trace!("WebSocket 握手完成。");
|
|
|
+ tokio::spawn(async move {
|
|
|
+ let (write, read) = ws_stream.split();
|
|
|
+
|
|
|
+ //将socket 的写操作与 写通道链接起来,将数据以ok的结构体封装进行传递
|
|
|
+ let stdin_to_ws = write_rx.map(Ok).forward(write);
|
|
|
+ let ws_to_stdout = {
|
|
|
+ trace!("---1");
|
|
|
+ //读,循环读取,然后拿到 message,,然后开启异步处理 message,
|
|
|
+ let result = read.for_each(|message| async {
|
|
|
+ read_tx.unbounded_send(message.unwrap()).unwrap();
|
|
|
+ });
|
|
|
+ trace!("---3");
|
|
|
+ result
|
|
|
+ };
|
|
|
+
|
|
|
+ //必须操作。,因为不同于其他的高级语言,有自动内存管理,所以为了防范地址改变,所以需要做此处理
|
|
|
+ pin_mut!(stdin_to_ws, ws_to_stdout,);
|
|
|
+ future::select(stdin_to_ws, ws_to_stdout).await;
|
|
|
+ trace!("---5");
|
|
|
+ });
|
|
|
+ trace!("---4");
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+ Err(e) => {
|
|
|
+ trace!("链接失败");
|
|
|
+ Err(Error::Url(UrlError::UnableToConnect("连接失败".to_string())))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+//创建链接
|
|
|
+pub async fn ws_connect_async(address_url: String) -> (SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>,
|
|
|
+ SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>) {
|
|
|
+ //1.是否走代理
|
|
|
+ /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
|
|
|
+ let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
|
|
|
+ let proxy = match proxy::ParsingDetail::env_proxy(ProxyEnum::WS) {
|
|
|
+ ProxyResponseEnum::NO => {
|
|
|
+ trace!("非 代理");
|
|
|
+ None
|
|
|
+ }
|
|
|
+ ProxyResponseEnum::YES(proxy) => {
|
|
|
+ trace!("代理");
|
|
|
+ Option::from(proxy)
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ let (ws_stream, _) = connect_async(address_url, proxy).await.expect("链接失败!");
|
|
|
+ trace!("WebSocket 握手完成。");
|
|
|
+ ws_stream.split()
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+pub async fn client(add_url: String) {
|
|
|
+ let request = Url::parse(add_url.as_str()).expect("Can't connect to case count URL");
|
|
|
+ let client_request = request.into_client_request().unwrap();
|
|
|
+
|
|
|
+ let proxy = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(
|
|
|
+ 127,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 1)
|
|
|
+ ), 7890);
|
|
|
+
|
|
|
+
|
|
|
+ //创建通道 开启线程,向通道写入数据
|
|
|
+ let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
|
|
|
+ let (read_tx, read_rx) = futures_channel::mpsc::unbounded();
|
|
|
+ tokio::spawn(write_sell(write_tx));
|
|
|
+
|
|
|
+
|
|
|
+ //创建socket,,并且读写分离
|
|
|
+ let (ws_stream, _) = connect_async(add_url, Option::from(proxy)).await.expect("Failed to connect");
|
|
|
+ trace!("WebSocket handshake has been successfully completed");
|
|
|
+ let (write, read) = ws_stream.split();
|
|
|
+
|
|
|
+ //将socket 的写操作与 写通道链接起来,将数据以ok的结构体封装进行传递
|
|
|
+ let stdin_to_ws = write_rx.map(Ok).forward(write);
|
|
|
+ let ws_to_stdout = {
|
|
|
+ trace!("---1");
|
|
|
+ //读,循环读取,然后拿到 message,,然后开启异步处理 message,
|
|
|
+ let result = read.for_each(|message| async {
|
|
|
+ read_tx.unbounded_send(message.unwrap()).unwrap();
|
|
|
+ });
|
|
|
+ trace!("---3");
|
|
|
+ result
|
|
|
+ };
|
|
|
+
|
|
|
+ tokio::spawn(read_sell(read_rx));
|
|
|
+
|
|
|
+ //必须操作。,因为不同于其他的高级语言,有自动内存管理,所以为了防范地址改变,所以需要做此处理
|
|
|
+ pin_mut!(stdin_to_ws, ws_to_stdout);
|
|
|
+ future::select(stdin_to_ws, ws_to_stdout).await;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+//模拟 业务场景中 发送指令给目标交易所
|
|
|
+async fn write_sell(tx: futures_channel::mpsc::UnboundedSender<Message>) {
|
|
|
+ let str = serde_json::json!({
|
|
|
+ "op": "subscribe",
|
|
|
+ "args": [
|
|
|
+ {
|
|
|
+ // "channel":"orders",
|
|
|
+ // "instType":"SWAP",
|
|
|
+ // "instFamily":"BTC-USDT"
|
|
|
+ "channel":"books5",
|
|
|
+ "instId":"BTC-USDT"
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ });
|
|
|
+ let str_array: Vec<String> = vec![
|
|
|
+ // log_in_to_str(),
|
|
|
+ // str.to_string(),
|
|
|
+ ];
|
|
|
+
|
|
|
+ let i = 0;
|
|
|
+ loop {
|
|
|
+ if str_array.len() > i {
|
|
|
+ let send_str = str_array.get(i).unwrap();
|
|
|
+ tx.unbounded_send(Message::Text(send_str.to_string())).unwrap();
|
|
|
+ }
|
|
|
+ tokio::time::sleep(Duration::from_secs(5)).await;
|
|
|
+ tx.unbounded_send(Message::Ping(Vec::from("Ping"))).unwrap();
|
|
|
+ tx.unbounded_send(Message::Ping(Vec::from("Pong"))).unwrap();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+async fn read_sell(mut rx: futures_channel::mpsc::UnboundedReceiver<Message>) {
|
|
|
+ loop {
|
|
|
+ if let Some(message) = rx.next().await {
|
|
|
+ match message {
|
|
|
+ Message::Text(s) => {
|
|
|
+ trace!("Text: {}", s);
|
|
|
+ }
|
|
|
+ Message::Binary(s) => {
|
|
|
+ trace!("Binary: {:?}", s);
|
|
|
+ }
|
|
|
+ Message::Ping(s) => {
|
|
|
+ trace!("Ping: {:?}", s);
|
|
|
+ }
|
|
|
+ Message::Pong(s) => {
|
|
|
+ trace!("Pong: {:?}", s);
|
|
|
+ }
|
|
|
+ Message::Close(s) => {
|
|
|
+ trace!("Close: {:?}", s);
|
|
|
+ }
|
|
|
+ Message::Frame(s) => {
|
|
|
+ trace!("Frame: {:?}", s);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ tokio::time::sleep(Duration::from_millis(1)).await
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+pub fn log_in_to_str() -> String {
|
|
|
+ let mut login_json_str = "".to_string();
|
|
|
+
|
|
|
+ let mut access_key: String = "b812814f-b792-432c-9c65-44ebe3b8976b".to_string();
|
|
|
+ let mut secret_key: String = "D6AF4764DF5FDC74BF2CC88A4A8FD035".to_string();
|
|
|
+ let mut passphrase: String = "Astest!@#1".to_string();
|
|
|
+
|
|
|
+ if access_key.len() > 0 || secret_key.len() > 0 || passphrase.len() > 0 {
|
|
|
+ let timestamp = Utc::now().timestamp().to_string();
|
|
|
+ // 时间戳 + 请求类型+ 请求参数字符串
|
|
|
+ let message = format!("{}GET{}", timestamp, "/users/self/verify");
|
|
|
+ let hmac_key = ring::hmac::Key::new(hmac::HMAC_SHA256, secret_key.as_bytes());
|
|
|
+ let result = ring::hmac::sign(&hmac_key, &message.as_bytes());
|
|
|
+ let sign = base64::encode(result);
|
|
|
+
|
|
|
+ let login_json = json!({
|
|
|
+ "op": "login",
|
|
|
+ "args": [{
|
|
|
+ "apiKey": access_key,
|
|
|
+ "passphrase": passphrase,
|
|
|
+ "timestamp": timestamp,
|
|
|
+ "sign": sign }]
|
|
|
+ });
|
|
|
+
|
|
|
+ // trace!("---login_json:{0}", login_json.to_string());
|
|
|
+ // trace!("--登陆:{}", login_json.to_string());
|
|
|
+ login_json_str = login_json.to_string();
|
|
|
+ }
|
|
|
+ login_json_str
|
|
|
+}
|