use std::thread;
use std::io::{Error, Result, ErrorKind};
use std::net::{IpAddr, SocketAddr, UdpSocket};
use std::sync::{Arc, atomic::{AtomicBool, AtomicUsize, Ordering}};
use std::time::Duration;
use futures::future::{FutureExt, LocalBoxFuture};
use crossbeam_channel::{Sender, Receiver, bounded, unbounded};
use async_channel::unbounded as async_unbounded;
use socket2::{Domain, Type, Protocol, Socket as SocketDef};
use log::{debug, warn, error};
use pi_async_rt::rt::serial_local_thread::LocalTaskRuntime;
use crate::{Socket, AsyncService, SocketHandle, SocketHandleRef, UdpEvent, TaskResult,
connect::{BlockingUdpSocket, new_blocking_udp_socket}};
const DEFAULT_THREAD_STACK_SIZE: usize = 2 * 1024 * 1024;
const MIN_RECV_BUFFER_LEN: usize = 0xffff;
const MIN_SEND_BUFFER_LEN: usize = 0xffff;
const MIN_READ_PACKET_BLOCK_LEN: usize = 4096;
const MIN_WRITE_PACKET_BLOCK_LEN: usize = 4096;
lazy_static! {
static ref TERMINAL_SOCKET_UID_ALLOCTOR: AtomicUsize = AtomicUsize::new(1);
}
pub fn alloc_terminal_socket_uid() -> usize {
TERMINAL_SOCKET_UID_ALLOCTOR.fetch_add(1, Ordering::Relaxed)
}
pub struct UdpTerminal(Sender<UdpEvent>, BlockingUdpSocket, Option<SocketAddr>, Receiver<()>);
impl UdpTerminal {
pub fn bind(address: SocketAddr, rt: LocalTaskRuntime<()>, mut recv_buffer_size: usize, mut send_buffer_size: usize, mut read_packet_size: usize, mut write_packet_size: usize, mut service: Box<dyn AsyncService>, ) -> Result<Self> {
let domain = if address.is_ipv4() {
Domain::IPV4
} else {
Domain::IPV6
};
let socket = SocketDef::new(domain, Type::DGRAM, Some(Protocol::UDP))?;
if recv_buffer_size < MIN_RECV_BUFFER_LEN {
recv_buffer_size = MIN_RECV_BUFFER_LEN
}
socket.set_recv_buffer_size(recv_buffer_size)?;
if send_buffer_size < MIN_SEND_BUFFER_LEN {
send_buffer_size = MIN_SEND_BUFFER_LEN;
}
socket.set_send_buffer_size(send_buffer_size)?;
service.bind_runtime(rt.clone());
socket.bind(&address.into())?;
let socket: UdpSocket = socket.into();
let running = Arc::new(AtomicBool::new(true));
let (event_sender, event_receiver) = unbounded();
let udp_socket = new_blocking_udp_socket(address.clone(),
None,
Arc::new(socket),
read_packet_size,
write_packet_size,
event_sender.clone());
let running_copy = running.clone();
let socket_handle = udp_socket.clone();
let event_sender_copy = event_sender.clone();
thread::Builder::new()
.name(format!("UDP-Socket-Receiver-{}", address))
.stack_size(DEFAULT_THREAD_STACK_SIZE)
.spawn(move || {
let socket_handle_ref = socket_handle.as_ref();
while running_copy.load(Ordering::Relaxed) {
if let Err(e) = recv_loop(socket_handle_ref,
&event_sender_copy) {
debug!("{:?}", e);
}
}
warn!("Udp receive loop already exited, local: {:?}", socket_handle.get_local());
});
let rt_copy = rt.clone();
let socket_handle = udp_socket.clone();
let (closed_send, closed_recv) = bounded(1);
rt.spawn(event_loop(rt_copy,
event_receiver,
socket_handle,
service,
running,
closed_send));
Ok(UdpTerminal(event_sender,
udp_socket,
None,
closed_recv))
}
pub fn bind_without_service(address: SocketAddr, rt: LocalTaskRuntime<()>, mut recv_buffer_size: usize, mut send_buffer_size: usize, mut read_packet_size: usize, mut write_packet_size: usize, ) -> Result<(Self, impl FnOnce(Box<dyn AsyncService>))> {
let domain = if address.is_ipv4() {
Domain::IPV4
} else {
Domain::IPV6
};
let socket = SocketDef::new(domain, Type::DGRAM, Some(Protocol::UDP))?;
if recv_buffer_size < MIN_RECV_BUFFER_LEN {
recv_buffer_size = MIN_RECV_BUFFER_LEN
}
socket.set_recv_buffer_size(recv_buffer_size)?;
if send_buffer_size < MIN_SEND_BUFFER_LEN {
send_buffer_size = MIN_SEND_BUFFER_LEN;
}
socket.set_send_buffer_size(send_buffer_size)?;
socket.bind(&address.into())?;
let socket: UdpSocket = socket.into();
let running = Arc::new(AtomicBool::new(true));
let (event_sender, event_receiver) = unbounded();
let udp_socket = new_blocking_udp_socket(address.clone(),
None,
Arc::new(socket),
read_packet_size,
write_packet_size,
event_sender.clone());
let running_copy = running.clone();
let socket_handle = udp_socket.clone();
let event_sender_copy = event_sender.clone();
thread::Builder::new()
.name(format!("UDP-Socket-Receiver-{}", address))
.stack_size(DEFAULT_THREAD_STACK_SIZE)
.spawn(move || {
let socket_handle_ref = socket_handle.as_ref();
while running_copy.load(Ordering::Relaxed) {
if let Err(e) = recv_loop(socket_handle_ref,
&event_sender_copy) {
debug!("{:?}", e);
}
}
warn!("Udp receive loop already exited, local: {:?}", socket_handle.get_local());
});
let socket_handle = udp_socket.clone();
let (closed_send, closed_recv) = bounded(1);
let func = move |mut service: Box<dyn AsyncService>| {
let rt_copy = rt.clone();
service.bind_runtime(rt.clone());
rt.spawn(event_loop(rt_copy,
event_receiver,
socket_handle,
service,
running,
closed_send));
};
Ok((UdpTerminal(event_sender,
udp_socket,
None,
closed_recv),
func))
}
pub fn is_client(&self) -> bool {
true
}
pub fn is_server(&self) -> bool {
self.2.is_none()
}
pub fn get_uid(&self) -> usize {
self.1.get_uid()
}
pub fn connect_to_peer(&self, _to: SocketAddr) -> Result<BlockingUdpSocket> {
if self.2.is_some() {
Err(Error::new(ErrorKind::AlreadyExists,
format!("Connect to peer failed, uid: {:?}, remote: {:?}, local: {:?}, reason: already connect to server",
self.1.get_uid(),
self.2,
self.1.get_local())))
} else {
Ok(self.1.clone())
}
}
pub fn reconnect_to_peer(&self) -> Result<BlockingUdpSocket> {
if self.2.is_some() {
Err(Error::new(ErrorKind::AlreadyExists,
format!("Connect to peer failed, uid: {:?}, remote: {:?}, local: {:?}, reason: already connect to server",
self.1.get_uid(),
self.2,
self.1.get_local())))
} else {
Ok(self.1.clone())
}
}
pub fn connect_to_server(&mut self, to: SocketAddr) -> Result<BlockingUdpSocket> {
if self.2.is_some() {
Err(Error::new(ErrorKind::AlreadyExists,
format!("Connect to peer failed, uid: {:?}, remote: {:?}, local: {:?}, reason: already connect to server",
self.1.get_uid(),
self.2,
self.1.get_local())))
} else {
self.1.connect(to)?;
self.2 = Some(to);
Ok(self.1.clone())
}
}
pub fn spawn(&self, task: LocalBoxFuture<'static, TaskResult>) {
self.1.spawn(task)
}
pub fn close_connection(&self, reason: Result<()>) -> Result<()> {
self.1.close(reason)
}
pub fn close(self, reason: Result<()>) -> Result<()> {
let mut server_local_address = self.1.get_local().clone();
if let Err(e) = self.0.send(UdpEvent::CloseListener(reason)) {
Err(Error::new(ErrorKind::Other,
format!("Close Udp listener failed, reason: {:?}",
e)))
} else {
if let Err(e) = self.3.recv_timeout(Duration::from_millis(5000 )) {
Err(Error::new(ErrorKind::Other,
format!("Close Udp listener failed, reason: {:?}",
e)))
} else {
let mut tmp_client_address = server_local_address.clone();
tmp_client_address.set_port(0);
let tmp_client = UdpSocket::bind(tmp_client_address)?;
server_local_address.set_ip(IpAddr::from([127, 0, 0, 1]));
tmp_client.connect(server_local_address)?;
let _ = tmp_client.send(&[0])?;
Ok(())
}
}
}
}
#[inline]
fn recv_loop(socket_handle: SocketHandleRef,
event_sender: &Sender<UdpEvent>) -> Result<()> {
match socket_handle.recv() {
Err(e) => {
Err(Error::new(ErrorKind::Other,
format!("Udp receive loop failed, uid: {:?}, local: {:?}, reason: {:?}",
socket_handle.get_uid(),
socket_handle.get_local(),
e)))
},
Ok((packet, peer)) => {
if let Err(e) = event_sender.send(UdpEvent::Recv(socket_handle.get_uid(), packet, peer)) {
Err(Error::new(ErrorKind::Other,
format!("Udp receive loop failed, uid: {:?}, local: {:?}, reason: {:?}",
socket_handle.get_uid(),
socket_handle.get_local(),
e)))
} else {
Ok(())
}
},
}
}
#[inline]
fn event_loop(rt: LocalTaskRuntime<()>,
event_receiver: Receiver<UdpEvent>,
socket_handle: SocketHandle,
service: Box<dyn AsyncService>,
recv_loop_running: Arc<AtomicBool>,
closed_send: Sender<()>) -> LocalBoxFuture<'static, ()> {
async move {
service
.handle_binded(socket_handle.clone(), Ok(()))
.await;
loop {
match event_receiver.recv() {
Err(e) => {
error!("Udp event loop failed, local: {:?}, reason: {:?}",
socket_handle.get_local(),
e);
service
.handle_received(socket_handle.clone(),
Err(Error::new(ErrorKind::Other, format!("Udp event loop failed, local: {:?}, reason: {:?}",
socket_handle.get_local(),
e)))).await;
return;
},
Ok(event) => {
match event {
UdpEvent::Recv(_uid, packet, peer) => {
service
.handle_received(socket_handle.clone(),
Ok((packet, peer))).await;
},
UdpEvent::Sent(_uid, packet, peer) => {
let result = socket_handle.send(packet.into(), peer);
service.handle_sended(socket_handle.clone(), result).await;
},
UdpEvent::Task(task) => {
if let TaskResult::ExitReady = task.await {
warn!("Event loop already exited, local: {:?}", socket_handle.get_local());
closed_send.send(()); return;
}
},
UdpEvent::CloseConnection(uid, reason) => {
warn!("Close udp socket, uid: {:?}, local: {:?}, remote: {:?}, reason: {:?}",
uid,
socket_handle.get_local(),
socket_handle.get_remote(),
reason);
service
.handle_closed(socket_handle.clone(),
reason)
.await;
recv_loop_running.store(false, Ordering::Relaxed);
socket_handle.spawn(async move {
TaskResult::ExitReady
}.boxed_local());
},
UdpEvent::CloseListener(reason) => {
socket_handle.close(reason); },
}
},
}
}
}.boxed_local()
}