bufferbloat fixes, deadlocks and buf sizes change

This commit is contained in:
2026-04-16 13:14:16 +07:00
parent f5f1d20f54
commit 2d70f33257
12 changed files with 191 additions and 211 deletions
+49 -67
View File
@@ -136,78 +136,22 @@ impl TcpConnection {
socket.set_tunnel_rtt(rtt);
// Читаем из браузера в Туннель
if self.core.tx.capacity() > 200 {
while socket.can_recv() {
if let Ok(n) = socket.peek_slice(&mut self.chunk_buf) {
if n == 0 {
break;
}
let chunk = Bytes::copy_from_slice(&self.chunk_buf[..n]);
match self.core.tx.try_send(chunk) {
Ok(_) => {
socket.recv_slice(&mut self.chunk_buf[..n]).unwrap();
}
Err(mpsc::error::TrySendError::Full(_)) => {
break;
}
Err(mpsc::error::TrySendError::Closed(_)) => {
self.server_eof = true;
break;
}
}
} else {
while socket.can_recv() && self.core.tx.capacity() > 0 {
if let Ok(n) = socket.peek_slice(&mut self.chunk_buf) {
if n == 0 {
break;
}
}
}
let chunk = Bytes::copy_from_slice(&self.chunk_buf[..n]);
// Читаем из Туннеля в браузер
if !self.server_eof {
// 🔥 ДИНАМИЧЕСКИЙ 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();
self.pending_data.push_back(data);
match self.core.tx.try_send(chunk) {
Ok(_) => {
socket.recv_slice(&mut self.chunk_buf[..n]).unwrap();
}
Err(mpsc::error::TryRecvError::Empty) => break,
Err(mpsc::error::TryRecvError::Disconnected) => {
debug!(%self.core.handle, "Tunnel channel closed (Server EOF)");
self.server_eof = true;
Err(mpsc::error::TrySendError::Full(_)) => {
break;
}
}
}
}
// Отправляем из очереди в smoltcp
while socket.can_send() {
if let Some(mut chunk) = self.pending_data.pop_front() {
match socket.send_slice(&chunk) {
Ok(n) => {
self.pending_bytes -= n;
if n < chunk.len() {
chunk.advance(n);
self.pending_data.push_front(chunk);
break;
}
}
Err(e) => {
debug!(%self.core.handle, "Smoltcp send error: {:?}", e);
self.pending_data.push_front(chunk);
Err(mpsc::error::TrySendError::Closed(_)) => {
self.server_eof = true;
break;
}
}
@@ -216,6 +160,43 @@ impl TcpConnection {
}
}
if !self.server_eof {
while socket.can_send() {
// Если есть недоотправленный кусок - берем его первым
if let Some(mut chunk) = self.pending_data.pop_front() {
match socket.send_slice(&chunk) {
Ok(n) => {
self.pending_bytes -= n;
if n < chunk.len() {
chunk.advance(n);
self.pending_data.push_front(chunk);
break; // Сокет заполнился
}
}
Err(e) => {
debug!(%self.core.handle, "Smoltcp send error: {:?}", e);
self.pending_data.push_front(chunk);
break;
}
}
} else {
// Очередь пуста, пытаемся прочитать из канала туннеля
match self.core.rx.try_recv() {
Ok(data) => {
self.pending_bytes += data.len();
self.pending_data.push_back(data);
}
Err(mpsc::error::TryRecvError::Empty) => break,
Err(mpsc::error::TryRecvError::Disconnected) => {
debug!(%self.core.handle, "Tunnel channel closed (Server EOF)");
self.server_eof = true;
break;
}
}
}
}
}
if self.server_eof && self.pending_data.is_empty() {
let state = socket.state();
if state == tcp::State::Established || state == tcp::State::CloseWait {
@@ -224,6 +205,7 @@ impl TcpConnection {
}
}
}
pub fn app_pending_out_size(&self) -> usize {
self.pending_bytes
}
@@ -269,7 +251,7 @@ impl TcpConnection {
});
}
}
// ... остальной код (UdpConnection и IcmpResponder без изменений) ...
pub type UdpPacketTarget = (Bytes, std::net::Ipv4Addr, u16);
pub struct UdpConnection {
+2 -2
View File
@@ -4,9 +4,9 @@ use netrunner_core::{
DNS_PORT, GLOBAL_IDLE_TIMEOUT, HTTPS_PORT, MAX_SOCKETS, NETBIOS_PORTS,
TCP_HANDSHAKE_TIMEOUT,
},
rawcast::{RawCastEvent, RawCastFrame},
rawcast::RawCastFrame,
};
use netrunner_logger::{info, trace, warn};
use netrunner_logger::warn;
use smoltcp::{
iface::{SocketHandle, SocketSet},
socket::{Socket, tcp, udp},
+7 -7
View File
@@ -85,10 +85,6 @@ 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();
@@ -97,9 +93,13 @@ impl Engine {
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.
// Ожидаем, пока локальный сокет не освободит место.
// 1. Берем лок, клонируем Sender, СРАЗУ отпускаем лок (конец scope `tx_opt`)
let tx_opt = inbound_map
.get(&frame.socket_id)
.map(|ref_tx| ref_tx.clone());
// 2. Теперь спокойно ждем (Backpressure), не блокируя остальную систему
if let Some(tx) = tx_opt {
let _ = tx.send(frame.payload).await;
}
}