use super::config::KcpSessionConfig;
use super::error::{KcpTokioError, KcpTokioResult};
use super::transport::KcpTransport;
use crate::core::Kcp;
use std::io;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Instant;
use tokio::net::UdpSocket;
use tokio::sync::{mpsc, Mutex};
use tokio::time;
enum RecvMode {
Socket,
Channel(mpsc::Receiver<Vec<u8>>),
}
pub struct KcpSession {
kcp: Kcp,
socket: Arc<UdpSocket>,
transport: Arc<dyn KcpTransport>,
remote_addr: SocketAddr,
config: KcpSessionConfig,
udp_recv_buf: Vec<u8>,
start_time: Instant,
last_recv_time: Instant,
closed: bool,
recv_mode: RecvMode,
}
impl KcpSession {
pub fn new(
conv: u32,
socket: Arc<UdpSocket>,
transport: Arc<dyn KcpTransport>,
remote_addr: SocketAddr,
config: KcpSessionConfig,
) -> KcpTokioResult<Self> {
Self::new_inner(
conv,
socket,
transport,
remote_addr,
config,
RecvMode::Socket,
)
}
pub(crate) fn new_with_channel(
conv: u32,
socket: Arc<UdpSocket>,
transport: Arc<dyn KcpTransport>,
remote_addr: SocketAddr,
config: KcpSessionConfig,
pkt_rx: mpsc::Receiver<Vec<u8>>,
) -> KcpTokioResult<Self> {
Self::new_inner(
conv,
socket,
transport,
remote_addr,
config,
RecvMode::Channel(pkt_rx),
)
}
fn new_inner(
conv: u32,
socket: Arc<UdpSocket>,
transport: Arc<dyn KcpTransport>,
remote_addr: SocketAddr,
config: KcpSessionConfig,
recv_mode: RecvMode,
) -> KcpTokioResult<Self> {
let transport_for_kcp = transport.clone();
let remote = remote_addr;
let kcp = Kcp::with_config(
conv,
&config.kcp_config,
move |data: &[u8]| -> io::Result<usize> {
let processed = transport_for_kcp.process_outgoing(data, remote);
transport_for_kcp.try_send(&processed, remote)
},
)?;
let now = Instant::now();
Ok(Self {
kcp,
socket,
transport,
remote_addr,
udp_recv_buf: vec![0u8; config.recv_buf_size],
config,
start_time: now,
last_recv_time: now,
closed: false,
recv_mode,
})
}
fn current_ms(&self) -> u32 {
self.start_time.elapsed().as_millis() as u32
}
pub fn send(&mut self, data: &[u8]) -> KcpTokioResult<usize> {
if self.closed {
return Err(KcpTokioError::Closed);
}
let n = self.kcp.send(data)?;
if self.config.flush_write {
self.kcp.flush();
}
Ok(n)
}
pub fn try_recv(&mut self, buf: &mut [u8]) -> KcpTokioResult<usize> {
if self.closed {
return Err(KcpTokioError::Closed);
}
Ok(self.kcp.recv(buf)?)
}
pub fn try_recv_auto(&mut self) -> KcpTokioResult<Vec<u8>> {
if self.closed {
return Err(KcpTokioError::Closed);
}
let size = self.kcp.peeksize()?;
let mut buf = vec![0u8; size];
let n = self.kcp.recv(&mut buf)?;
buf.truncate(n);
Ok(buf)
}
pub fn input(&mut self, data: &[u8]) -> KcpTokioResult<()> {
self.last_recv_time = Instant::now();
self.kcp.input(data)?;
Ok(())
}
pub fn update(&mut self) {
let current = self.current_ms();
self.kcp.update(current);
}
pub fn is_timed_out(&self) -> bool {
self.config
.timeout
.is_some_and(|t| self.last_recv_time.elapsed() > t)
}
pub fn is_closed(&self) -> bool {
self.closed
}
pub fn close(&mut self) {
self.closed = true;
}
pub fn conv(&self) -> u32 {
self.kcp.conv()
}
pub fn remote_addr(&self) -> SocketAddr {
self.remote_addr
}
#[allow(dead_code)]
pub fn waitsnd(&self) -> u32 {
self.kcp.waitsnd()
}
pub fn socket(&self) -> &Arc<UdpSocket> {
&self.socket
}
pub fn transport(&self) -> &Arc<dyn KcpTransport> {
&self.transport
}
pub fn config(&self) -> &KcpSessionConfig {
&self.config
}
pub(crate) fn take_channel_receiver(&mut self) -> Option<mpsc::Receiver<Vec<u8>>> {
match std::mem::replace(&mut self.recv_mode, RecvMode::Socket) {
RecvMode::Channel(rx) => Some(rx),
RecvMode::Socket => None,
}
}
pub async fn recv(&mut self, buf: &mut [u8]) -> KcpTokioResult<usize> {
if self.closed {
return Err(KcpTokioError::Closed);
}
loop {
match self.kcp.recv(buf) {
Ok(n) => return Ok(n),
Err(crate::core::KcpError::RecvWouldBlock) => {}
Err(e) => return Err(e.into()),
}
self.wait_for_data().await?;
}
}
pub async fn recv_auto(&mut self) -> KcpTokioResult<Vec<u8>> {
if self.closed {
return Err(KcpTokioError::Closed);
}
loop {
match self.try_recv_auto() {
Ok(data) => return Ok(data),
Err(KcpTokioError::Kcp(crate::core::KcpError::RecvWouldBlock)) => {}
Err(e) => return Err(e),
}
self.wait_for_data().await?;
}
}
async fn wait_for_data(&mut self) -> KcpTokioResult<()> {
if self.is_timed_out() {
self.closed = true;
return Err(KcpTokioError::Timeout);
}
let flush_interval = self.config.flush_interval;
match &mut self.recv_mode {
RecvMode::Socket => {
tokio::select! {
result = self.socket.recv_from(&mut self.udp_recv_buf) => {
let (n, addr) = result?;
if addr == self.remote_addr {
self.last_recv_time = Instant::now();
if let Some(processed) = self.transport.process_incoming(&self.udp_recv_buf[..n], addr) {
self.kcp.input(&processed).ok();
}
}
}
_ = time::sleep(flush_interval) => {}
}
}
RecvMode::Channel(rx) => {
tokio::select! {
pkt = rx.recv() => {
match pkt {
Some(data) => {
self.last_recv_time = Instant::now();
if let Some(processed) = self.transport.process_incoming(&data, self.remote_addr) {
self.kcp.input(&processed).ok();
}
}
None => { self.closed = true; return Err(KcpTokioError::Closed); }
}
}
_ = time::sleep(flush_interval) => {}
}
}
}
self.update();
Ok(())
}
}
#[allow(dead_code)]
pub type SharedKcpSession = Arc<Mutex<KcpSession>>;