use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
use std::{
io::{Read, Write},
net::{Shutdown, SocketAddr, TcpStream},
};
#[derive(Serialize)]
pub(crate) struct Request {
controller_name: String,
controller_type: String,
d2c_port: u16,
#[serde(skip_serializing_if = "Option::is_some")]
arstream2_client_stream_port: Option<u16>,
#[serde(skip_serializing_if = "Option::is_some")]
arstream2_client_control_port: Option<u16>,
}
#[derive(Deserialize, Debug)]
pub(crate) struct Response {
#[serde(default)]
pub arstream_fragment_maximum_number: Option<u8>,
#[serde(default)]
pub arstream_fragment_size: Option<u16>,
#[serde(default)]
pub arstream_max_ack_interval: Option<i8>,
pub arstream2_server_stream_port: u16,
pub arstream2_server_control_port: u16,
pub c2d_port: u16,
pub c2d_update_port: u16,
pub c2d_user_port: u16,
pub status: i8,
}
pub(crate) fn perform_handshake(target: SocketAddr, d2c_port: u16) -> Result<Response> {
let request = Request {
controller_name: "arsdk-rs".to_string(),
controller_type: "computer".to_string(),
d2c_port,
arstream2_client_stream_port: Some(44445),
arstream2_client_control_port: Some(44446),
};
println!("connecting controller {}", request.controller_name,);
let mut handshake_stream = retry(10, target)?;
let request_string = serde_json::to_string(&request)?;
handshake_stream.write_all(&request_string.as_bytes())?;
let mut response_string = String::new();
handshake_stream.read_to_string(&mut response_string)?;
let response_string = response_string.trim_end_matches('\u{0}');
handshake_stream.shutdown(Shutdown::Both)?;
let response: Response = serde_json::from_str(&response_string)?;
if response.status != 0 {
anyhow!("connection refused - {:?}", response);
}
Ok(response)
}
fn retry(times: usize, target: SocketAddr) -> Result<TcpStream> {
let timeout = std::time::Duration::from_secs(2);
let mut retry = 0;
let mut res = TcpStream::connect_timeout(&target, timeout);
while res.is_err() && retry < times {
retry += 1;
res = TcpStream::connect_timeout(&target, timeout);
}
res.map_err(std::convert::Into::into)
}