use crate::bytes_helper::u16_from_le_bytes;
use log::trace;
use std::{
io::{BufReader, Read, Result, Write},
net::{SocketAddr, TcpStream},
time::Duration,
};
#[macro_use]
#[cfg(test)]
pub(crate) mod tests;
mod basic;
pub use basic::*;
pub mod helper;
pub mod ip;
pub mod stock;
pub type BufTcp = BufReader<TcpStream>;
#[derive(Debug, Clone)]
pub struct TcpConfig {
pub timeout: Duration,
pub ip: Option<SocketAddr>,
}
impl Default for TcpConfig {
fn default() -> Self {
Self {
timeout: TIMEOUT,
ip: None,
}
}
}
impl TcpConfig {
pub fn with_index(index: usize, timeout: Duration) -> Self {
Self {
timeout,
ip: Some(ip::STOCK_IP.get(index).copied().unwrap_or(ip::STOCK_IP[0])),
}
}
}
#[derive(Debug)]
pub struct Tcp {
stream: TcpStream,
buffer: BufTcp,
recv: [u8; RECV_SIZE],
config: TcpConfig,
}
impl Tcp {
pub fn new() -> Result<Self> {
Self::with_config(&TcpConfig::default())
}
pub fn with_config(config: &TcpConfig) -> Result<Self> {
match config.ip {
Some(addr) => Self::connect_addr(&addr, config),
None => {
let mut last_err = None;
for addr in ip::STOCK_IP.iter() {
match Self::connect_addr(addr, config) {
Ok(tcp) => return Ok(tcp),
Err(e) => last_err = Some((addr, e)),
}
}
let (addr, err) = last_err.expect("服务器列表不能为空");
log::warn!("所有行情服务器连接/握手失败,最后尝试: {addr}");
Err(err)
}
}
}
fn connect_addr(addr: &SocketAddr, config: &TcpConfig) -> Result<Self> {
let (stream, buffer, recv) = tcpstream_ip_with_timeout(addr, config.timeout)?;
let mut tcp = Self {
stream,
buffer,
recv,
config: config.clone(),
};
send_packs(&mut tcp, false)?;
Ok(tcp)
}
pub fn reconnect(&mut self) -> Result<()> {
*self = Self::with_config(&self.config)?;
Ok(())
}
pub fn heartbeat(&mut self) -> Result<u16> {
let mut hb = SecurityCount::new(0);
Ok(*hb.recv_parsed(self)?)
}
pub fn retry<T>(
&mut self,
mut f: impl FnMut(&mut Tcp) -> Result<T>,
attempts: usize,
) -> Result<T> {
debug_assert!(attempts >= 1);
let mut last_err = None;
for attempt in 0..attempts {
match f(self) {
Ok(v) => return Ok(v),
Err(e) => {
if attempt + 1 < attempts {
self.reconnect()?;
}
last_err = Some(e);
}
}
}
Err(last_err.unwrap())
}
pub fn send_recv(&mut self, send: &[u8]) -> Result<(usize, usize)> {
self.stream.write_all(send)?;
self.buffer.read_exact(&mut self.recv)?;
trace!("send: {:?}\nrecv[16B]: {:?}", send, self);
Ok((send.len(), RECV_SIZE))
}
pub fn into_inner(self) -> (TcpStream, BufTcp, [u8; RECV_SIZE]) {
(self.stream, self.buffer, self.recv)
}
pub fn get_ref(&self) -> (&TcpStream, &BufTcp, &[u8]) {
(&self.stream, &self.buffer, &self.recv)
}
pub fn get_ref_recv(&self) -> &[u8] {
&self.recv
}
}
pub trait Tdx {
const SEND: &'static [u8];
const TAG: &'static str;
const LEN: usize = Self::SEND.len();
type Item: ?Sized;
fn send(&mut self) -> &[u8];
fn recv(&mut self, tcp: &mut Tcp) -> Result<Vec<u8>> {
send_recv_decompress(tcp, self.send(), Self::TAG)
}
fn parse(&mut self, response: Vec<u8>);
fn recv_parsed(&mut self, tcp: &mut Tcp) -> Result<&Self::Item> {
let response = self.recv(tcp)?;
self.parse(response);
Ok(self.result())
}
fn result(&self) -> &Self::Item;
}
pub fn send_recv_decompress(tcp: &mut Tcp, send: &[u8], tag: &str) -> Result<Vec<u8>> {
let (mut buf, deflate_size, inflate_size) = send_recv(tcp, send, tag)?;
if deflate_size != inflate_size {
buf = miniz_oxide::inflate::decompress_to_vec_zlib(&buf).unwrap();
trace!("解压后数据:\n{:?}\n", buf);
debug_assert_eq!(buf.len(), inflate_size as usize);
} else {
trace!("无需解压\n");
};
Ok(buf)
}
pub fn send_recv(tcp: &mut Tcp, send: &[u8], tag: &str) -> Result<(Vec<u8>, u16, u16)> {
tcp.send_recv(send)?;
trace!("{tag}\nsend: {:?}\nrecv[16B]: {:?}", send, tcp);
let deflate_size = u16_from_le_bytes(&tcp.recv, 12); let mut buf = vec![0; deflate_size as usize];
tcp.buffer.read_exact(&mut buf)?;
let inflate_size = u16_from_le_bytes(&tcp.recv, 14); #[rustfmt::skip]
trace!("\n解压前:#{:?}# -> {},解压后:#{:?}# -> {}\n剩余数据(即解压前):{:x?}\n",
&tcp.recv[12..14], deflate_size, &tcp.recv[14..16], inflate_size, buf);
Ok((buf, deflate_size, inflate_size))
}
pub const TIMEOUT: Duration = Duration::from_secs(5);
pub fn tcpstream() -> Result<(TcpStream, BufTcp, [u8; RECV_SIZE])> {
tcpstream_with_timeout(TIMEOUT)
}
pub fn tcpstream_with_timeout(timeout: Duration) -> Result<(TcpStream, BufTcp, [u8; RECV_SIZE])> {
let mut last_err = None;
for addr in ip::STOCK_IP.iter() {
match tcpstream_ip_with_timeout(addr, timeout) {
Ok(x) => return Ok(x),
Err(e) => last_err = Some((addr, e)),
}
}
let (addr, err) = last_err.expect("服务器列表不能为空");
log::warn!("所有行情服务器连接失败,最后尝试: {addr}");
Err(err)
}
pub fn tcpstream_ip(ip: &SocketAddr) -> Result<(TcpStream, BufTcp, [u8; RECV_SIZE])> {
tcpstream_ip_with_timeout(ip, TIMEOUT)
}
pub fn tcpstream_ip_with_timeout(
ip: &SocketAddr,
timeout: Duration,
) -> Result<(TcpStream, BufTcp, [u8; RECV_SIZE])> {
let stream = TcpStream::connect_timeout(ip, timeout)?;
stream.set_read_timeout(Some(timeout))?;
stream.set_write_timeout(Some(timeout))?;
let recv = [0; RECV_SIZE];
let buffer = BufReader::new(stream.try_clone()?);
Ok((stream, buffer, recv))
}