use std::{sync::Arc, time::Duration};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::TcpStream,
sync::{
Mutex,
mpsc::{self, Sender},
watch,
},
task::JoinHandle,
time::interval,
};
use tracing::{error, info, warn};
use crate::domain::model::handler_result::HandlerResult;
use crate::domain::model::input_buf_vo::InputBufVO;
use crate::infrastructure::protocol::big_buf_reader::BigBufReader;
use crate::infrastructure::tcp::stream::LynnStream;
use crate::{LynnError, const_config::DEFAULT_MAX_RECEIVE_BYTES_SIZE};
pub(super) type SharedWriteReceiver = Arc<Mutex<mpsc::Receiver<HandlerResult>>>;
#[inline(always)]
pub(super) fn spawn_connection_pumps(
stream: LynnStream,
message_header_mark: u16,
message_tail_mark: u16,
tx_read: Sender<InputBufVO>,
rx_write: SharedWriteReceiver,
close_rx: watch::Receiver<bool>,
) -> JoinHandle<()> {
let (read_half, write_half) = tokio::io::split(stream);
let write_handle = tokio::spawn(write_pump(
write_half,
message_header_mark,
message_tail_mark,
rx_write,
close_rx,
));
let read_handle = tokio::spawn(read_pump(
read_half,
message_header_mark,
message_tail_mark,
tx_read,
));
let _ = write_handle; read_handle
}
async fn read_pump(
mut read_half: tokio::io::ReadHalf<LynnStream>,
message_header_mark: u16,
message_tail_mark: u16,
tx_read: Sender<InputBufVO>,
) {
let mut buf = [0; DEFAULT_MAX_RECEIVE_BYTES_SIZE];
let mut big_buf = BigBufReader::new(message_header_mark, message_tail_mark);
'read_loop: loop {
match read_half.read(&mut buf).await {
Ok(0) => {
break;
},
Ok(n) => {
big_buf.extend_from_slice(&buf[..n]);
while big_buf.is_complete() {
let input_buf_vo = InputBufVO::new_without_socket_addr(big_buf.get_data());
if tx_read.send(input_buf_vo).await.is_err() {
break 'read_loop;
}
}
},
Err(e) => {
error!("read from server failed : {}", e);
break;
},
}
}
}
async fn write_pump(
mut write_half: tokio::io::WriteHalf<LynnStream>,
message_header_mark: u16,
message_tail_mark: u16,
rx_write: SharedWriteReceiver,
mut close_rx: watch::Receiver<bool>,
) {
loop {
let received = tokio::select! {
changed = close_rx.changed() => {
if changed.is_err() || *close_rx.borrow() {
let _ = write_half.shutdown().await;
break;
}
continue;
},
received = async { rx_write.lock().await.recv().await } => received,
};
match received {
Some(mut handler_result) => {
if !handler_result.is_with_mark() {
handler_result.set_marks(message_header_mark, message_tail_mark);
}
if let Some(response) = handler_result.get_response_data() {
if let Err(e) = write_half.write_all(&response).await {
error!("write to server failed - e: {:?}", e);
break;
}
} else {
warn!("nothing to send");
}
},
None => {
let _ = write_half.shutdown().await;
break;
},
}
}
}
#[inline(always)]
pub(super) fn spawn_check_heart(interval_time: u64, sender: mpsc::Sender<HandlerResult>) {
tokio::spawn(async move {
info!(
"Client - [check heart] start sucess!!! with [client_check_heart_interval:{}s]",
interval_time
);
let mut interval = interval(Duration::from_secs(interval_time));
loop {
interval.tick().await;
if !sender.is_closed() {
if let Err(e) = sender
.send(HandlerResult::new_with_send_heart_to_server())
.await
{
error!("send to server failed - e: {:?}", e)
}
} else {
break;
}
}
});
}