bufferbloat fixes and reconnect
This commit is contained in:
@@ -132,11 +132,11 @@ impl TcpConnection {
|
||||
|
||||
fn poll_and_process(&mut self, socket: &mut tcp::Socket) {
|
||||
let current_rtt = GLOBAL_MIN_RTT.load(Ordering::Relaxed);
|
||||
let rtt = Duration::from_millis(current_rtt as u64);
|
||||
let rtt = smoltcp::time::Duration::from_millis(current_rtt as u64);
|
||||
socket.set_tunnel_rtt(rtt);
|
||||
|
||||
// Читаем из браузера в Туннель
|
||||
if self.core.tx.capacity() > 0 {
|
||||
if self.core.tx.capacity() > 200 {
|
||||
while socket.can_recv() {
|
||||
if let Ok(n) = socket.peek_slice(&mut self.chunk_buf) {
|
||||
if n == 0 {
|
||||
@@ -164,9 +164,20 @@ impl TcpConnection {
|
||||
|
||||
// Читаем из Туннеля в браузер
|
||||
if !self.server_eof {
|
||||
// 🔥 ФИКС БУФЕРБЛОАТА НА LTE: Ограничиваем локальный буфер ровно до 1 МБ!
|
||||
// Это сбросит пинг на мобилках с 4500 мс до нормальных значений
|
||||
while self.pending_bytes < 1024 * 1024 {
|
||||
// 🔥 ДИНАМИЧЕСКИЙ BACKPRESSURE (Auto-Scaling Buffer)
|
||||
// В зависимости от текущего пинга всей системы, мы разрешаем
|
||||
// локальному сокету буферизовать разное количество данных.
|
||||
let dynamic_limit = if current_rtt < 50 {
|
||||
2 * 1024 * 1024 // 2 MB: Отличный Wi-Fi, качаем на максимум
|
||||
} else if current_rtt < 150 {
|
||||
1024 * 1024 // 1 MB: Хороший LTE
|
||||
} else if current_rtt < 300 {
|
||||
256 * 1024 // 256 KB: Загруженный 3G/LTE, начинаем зажимать
|
||||
} else {
|
||||
128 * 1024 // 128 KB: Сеть умирает (или сменилась вышка), спасаем пинг
|
||||
};
|
||||
|
||||
while self.pending_bytes < dynamic_limit {
|
||||
match self.core.rx.try_recv() {
|
||||
Ok(data) => {
|
||||
self.pending_bytes += data.len();
|
||||
@@ -213,7 +224,6 @@ impl TcpConnection {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn app_pending_out_size(&self) -> usize {
|
||||
self.pending_bytes
|
||||
}
|
||||
|
||||
@@ -75,9 +75,9 @@ impl TargetResolver {
|
||||
}
|
||||
|
||||
pub struct ConnectionManager {
|
||||
tracker: SessionTracker,
|
||||
pub tracker: SessionTracker, // 🔥 Теперь pub для Engine
|
||||
resolver: TargetResolver,
|
||||
tx_to_tunnel: mpsc::Sender<RawCastFrame>, // 🔥 Bounded
|
||||
tx_to_tunnel: mpsc::Sender<RawCastFrame>,
|
||||
factory: Arc<dyn SocketProvider>,
|
||||
pending_connects: DashMap<FlowKey, Instant>,
|
||||
}
|
||||
@@ -97,33 +97,6 @@ impl ConnectionManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_inject_inbound(&mut self, frame: RawCastFrame) -> Result<(), RawCastFrame> {
|
||||
if frame.event == RawCastEvent::Close {
|
||||
self.tracker.close_tunnel_session(frame.socket_id);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if frame.event != RawCastEvent::Data {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(tx) = self.tracker.get_inbound_tx(frame.socket_id) {
|
||||
// 🔥 Защита от переполнения: Если очередь Bounded канала полна,
|
||||
// мы тихо удаляем пакет. Очередь освободится, и TCP/BBR сделает свое дело (надежная доставка).
|
||||
// Нельзя закрывать соединение!
|
||||
if let Err(e) = tx.try_send(frame.payload.clone()) {
|
||||
if let mpsc::error::TrySendError::Closed(_) = e {
|
||||
self.tracker.close_tunnel_session(frame.socket_id);
|
||||
} else {
|
||||
// Backpressure in action: drop the packet, TCP will handle it
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_sockets(factory: &dyn SocketProvider, n_icmp: usize) -> SocketSet<'static> {
|
||||
factory.create_base_set(n_icmp)
|
||||
}
|
||||
|
||||
+28
-18
@@ -1,6 +1,6 @@
|
||||
use netrunner_core::net::ClientHandler;
|
||||
use netrunner_core::net::NetworkConfig;
|
||||
use netrunner_core::rawcast::RawCastFrame;
|
||||
use netrunner_core::rawcast::{RawCastEvent, RawCastFrame};
|
||||
use smoltcp::iface::PollResult;
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::wire::{IpAddress, IpCidr};
|
||||
@@ -38,7 +38,7 @@ pub struct Engine {
|
||||
to_smoltcp_tx: UnboundedSender<TokenBuffer>,
|
||||
from_smoltcp_rx: Option<UnboundedReceiver<TokenBuffer>>,
|
||||
avail: Arc<AtomicBool>,
|
||||
rx_from_tunnel: Receiver<RawCastFrame>, // 🔥 Bounded
|
||||
rx_from_tunnel: Option<Receiver<RawCastFrame>>, // 🔥 Теперь Option, чтобы отдать его в таску
|
||||
factory: Arc<dyn SocketProvider>,
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ impl Engine {
|
||||
config: Config,
|
||||
caps: DeviceCapabilities,
|
||||
dns_handler: DnsHandler,
|
||||
tx_to_tunnel: Sender<RawCastFrame>, // 🔥 Bounded
|
||||
rx_from_tunnel: Receiver<RawCastFrame>, // 🔥 Bounded
|
||||
tx_to_tunnel: Sender<RawCastFrame>,
|
||||
rx_from_tunnel: Receiver<RawCastFrame>,
|
||||
factory: Arc<dyn SocketProvider>,
|
||||
) -> Self {
|
||||
let now = Engine::current_time();
|
||||
@@ -57,7 +57,6 @@ impl Engine {
|
||||
let interface = Interface::new(config, &mut device, now);
|
||||
|
||||
let socket_set = ConnectionManager::setup_sockets(factory.as_ref(), 2);
|
||||
|
||||
let manager = ConnectionManager::new(dns_handler, tx_to_tunnel, factory.clone());
|
||||
|
||||
Self {
|
||||
@@ -68,7 +67,7 @@ impl Engine {
|
||||
from_smoltcp_rx: Some(from_smoltcp_rx),
|
||||
avail,
|
||||
manager,
|
||||
rx_from_tunnel,
|
||||
rx_from_tunnel: Some(rx_from_tunnel),
|
||||
factory,
|
||||
}
|
||||
}
|
||||
@@ -86,6 +85,27 @@ impl Engine {
|
||||
|
||||
let mut last_stats_log = StdInstant::now();
|
||||
|
||||
// 🔥 ФИКС: Выносим чтение из туннеля в отдельную изолированную задачу!
|
||||
// Это позволяет использовать `.await` при отправке данных в локальный сокет smoltcp.
|
||||
// Если локальный сокет забит, эта задача мягко "засыпает", создавая идеальный Backpressure,
|
||||
// при этом НЕ БЛОКИРУЯ основной цикл обработки интерфейсов Engine.
|
||||
let inbound_map = self.manager.tracker.inbound_tx.clone();
|
||||
let mut rx_tunnel = self.rx_from_tunnel.take().unwrap();
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Some(frame) = rx_tunnel.recv().await {
|
||||
if frame.event == RawCastEvent::Close {
|
||||
inbound_map.remove(&frame.socket_id);
|
||||
} else if frame.event == RawCastEvent::Data {
|
||||
if let Some(tx) = inbound_map.get(&frame.socket_id) {
|
||||
// ZERO DROP, PERFECT BACKPRESSURE.
|
||||
// Ожидаем, пока локальный сокет не освободит место.
|
||||
let _ = tx.send(frame.payload).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
loop {
|
||||
let mut repeat_poll = true;
|
||||
while repeat_poll {
|
||||
@@ -109,21 +129,10 @@ impl Engine {
|
||||
.map(|d| std::cmp::min(Duration::from_micros(d.micros()), Duration::from_millis(5)))
|
||||
.unwrap_or(Duration::from_millis(5));
|
||||
|
||||
// Основной цикл теперь занимается ТОЛЬКО прогонкой данных: TUN <-> smoltcp
|
||||
tokio::select! {
|
||||
_ = sleep(sleep_time) => {}
|
||||
|
||||
msg = self.rx_from_tunnel.recv() => {
|
||||
if let Some(frame) = msg {
|
||||
let _ = self.manager.try_inject_inbound(frame);
|
||||
let mut count = 0;
|
||||
while let Ok(frame) = self.rx_from_tunnel.try_recv() {
|
||||
let _ = self.manager.try_inject_inbound(frame);
|
||||
count += 1;
|
||||
if count >= 64 { break; }
|
||||
}
|
||||
} else { break; }
|
||||
}
|
||||
|
||||
msg = tun_to_engine_rx.recv() => {
|
||||
if let Some(token) = msg {
|
||||
self.manager.try_create_socket_from_packet(&token, &mut self.socket_set);
|
||||
@@ -142,6 +151,7 @@ impl Engine {
|
||||
.poll(now, &mut self.device, &mut self.socket_set)
|
||||
}
|
||||
|
||||
// ... [Остальные методы spawn_tun_reader, spawn_tun_writer и set_route остаются без изменений] ...
|
||||
fn spawn_tun_reader(
|
||||
mut reader: DeviceReader,
|
||||
to_engine: mpsc::UnboundedSender<TokenBuffer>,
|
||||
|
||||
@@ -4,11 +4,13 @@ use std::{
|
||||
};
|
||||
|
||||
use bytes::Bytes;
|
||||
use dashmap::DashMap;
|
||||
use smoltcp::{
|
||||
iface::{SocketHandle, SocketSet},
|
||||
socket::Socket,
|
||||
wire::IpAddress,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::net::connection::{TcpConnection, UdpConnection};
|
||||
@@ -17,7 +19,7 @@ pub struct SessionTracker {
|
||||
last_activity: HashMap<SocketHandle, StdInstant>,
|
||||
active_tcp: HashMap<SocketHandle, TcpConnection>,
|
||||
active_udp: HashMap<SocketHandle, UdpConnection>,
|
||||
inbound_tx: HashMap<u64, mpsc::Sender<Bytes>>, // 🔥 Bounded
|
||||
pub inbound_tx: Arc<DashMap<u64, mpsc::Sender<Bytes>>>, // 🔥 Теперь Arc<DashMap>
|
||||
handle_to_id: HashMap<SocketHandle, u64>,
|
||||
|
||||
id_to_handle: HashMap<u64, SocketHandle>,
|
||||
@@ -32,7 +34,7 @@ impl SessionTracker {
|
||||
last_activity: HashMap::new(),
|
||||
active_tcp: HashMap::new(),
|
||||
active_udp: HashMap::new(),
|
||||
inbound_tx: HashMap::new(),
|
||||
inbound_tx: Arc::new(DashMap::new()),
|
||||
handle_to_id: HashMap::new(),
|
||||
id_to_handle: HashMap::new(),
|
||||
pending_tcp: HashMap::new(),
|
||||
@@ -122,10 +124,6 @@ impl SessionTracker {
|
||||
self.last_activity.insert(handle, StdInstant::now());
|
||||
}
|
||||
|
||||
pub fn get_inbound_tx(&self, id: u64) -> Option<&mpsc::Sender<Bytes>> {
|
||||
self.inbound_tx.get(&id)
|
||||
}
|
||||
|
||||
pub fn close_tunnel_session(&mut self, id: u64) {
|
||||
self.inbound_tx.remove(&id);
|
||||
|
||||
|
||||
@@ -95,14 +95,7 @@ impl SocketProvider for SmolSocketFactory {
|
||||
fn reconfigure_tcp(&self, socket: &mut tcp::Socket, profile: TrafficProfile) {
|
||||
socket.set_nagle_enabled(false);
|
||||
|
||||
match profile {
|
||||
TrafficProfile::Interactive | TrafficProfile::Dns => {
|
||||
socket.set_ack_delay(None);
|
||||
}
|
||||
TrafficProfile::Bulk | TrafficProfile::Default => {
|
||||
socket.set_ack_delay(Some(Duration::from_millis(1)));
|
||||
}
|
||||
}
|
||||
socket.set_ack_delay(Some(Duration::from_millis(50)));
|
||||
|
||||
socket.set_congestion_control(CongestionControl::Bbr);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user