use super::config::KcpSessionConfig;
use super::error::{KcpTokioError, KcpTokioResult};
use super::session::KcpSession;
use super::transport::{KcpTransport, KcpUdpTransport};
use std::future::Future;
use std::io;
use std::net::SocketAddr;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::net::UdpSocket;
pub struct KcpStream {
session: KcpSession,
read_buf: Vec<u8>,
read_pos: usize,
read_len: usize,
}
impl KcpStream {
pub async fn connect<A: tokio::net::ToSocketAddrs>(
addr: A,
config: KcpSessionConfig,
) -> KcpTokioResult<Self> {
Self::connect_with_conv(addr, config, rand_conv()).await
}
pub async fn connect_with_conv<A: tokio::net::ToSocketAddrs>(
addr: A,
config: KcpSessionConfig,
conv: u32,
) -> KcpTokioResult<Self> {
let remote_addr = tokio::net::lookup_host(addr).await?.next().ok_or_else(|| {
KcpTokioError::ConnectionFailed("could not resolve address".to_string())
})?;
let local_addr = if remote_addr.is_ipv4() {
"0.0.0.0:0"
} else {
"[::]:0"
};
let socket = Arc::new(UdpSocket::bind(local_addr).await?);
let transport = Arc::new(KcpUdpTransport::new(socket.clone()));
let recv_buf_size = config.recv_buf_size;
let session = KcpSession::new(conv, socket, transport, remote_addr, config)?;
Ok(Self {
session,
read_buf: vec![0u8; recv_buf_size],
read_pos: 0,
read_len: 0,
})
}
pub async fn connect_with_transport<A: tokio::net::ToSocketAddrs>(
addr: A,
config: KcpSessionConfig,
transport: Arc<dyn KcpTransport>,
socket: Arc<UdpSocket>,
conv: u32,
) -> KcpTokioResult<Self> {
let remote_addr = tokio::net::lookup_host(addr).await?.next().ok_or_else(|| {
KcpTokioError::ConnectionFailed("could not resolve address".to_string())
})?;
let recv_buf_size = config.recv_buf_size;
let session = KcpSession::new(conv, socket, transport, remote_addr, config)?;
Ok(Self {
session,
read_buf: vec![0u8; recv_buf_size],
read_pos: 0,
read_len: 0,
})
}
pub(crate) fn from_session(session: KcpSession) -> Self {
let recv_buf_size = session.config().recv_buf_size;
Self {
session,
read_buf: vec![0u8; recv_buf_size],
read_pos: 0,
read_len: 0,
}
}
pub fn conv(&self) -> u32 {
self.session.conv()
}
pub fn remote_addr(&self) -> SocketAddr {
self.session.remote_addr()
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.session.socket().local_addr()
}
pub async fn send_kcp(&mut self, data: &[u8]) -> KcpTokioResult<usize> {
self.session.send(data)
}
pub async fn recv_kcp(&mut self) -> KcpTokioResult<Vec<u8>> {
self.session.recv_auto().await
}
pub async fn recv_kcp_buf(&mut self, buf: &mut [u8]) -> KcpTokioResult<usize> {
self.session.recv(buf).await
}
pub fn flush(&mut self) {
self.session.update();
}
pub fn close(&mut self) {
self.session.close();
}
pub fn is_closed(&self) -> bool {
self.session.is_closed()
}
}
impl AsyncRead for KcpStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
let this = self.get_mut();
if this.read_pos < this.read_len {
let remaining = &this.read_buf[this.read_pos..this.read_len];
let to_copy = remaining.len().min(buf.remaining());
buf.put_slice(&remaining[..to_copy]);
this.read_pos += to_copy;
return Poll::Ready(Ok(()));
}
this.read_pos = 0;
this.read_len = 0;
match this.session.try_recv(&mut this.read_buf) {
Ok(n) => {
let to_copy = n.min(buf.remaining());
buf.put_slice(&this.read_buf[..to_copy]);
if to_copy < n {
this.read_pos = to_copy;
this.read_len = n;
}
return Poll::Ready(Ok(()));
}
Err(KcpTokioError::Kcp(crate::core::KcpError::RecvWouldBlock)) => {}
Err(KcpTokioError::Closed) => return Poll::Ready(Ok(())),
Err(KcpTokioError::Timeout) => {
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::TimedOut,
"KCP session timed out",
)))
}
Err(KcpTokioError::Io(e)) => return Poll::Ready(Err(e)),
Err(e) => return Poll::Ready(Err(io::Error::other(e.to_string()))),
}
let socket = this.session.socket().clone();
let mut udp_buf = [0u8; 65536];
let mut read_buf = ReadBuf::new(&mut udp_buf);
match socket.poll_recv_from(cx, &mut read_buf) {
Poll::Ready(Ok(addr)) => {
let n = read_buf.filled().len();
if addr == this.session.remote_addr() {
if let Some(processed) = this
.session
.transport()
.process_incoming(&udp_buf[..n], addr)
{
this.session.input(&processed).ok();
}
}
this.session.update();
match this.session.try_recv(&mut this.read_buf) {
Ok(n) => {
let to_copy = n.min(buf.remaining());
buf.put_slice(&this.read_buf[..to_copy]);
if to_copy < n {
this.read_pos = to_copy;
this.read_len = n;
}
Poll::Ready(Ok(()))
}
Err(KcpTokioError::Kcp(crate::core::KcpError::RecvWouldBlock)) => {
cx.waker().wake_by_ref();
Poll::Pending
}
Err(KcpTokioError::Closed) => Poll::Ready(Ok(())),
Err(e) => Poll::Ready(Err(io::Error::other(e.to_string()))),
}
}
Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
Poll::Pending => {
this.session.update();
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
}
impl AsyncWrite for KcpStream {
fn poll_write(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
match self.session.send(buf) {
Ok(n) => Poll::Ready(Ok(n)),
Err(KcpTokioError::Closed) => Poll::Ready(Err(io::Error::new(
io::ErrorKind::BrokenPipe,
"KCP session closed",
))),
Err(KcpTokioError::Io(e)) => Poll::Ready(Err(e)),
Err(e) => Poll::Ready(Err(io::Error::other(e.to_string()))),
}
}
fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.session.update();
Poll::Ready(Ok(()))
}
fn poll_shutdown(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.session.close();
Poll::Ready(Ok(()))
}
}
fn rand_conv() -> u32 {
use std::time::SystemTime;
let seed = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
((seed ^ (seed >> 16) ^ (seed >> 32)) & 0xFFFFFFFF) as u32
}
use std::time::Duration;
use tokio::sync::Mutex as TokioMutex;
enum RecvSource {
Socket,
Channel(tokio::sync::mpsc::Receiver<Vec<u8>>),
}
pub struct OwnedReadHalf {
session: Arc<TokioMutex<KcpSession>>,
socket: Arc<UdpSocket>,
transport: Arc<dyn KcpTransport>,
remote_addr: SocketAddr,
recv_source: RecvSource,
udp_recv_buf: Vec<u8>,
flush_interval: Duration,
read_buf: Vec<u8>,
read_pos: usize,
read_len: usize,
}
pub struct OwnedWriteHalf {
session: Arc<TokioMutex<KcpSession>>,
}
impl KcpStream {
pub fn into_split(mut self) -> (OwnedReadHalf, OwnedWriteHalf) {
let socket = self.session.socket().clone();
let transport = self.session.transport().clone();
let remote_addr = self.session.remote_addr();
let flush_interval = self.session.config().flush_interval;
let recv_buf_size = self.session.config().recv_buf_size;
let recv_source = match self.session.take_channel_receiver() {
Some(rx) => RecvSource::Channel(rx),
None => RecvSource::Socket,
};
let session = Arc::new(TokioMutex::new(self.session));
let read_half = OwnedReadHalf {
session: session.clone(),
socket,
transport,
remote_addr,
recv_source,
udp_recv_buf: vec![0u8; recv_buf_size],
flush_interval,
read_buf: self.read_buf,
read_pos: self.read_pos,
read_len: self.read_len,
};
let write_half = OwnedWriteHalf { session };
(read_half, write_half)
}
}
impl OwnedReadHalf {
pub async fn recv_kcp(&mut self) -> KcpTokioResult<Vec<u8>> {
loop {
{
let mut session = self.session.lock().await;
match session.try_recv_auto() {
Ok(data) => return Ok(data),
Err(KcpTokioError::Kcp(crate::core::KcpError::RecvWouldBlock)) => {}
Err(e) => return Err(e),
}
if session.is_timed_out() {
session.close();
return Err(KcpTokioError::Timeout);
}
if session.is_closed() {
return Err(KcpTokioError::Closed);
}
}
self.wait_for_data().await?;
}
}
pub async fn recv_kcp_buf(&mut self, buf: &mut [u8]) -> KcpTokioResult<usize> {
loop {
{
let mut session = self.session.lock().await;
match session.try_recv(buf) {
Ok(n) => return Ok(n),
Err(KcpTokioError::Kcp(crate::core::KcpError::RecvWouldBlock)) => {}
Err(e) => return Err(e),
}
if session.is_timed_out() {
session.close();
return Err(KcpTokioError::Timeout);
}
if session.is_closed() {
return Err(KcpTokioError::Closed);
}
}
self.wait_for_data().await?;
}
}
async fn wait_for_data(&mut self) -> KcpTokioResult<()> {
let received_data = match &mut self.recv_source {
RecvSource::Socket => {
tokio::select! {
result = self.socket.recv_from(&mut self.udp_recv_buf) => {
match result {
Ok((n, addr)) if addr == self.remote_addr => {
self.transport.process_incoming(&self.udp_recv_buf[..n], addr)
}
Ok(_) => None,
Err(ref e) if e.kind() == io::ErrorKind::ConnectionReset => None,
Err(e) => return Err(e.into()),
}
}
_ = tokio::time::sleep(self.flush_interval) => None,
}
}
RecvSource::Channel(rx) => {
tokio::select! {
pkt = rx.recv() => {
match pkt {
Some(data) => {
self.transport.process_incoming(&data, self.remote_addr)
}
None => return Err(KcpTokioError::Closed),
}
}
_ = tokio::time::sleep(self.flush_interval) => None,
}
}
};
{
let mut session = self.session.lock().await;
if let Some(data) = received_data {
session.input(&data).ok();
}
session.update();
} Ok(())
}
pub async fn conv(&self) -> u32 {
self.session.lock().await.conv()
}
pub fn remote_addr(&self) -> SocketAddr {
self.remote_addr
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.socket.local_addr()
}
}
impl AsyncRead for OwnedReadHalf {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
let this = self.get_mut();
if this.read_pos < this.read_len {
let remaining = &this.read_buf[this.read_pos..this.read_len];
let to_copy = remaining.len().min(buf.remaining());
buf.put_slice(&remaining[..to_copy]);
this.read_pos += to_copy;
return Poll::Ready(Ok(()));
}
this.read_pos = 0;
this.read_len = 0;
let mut lock_fut = Box::pin(this.session.lock());
match lock_fut.as_mut().poll(cx) {
Poll::Ready(mut session) => {
match session.try_recv(&mut this.read_buf) {
Ok(n) => {
let to_copy = n.min(buf.remaining());
buf.put_slice(&this.read_buf[..to_copy]);
if to_copy < n {
this.read_pos = to_copy;
this.read_len = n;
}
return Poll::Ready(Ok(()));
}
Err(KcpTokioError::Kcp(crate::core::KcpError::RecvWouldBlock)) => {}
Err(KcpTokioError::Closed) => return Poll::Ready(Ok(())),
Err(KcpTokioError::Timeout) => {
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::TimedOut,
"KCP session timed out",
)))
}
Err(e) => return Poll::Ready(Err(io::Error::other(e.to_string()))),
}
session.update();
}
Poll::Pending => {}
}
let mut udp_buf = [0u8; 65536];
let mut read_buf = ReadBuf::new(&mut udp_buf);
match this.socket.poll_recv_from(cx, &mut read_buf) {
Poll::Ready(Ok(addr)) => {
let n = read_buf.filled().len();
if addr == this.remote_addr {
let processed = this.transport.process_incoming(&udp_buf[..n], addr);
let mut lock_fut = Box::pin(this.session.lock());
if let Poll::Ready(mut session) = lock_fut.as_mut().poll(cx) {
if let Some(data) = processed {
session.input(&data).ok();
}
session.update();
match session.try_recv(&mut this.read_buf) {
Ok(n) => {
let to_copy = n.min(buf.remaining());
buf.put_slice(&this.read_buf[..to_copy]);
if to_copy < n {
this.read_pos = to_copy;
this.read_len = n;
}
return Poll::Ready(Ok(()));
}
Err(KcpTokioError::Kcp(crate::core::KcpError::RecvWouldBlock)) => {}
Err(KcpTokioError::Closed) => return Poll::Ready(Ok(())),
Err(e) => return Poll::Ready(Err(io::Error::other(e.to_string()))),
}
}
}
}
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
Poll::Pending => {}
}
cx.waker().wake_by_ref();
Poll::Pending
}
}
impl OwnedWriteHalf {
pub async fn send_kcp(&mut self, data: &[u8]) -> KcpTokioResult<usize> {
let mut session = self.session.lock().await;
session.send(data)
}
pub async fn flush(&mut self) {
let mut session = self.session.lock().await;
session.update();
}
pub async fn close(&mut self) {
let mut session = self.session.lock().await;
session.close();
}
pub async fn is_closed(&self) -> bool {
self.session.lock().await.is_closed()
}
pub async fn conv(&self) -> u32 {
self.session.lock().await.conv()
}
}
impl AsyncWrite for OwnedWriteHalf {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
let this = self.get_mut();
let mut lock_fut = Box::pin(this.session.lock());
match lock_fut.as_mut().poll(cx) {
Poll::Ready(mut session) => match session.send(buf) {
Ok(n) => Poll::Ready(Ok(n)),
Err(KcpTokioError::Closed) => Poll::Ready(Err(io::Error::new(
io::ErrorKind::BrokenPipe,
"KCP session closed",
))),
Err(KcpTokioError::Io(e)) => Poll::Ready(Err(e)),
Err(e) => Poll::Ready(Err(io::Error::other(e.to_string()))),
},
Poll::Pending => Poll::Pending,
}
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let this = self.get_mut();
let mut lock_fut = Box::pin(this.session.lock());
match lock_fut.as_mut().poll(cx) {
Poll::Ready(mut session) => {
session.update();
Poll::Ready(Ok(()))
}
Poll::Pending => Poll::Pending,
}
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let this = self.get_mut();
let mut lock_fut = Box::pin(this.session.lock());
match lock_fut.as_mut().poll(cx) {
Poll::Ready(mut session) => {
session.close();
Poll::Ready(Ok(()))
}
Poll::Pending => Poll::Pending,
}
}
}