|
|
@@ -0,0 +1,252 @@
|
|
|
+use std::collections::{BTreeMap};
|
|
|
+use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
|
|
+use std::sync::Arc;
|
|
|
+use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
+use std::time::Duration;
|
|
|
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
|
|
+use serde_json::json;
|
|
|
+use tokio::sync::mpsc::Sender;
|
|
|
+use tracing::{error, info, trace};
|
|
|
+use crate::{proxy};
|
|
|
+use tungstenite::client::{AutoStream, connect_with_proxy, ProxyAutoStream};
|
|
|
+use tungstenite::protocol::WebSocketConfig;
|
|
|
+use url::Url;
|
|
|
+use crate::proxy::ParsingDetail;
|
|
|
+use crate::response_base::ResponseData;
|
|
|
+use crate::utils::get_time_microsecond;
|
|
|
+use tokio_tungstenite::tungstenite::{Error, Message};
|
|
|
+use crate::socket_tool::AbstractWsMode;
|
|
|
+
|
|
|
+pub enum BinanceSwapWsType {
|
|
|
+ //订阅频道类型
|
|
|
+ PublicAndPrivate,
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#[derive(Clone)] //订阅枚举
|
|
|
+pub enum BinanceSwapSubscribeType {
|
|
|
+ PuBookTicker,
|
|
|
+ PuAggTrade,
|
|
|
+ PuDepth20levels100ms,
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Clone)]
|
|
|
+pub struct BinanceSwapLogin {
|
|
|
+ api_key: String,
|
|
|
+ api_secret: String,
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Clone)]
|
|
|
+pub struct BinanceSwapWs {
|
|
|
+ label: String,
|
|
|
+ address_url: String,
|
|
|
+ //账号信息
|
|
|
+ login_param: Option<BinanceSwapLogin>,
|
|
|
+ //kuconis特殊参数
|
|
|
+ symbol_s: Vec<String>,
|
|
|
+ //订阅币对
|
|
|
+ subscribe_types: Vec<BinanceSwapSubscribeType>,
|
|
|
+}
|
|
|
+
|
|
|
+impl BinanceSwapWs {
|
|
|
+ /*******************************************************************************************************/
|
|
|
+ /*****************************************获取一个对象****************************************************/
|
|
|
+ /*******************************************************************************************************/
|
|
|
+ pub fn new(is_colo: bool, login_param: Option<BinanceSwapLogin>, ws_type: BinanceSwapWsType) -> BinanceSwapWs {
|
|
|
+ return BinanceSwapWs::new_label("default-BinanceSwapWs".to_string(), is_colo, login_param, ws_type);
|
|
|
+ }
|
|
|
+ pub fn new_label(label: String, is_colo: bool, login_param: Option<BinanceSwapLogin>, ws_type: BinanceSwapWsType) -> BinanceSwapWs {
|
|
|
+ /*******公共频道-私有频道数据组装*/
|
|
|
+ let address_url = match ws_type {
|
|
|
+ BinanceSwapWsType::PublicAndPrivate => {
|
|
|
+ "wss://fstream.binance.com/stream?streams=btcusdt@depth20@100ms".to_string()
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ if is_colo {
|
|
|
+ info!("开启高速(未配置,走普通:{})通道",address_url);
|
|
|
+ } else {
|
|
|
+ info!("走普通通道:{}",address_url);
|
|
|
+ }
|
|
|
+ BinanceSwapWs {
|
|
|
+ label,
|
|
|
+ address_url,
|
|
|
+ login_param,
|
|
|
+ symbol_s: vec![],
|
|
|
+ subscribe_types: vec![],
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*******************************************************************************************************/
|
|
|
+ /*****************************************订阅函数********************************************************/
|
|
|
+ /*******************************************************************************************************/
|
|
|
+ //手动添加订阅信息
|
|
|
+ pub fn set_subscribe(&mut self, subscribe_types: Vec<BinanceSwapSubscribeType>) {
|
|
|
+ self.subscribe_types.extend(subscribe_types);
|
|
|
+ }
|
|
|
+ //手动添加币对
|
|
|
+ pub fn set_symbols(&mut self, mut b_array: Vec<String>) {
|
|
|
+ for symbol in b_array.iter_mut() {
|
|
|
+ // 大写
|
|
|
+ *symbol = symbol.to_lowercase();
|
|
|
+ // 字符串替换
|
|
|
+ *symbol = symbol.replace("_", "");
|
|
|
+ *symbol = symbol.replace("-", "");
|
|
|
+ }
|
|
|
+ self.symbol_s = b_array;
|
|
|
+ }
|
|
|
+ fn contains_pr(&self) -> bool {
|
|
|
+ for t in self.subscribe_types.clone() {
|
|
|
+ if let t_f = match t {
|
|
|
+ BinanceSwapSubscribeType::PuBookTicker => false,
|
|
|
+ BinanceSwapSubscribeType::PuAggTrade => false,
|
|
|
+ BinanceSwapSubscribeType::PuDepth20levels100ms => false,
|
|
|
+ } {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ false
|
|
|
+ }
|
|
|
+ /*******************************************************************************************************/
|
|
|
+ /*****************************************工具函数********************************************************/
|
|
|
+ /*******************************************************************************************************/
|
|
|
+ //订阅枚举解析
|
|
|
+ pub fn enum_to_string(symbol: String, subscribe_type: BinanceSwapSubscribeType) -> String {
|
|
|
+ match subscribe_type {
|
|
|
+ BinanceSwapSubscribeType::PuAggTrade => {
|
|
|
+ format!("{}@aggTrade", symbol)
|
|
|
+ }
|
|
|
+ BinanceSwapSubscribeType::PuDepth20levels100ms => {
|
|
|
+ format!("{}@depth20@100ms", symbol)
|
|
|
+ }
|
|
|
+ BinanceSwapSubscribeType::PuBookTicker => {
|
|
|
+ format!("{}@bookTicker", symbol)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //组装订阅数据-实时订阅
|
|
|
+ fn get_subscription(&self) -> String {
|
|
|
+ // let mut str = "".to_string();
|
|
|
+ let mut params = vec![];
|
|
|
+ for symbol in &self.symbol_s {
|
|
|
+ for subscribe_type in &self.subscribe_types {
|
|
|
+ let ty_str = Self::enum_to_string(symbol.clone(), subscribe_type.clone());
|
|
|
+ params.push(ty_str);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // trace!("订阅信息:{}", str.to_string());
|
|
|
+ // str.to_string()
|
|
|
+
|
|
|
+ let str = json!({
|
|
|
+ "method": "SUBSCRIBE",
|
|
|
+ "params": params,
|
|
|
+ "id": 1
|
|
|
+ });
|
|
|
+ str.to_string()
|
|
|
+ }
|
|
|
+ //数据解析
|
|
|
+ pub fn analysis_message(message: Result<Message, Error>) -> ResponseData {
|
|
|
+ let mut code = "00".to_string();
|
|
|
+ let mut message_str = "".to_string();
|
|
|
+ let mut data = "".to_string();
|
|
|
+ match message {
|
|
|
+ Ok(Message::Text(text)) => {
|
|
|
+ // trace!("Text:{}",text);
|
|
|
+ let response_data = Self::ok_text(text);
|
|
|
+ // trace!("response_data:{:?}",response_data);
|
|
|
+ if response_data.code == "200" {
|
|
|
+ return response_data;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Ok(Message::Binary(s)) => {
|
|
|
+ trace!("Binary:{:?}",s);
|
|
|
+ }
|
|
|
+ Ok(Message::Ping(pi)) => {
|
|
|
+ trace!("Ping:{:?}",pi);
|
|
|
+ code = "200".to_string();
|
|
|
+ message_str = format!("服务器响应ping:{:?}", String::from_utf8(pi));
|
|
|
+ }
|
|
|
+ Ok(Message::Pong(po)) => {
|
|
|
+ trace!("Pong:{:?}",po);
|
|
|
+ //原始帧 正常读取数据不会读取到该 信息类型
|
|
|
+ code = "200".to_string();
|
|
|
+ message_str = format!("服务器响应pong:{:?}", String::from_utf8(po));
|
|
|
+ }
|
|
|
+ Ok(Message::Close(c)) => {
|
|
|
+ trace!("Close:{:?}",c);
|
|
|
+ //原始帧 正常读取数据不会读取到该 信息类型
|
|
|
+ code = "0".to_string();
|
|
|
+ message_str = format!("关闭指令:{:?}", c);
|
|
|
+ }
|
|
|
+ Ok(Message::Frame(f)) => {
|
|
|
+ trace!("Frame:{:?}",f);
|
|
|
+ //原始帧 正常读取数据不会读取到该 信息类型
|
|
|
+ code = "-2".to_string();
|
|
|
+ message_str = format!("意外读取到原始帧:{:?}", f);
|
|
|
+ }
|
|
|
+ Err(e) => {
|
|
|
+ trace!("Err:{:?}",e);
|
|
|
+ code = "-1".to_string();
|
|
|
+ message_str = format!("币安-数据解析 未知错误:{:?}", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ResponseData::new("".to_string(), code, message_str, data);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*******************************************************************************************************/
|
|
|
+ /*****************************************socket基本*****************************************************/
|
|
|
+ /*******************************************************************************************************/
|
|
|
+ //链接
|
|
|
+ pub async fn ws_connect_async(&self, write_rx: UnboundedReceiver<Message>,
|
|
|
+ read_tx: UnboundedSender<ResponseData>) -> Result<(), Error> {
|
|
|
+
|
|
|
+ AbstractWsMode::ws_connect_async(self.address_url.clone(), write_rx, read_tx, self.label.clone(), BinanceSwapWs::analysis_message).await
|
|
|
+ }
|
|
|
+ //发出指令
|
|
|
+ pub fn write_message(&self, write_tx: UnboundedSender<Message>, str: String) {
|
|
|
+ trace!("发送指令:{}",str);
|
|
|
+ write_tx.unbounded_send(Message::Text(str.to_string())).expect(format!("发送失败:{}", str).as_str());
|
|
|
+ }
|
|
|
+ //发出指令-订阅频道
|
|
|
+ pub fn send_subscribe(&self, write_tx: UnboundedSender<Message>) {
|
|
|
+ if self.contains_pr() {
|
|
|
+ //有订阅需要登录
|
|
|
+ }
|
|
|
+ //发送订阅
|
|
|
+ let subscribe = self.get_subscription();
|
|
|
+ self.write_message(write_tx, subscribe);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //数据解析
|
|
|
+ pub fn ok_text(text: String) -> ResponseData
|
|
|
+ {
|
|
|
+ // trace!("原始数据");
|
|
|
+ // trace!(?text);
|
|
|
+ let mut res_data = ResponseData::new("".to_string(), "200".to_string(), "success".to_string(), "".to_string());
|
|
|
+ let json_value: serde_json::Value = serde_json::from_str(&text).unwrap();
|
|
|
+
|
|
|
+ if json_value.get("error").is_some() {//订阅返回
|
|
|
+ res_data.code = json_value["error"]["code"].to_string();
|
|
|
+ res_data.message = json_value["error"]["msg"].to_string();
|
|
|
+ } else if json_value.get("stream").is_some() {//订阅返回
|
|
|
+ res_data.data = format!("{}", json_value.get("data").as_ref().unwrap());
|
|
|
+ res_data.code = "200".to_string();
|
|
|
+
|
|
|
+ let channel = format!("{}", json_value.get("stream").as_ref().unwrap());
|
|
|
+ if channel.contains("@aggTrade") {
|
|
|
+ res_data.channel = "aggTrade".to_string();
|
|
|
+ } else if channel.contains("@depth20@100ms") {
|
|
|
+ res_data.channel = "depth".to_string();
|
|
|
+ } else if channel.contains("@bookTicker") {
|
|
|
+ res_data.channel = "bookTicker".to_string();
|
|
|
+ } else {
|
|
|
+ res_data.channel = "未知的频道".to_string();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ res_data.data = text
|
|
|
+ }
|
|
|
+ res_data
|
|
|
+ }
|
|
|
+}
|