fixed dedadlocks and buffer changes
This commit is contained in:
@@ -2,3 +2,4 @@
|
|||||||
things.txt
|
things.txt
|
||||||
hosts_cache.txt
|
hosts_cache.txt
|
||||||
/gen
|
/gen
|
||||||
|
.env
|
||||||
@@ -1,10 +1,42 @@
|
|||||||
|
-include .env
|
||||||
# Настройки
|
# Настройки
|
||||||
SERVER_IP = 147.45.43.70
|
SERVER_IP := $(strip $(SERVER_IP))
|
||||||
REMOTE_USER = root
|
REMOTE_USER := $(strip $(REMOTE_USER))
|
||||||
REMOTE_PATH = /root/netr-core
|
REMOTE_PATH := $(strip $(REMOTE_PATH))
|
||||||
SERVICE_NAME = netrunner-server
|
SERVICE_NAME := $(strip $(SERVICE_NAME))
|
||||||
|
|
||||||
|
# Безопасное получение путей из ENV или дефолтов
|
||||||
|
ANDROID_ADB_HOST := $(strip $(ANDROID_ADB_HOST))
|
||||||
|
ANDROID_BUILD_SRC := $(strip $(ANDROID_BUILD_SRC))
|
||||||
|
ANDROID_PROJECT_LIBS := $(strip $(ANDROID_PROJECT_LIBS))
|
||||||
|
# Опции для стабильного SSH/Rsync в условиях плохого коннекта
|
||||||
|
# IPQoS=throughput помогает проталкивать пакеты через тайские магистрали
|
||||||
|
SSH_OPTS = -o IPQoS=throughput -o ServerAliveInterval=30
|
||||||
|
RSYNC_OPTS = -avz --inplace --progress -e "ssh $(SSH_OPTS)"
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: debug-client debug-server build-android build-server deploy-server logs ssh setup-server release-all
|
||||||
|
|
||||||
|
# --- Релизный цикл ---
|
||||||
|
# --- Релизный цикл ---
|
||||||
|
release-all: build-server deploy-server build-android
|
||||||
|
@echo "--- [Android] Проверка путей ---"
|
||||||
|
@if [ ! -d "$(ANDROID_BUILD_SRC)" ]; then echo "Ошибка: Директория сборки $(ANDROID_BUILD_SRC) не найдена!"; exit 1; fi
|
||||||
|
|
||||||
|
@echo "--- [Android] Синхронизация библиотек в проект ---"
|
||||||
|
@mkdir -p $(ANDROID_PROJECT_LIBS)
|
||||||
|
# Используем rsync локально: это безопаснее и быстрее, чем cp -r
|
||||||
|
# Удалит в jniLibs всё, чего нет в gen, чтобы билд был чистым
|
||||||
|
rsync -av "$(ANDROID_BUILD_SRC)/" "$(ANDROID_PROJECT_LIBS)/"
|
||||||
|
|
||||||
|
@echo "--- [ADB] Подключение к устройству ---"
|
||||||
|
@if [ -z "$(port)" ]; then \
|
||||||
|
echo "Ошибка: укажите порт, например: make release-all port=5555"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
adb connect $(ANDROID_ADB_HOST):$(port)
|
||||||
|
@echo "--- Релиз полностью завершен ---"
|
||||||
|
|
||||||
.PHONY: debug-client debug-server build-android build-server deploy-server logs
|
|
||||||
|
|
||||||
debug-client:
|
debug-client:
|
||||||
@echo "--- Сборка клиента (Debug) ---"
|
@echo "--- Сборка клиента (Debug) ---"
|
||||||
@@ -20,7 +52,6 @@ debug-server:
|
|||||||
@echo "--- Запуск сервера локально ---"
|
@echo "--- Запуск сервера локально ---"
|
||||||
sudo ./target/debug/netrunner-server --port=4443 --host=0.0.0.0
|
sudo ./target/debug/netrunner-server --port=4443 --host=0.0.0.0
|
||||||
|
|
||||||
|
|
||||||
ABIS = arm64-v8a armeabi-v7a x86_64 x86
|
ABIS = arm64-v8a armeabi-v7a x86_64 x86
|
||||||
|
|
||||||
build-android:
|
build-android:
|
||||||
@@ -28,46 +59,46 @@ build-android:
|
|||||||
echo "Building for $$abi..."; \
|
echo "Building for $$abi..."; \
|
||||||
cargo ndk -t $$abi -o ./gen build --bin netrunner-client --release; \
|
cargo ndk -t $$abi -o ./gen build --bin netrunner-client --release; \
|
||||||
done
|
done
|
||||||
|
|
||||||
cargo run --bin bindgen-tool generate \
|
cargo run --bin bindgen-tool generate \
|
||||||
--library gen/arm64-v8a/libnetrunner_client.so \
|
--library gen/arm64-v8a/libnetrunner_client.so \
|
||||||
--language kotlin \
|
--language kotlin \
|
||||||
--no-format \
|
--no-format \
|
||||||
--out-dir gen; \
|
--out-dir gen
|
||||||
|
|
||||||
build-server:
|
build-server:
|
||||||
|
@echo "--- Сборка сервера (Release) ---"
|
||||||
cargo build --bin netrunner-server --release
|
cargo build --bin netrunner-server --release
|
||||||
|
|
||||||
setup-server:
|
setup-server:
|
||||||
@echo "--- Обновление системы и установка зависимостей ---"
|
@echo "--- Подготовка сервера ---"
|
||||||
ssh $(REMOTE_USER)@$(SERVER_IP) "\
|
ssh $(SSH_OPTS) $(REMOTE_USER)@$(SERVER_IP) "\
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get upgrade -y && \
|
apt-get install -y build-essential libssl-dev pkg-config rsync && \
|
||||||
apt-get install -y build-essential libssl-dev pkg-config && \
|
|
||||||
mkdir -p $(REMOTE_PATH)"
|
mkdir -p $(REMOTE_PATH)"
|
||||||
|
|
||||||
# Деплой
|
# Деплой с использованием rsync и принудительной очисткой
|
||||||
deploy-server:
|
deploy-server: build-server
|
||||||
@echo "--- Останавливаем старый сервер ---"
|
@echo "--- [1/4] Остановка сервиса и очистка зависших процессов ---"
|
||||||
ssh $(REMOTE_USER)@$(SERVER_IP) "systemctl stop $(SERVICE_NAME)" || true
|
ssh $(SSH_OPTS) $(REMOTE_USER)@$(SERVER_IP) "\
|
||||||
|
systemctl stop $(SERVICE_NAME) || true; \
|
||||||
|
pkill -9 $(SERVICE_NAME) || true; \
|
||||||
|
rm -f $(REMOTE_PATH)/$(SERVICE_NAME).tmp"
|
||||||
|
|
||||||
@echo "--- Копируем бинарник ---"
|
@echo "--- [2/4] Копирование бинарника (rsync) ---"
|
||||||
ssh $(REMOTE_USER)@$(SERVER_IP) "mkdir -p $(REMOTE_PATH)"
|
rsync $(RSYNC_OPTS) target/release/netrunner-server $(REMOTE_USER)@$(SERVER_IP):$(REMOTE_PATH)/
|
||||||
scp target/release/netrunner-server $(REMOTE_USER)@$(SERVER_IP):$(REMOTE_PATH)/
|
|
||||||
|
|
||||||
@echo "--- Копируем файл сервиса ---"
|
@echo "--- [3/4] Обновление конфигурации systemd ---"
|
||||||
scp server/netrunner-server.service $(REMOTE_USER)@$(SERVER_IP):/etc/systemd/system/$(SERVICE_NAME).service
|
rsync $(RSYNC_OPTS) server/netrunner-server.service $(REMOTE_USER)@$(SERVER_IP):/etc/systemd/system/
|
||||||
|
|
||||||
@echo "--- Запускаем сервис ---"
|
@echo "--- [4/4] Перезапуск сервиса ---"
|
||||||
ssh $(REMOTE_USER)@$(SERVER_IP) "\
|
ssh $(SSH_OPTS) $(REMOTE_USER)@$(SERVER_IP) "\
|
||||||
systemctl daemon-reload && \
|
systemctl daemon-reload && \
|
||||||
systemctl enable $(SERVICE_NAME) && \
|
systemctl enable $(SERVICE_NAME) && \
|
||||||
systemctl start $(SERVICE_NAME)"
|
systemctl start $(SERVICE_NAME)"
|
||||||
|
@echo "--- Деплой завершен успешно! ---"
|
||||||
|
|
||||||
# Логи
|
|
||||||
logs:
|
logs:
|
||||||
ssh $(REMOTE_USER)@$(SERVER_IP) "journalctl -u $(SERVICE_NAME) -f"
|
ssh $(SSH_OPTS) $(REMOTE_USER)@$(SERVER_IP) "journalctl -u $(SERVICE_NAME) -f"
|
||||||
|
|
||||||
# Вход на сервер
|
|
||||||
ssh:
|
ssh:
|
||||||
ssh $(REMOTE_USER)@$(SERVER_IP)
|
ssh $(SSH_OPTS) $(REMOTE_USER)@$(SERVER_IP)
|
||||||
@@ -129,8 +129,6 @@ impl TcpConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn poll_and_process(&mut self, socket: &mut tcp::Socket) {
|
fn poll_and_process(&mut self, socket: &mut tcp::Socket) {
|
||||||
let max_pending = NetworkConfig::global().tcp_max_pending;
|
|
||||||
|
|
||||||
// 1. Вычитываем данные из smoltcp и шлем в Muxer
|
// 1. Вычитываем данные из smoltcp и шлем в Muxer
|
||||||
while socket.can_recv() {
|
while socket.can_recv() {
|
||||||
let mut full = false;
|
let mut full = false;
|
||||||
@@ -166,19 +164,17 @@ impl TcpConnection {
|
|||||||
// 2. Читаем данные из Muxer'а
|
// 2. Читаем данные из Muxer'а
|
||||||
if !self.server_eof {
|
if !self.server_eof {
|
||||||
loop {
|
loop {
|
||||||
|
if self.pending_data.len() >= socket.send_capacity() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
match self.core.rx.try_recv() {
|
match self.core.rx.try_recv() {
|
||||||
Ok(data) => {
|
Ok(data) => {
|
||||||
self.pending_data.extend_from_slice(&data);
|
self.pending_data.extend_from_slice(&data);
|
||||||
if self.pending_data.len() >= max_pending {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(mpsc::error::TryRecvError::Empty) => {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
Err(mpsc::error::TryRecvError::Empty) => break,
|
||||||
Err(mpsc::error::TryRecvError::Disconnected) => {
|
Err(mpsc::error::TryRecvError::Disconnected) => {
|
||||||
// ВАЖНО: Удаленный сервер прислал EOF!
|
debug!(%self.core.handle, "Server sent EOF");
|
||||||
debug!(%self.core.handle, "Server sent EOF (channel disconnected).");
|
|
||||||
self.server_eof = true;
|
self.server_eof = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ impl SocketFactory {
|
|||||||
443 | 80 | 8080 => max_buf,
|
443 | 80 | 8080 => max_buf,
|
||||||
22 => 32 * 1024,
|
22 => 32 * 1024,
|
||||||
53 => 16 * 1024,
|
53 => 16 * 1024,
|
||||||
_ => max_buf / 4,
|
_ => max_buf,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut socket = tcp::Socket::new(
|
let mut socket = tcp::Socket::new(
|
||||||
@@ -240,9 +240,7 @@ impl ConnectionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Если это происходит во время Connecting - значит сервер шлет ответы,
|
trace!(
|
||||||
// а клиент уже забыл про этот ID или еще не создал его.
|
|
||||||
error!(
|
|
||||||
"👻 [Stream {}] ORPHAN packet from tunnel. ID mismatch?",
|
"👻 [Stream {}] ORPHAN packet from tunnel. ID mismatch?",
|
||||||
frame.socket_id
|
frame.socket_id
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -99,19 +99,17 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Если затора нет, читаем новые кадры из VPN-туннеля
|
|
||||||
if stuck_frame.is_none() {
|
if stuck_frame.is_none() {
|
||||||
while let Ok(frame) = self.rx_from_tunnel.try_recv() {
|
while let Ok(frame) = self.rx_from_tunnel.try_recv() {
|
||||||
if let Err(returned_frame) = self.manager.try_inject_inbound(frame) {
|
if let Err(returned_frame) = self.manager.try_inject_inbound(frame) {
|
||||||
stuck_frame = Some(returned_frame);
|
stuck_frame = Some(returned_frame); // Возвращаем пакет в "заначку"
|
||||||
break; // СТОП! Локальный сокет переполнен, давим на тормоза!
|
break; // ТОРМОЗИМ чтение туннеля, пока канал не освободится
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Двигаем стейт-машину smoltcp
|
|
||||||
let result = self.poll();
|
|
||||||
self.manager.process_sockets(&mut self.socket_set);
|
self.manager.process_sockets(&mut self.socket_set);
|
||||||
|
let result = self.poll();
|
||||||
|
|
||||||
if last_log.elapsed() >= Duration::from_secs(5) {
|
if last_log.elapsed() >= Duration::from_secs(5) {
|
||||||
self.manager.log_status(&self.socket_set);
|
self.manager.log_status(&self.socket_set);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ pub(crate) async fn run_tcp_bridge<R, W>(
|
|||||||
frame_type: FrameType::Data,
|
frame_type: FrameType::Data,
|
||||||
data: buf.split().freeze(),
|
data: buf.split().freeze(),
|
||||||
};
|
};
|
||||||
if muxer.send_to_network(msg).await.is_err() { break; }
|
if muxer.send_to_network(msg).is_err() { break; }
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(stream_id, error = %e, "Socket read error");
|
error!(stream_id, error = %e, "Socket read error");
|
||||||
@@ -61,13 +61,11 @@ pub(crate) async fn run_tcp_bridge<R, W>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = muxer
|
let _ = muxer.send_to_network(MuxMessage {
|
||||||
.send_to_network(MuxMessage {
|
|
||||||
stream_id,
|
stream_id,
|
||||||
frame_type: FrameType::Close,
|
frame_type: FrameType::Close,
|
||||||
data: Bytes::new(),
|
data: Bytes::new(),
|
||||||
})
|
});
|
||||||
.await;
|
|
||||||
|
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
|
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
|
||||||
|
|
||||||
@@ -97,7 +95,7 @@ pub(crate) async fn run_udp_bridge(
|
|||||||
|
|
||||||
// КРИТИЧЕСКИЙ МОМЕНТ: передаем true (is_udp),
|
// КРИТИЧЕСКИЙ МОМЕНТ: передаем true (is_udp),
|
||||||
// чтобы Muxer выбрал правильную ногу для реалтайм трафика
|
// чтобы Muxer выбрал правильную ногу для реалтайм трафика
|
||||||
if let Err(e) = muxer.send_data_safe(stream_id, data, true).await {
|
if let Err(e) = muxer.send_data_safe(stream_id, data, true) {
|
||||||
error!("❌ [UDP {}] Failed to send to tunnel: {}", stream_id, e);
|
error!("❌ [UDP {}] Failed to send to tunnel: {}", stream_id, e);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -282,17 +282,15 @@ impl ClientHandler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Отправляем управляющий фрейм с адресом
|
// Отправляем управляющий фрейм с адресом
|
||||||
let _ = muxer_inner.send_control(stream_id, f_type, payload).await;
|
let _ = muxer_inner.send_control(stream_id, f_type, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameType::Data | FrameType::UdpData => {
|
FrameType::Data | FrameType::UdpData => {
|
||||||
let _ = muxer_inner.send_data_safe(stream_id, payload, is_udp).await;
|
let _ = muxer_inner.send_data_safe(stream_id, payload, is_udp);
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameType::Close => {
|
FrameType::Close => {
|
||||||
let _ = muxer_inner
|
let _ = muxer_inner.send_control(stream_id, FrameType::Close, Bytes::new());
|
||||||
.send_control(stream_id, FrameType::Close, Bytes::new())
|
|
||||||
.await;
|
|
||||||
muxer_inner.remove_stream(stream_id);
|
muxer_inner.remove_stream(stream_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,8 +71,9 @@ impl StreamHandler {
|
|||||||
let elapsed = start.elapsed();
|
let elapsed = start.elapsed();
|
||||||
info!(stream_id, target = %target_str, "✅ [TCP] Established in {:?}. Starting bridge.", elapsed);
|
info!(stream_id, target = %target_str, "✅ [TCP] Established in {:?}. Starting bridge.", elapsed);
|
||||||
let (r, w) = stream.into_split();
|
let (r, w) = stream.into_split();
|
||||||
run_tcp_bridge(stream_id, r, w, muxer, v_rx).await;
|
run_tcp_bridge(stream_id, r, w, muxer.clone(), v_rx).await;
|
||||||
info!(stream_id, "🔚 [TCP Worker] Bridge task finished");
|
info!(stream_id, "🔚 [TCP Worker] Bridge task finished");
|
||||||
|
Self::close_stream(&muxer, stream_id).await;
|
||||||
}
|
}
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
error!(stream_id, target = %target_str, error = %e, "❌ [TCP] Connection failed");
|
error!(stream_id, target = %target_str, error = %e, "❌ [TCP] Connection failed");
|
||||||
@@ -86,7 +87,7 @@ impl StreamHandler {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
info!(stream_id, "📲 [TCP] Dispatching payload to local stack");
|
info!(stream_id, "📲 [TCP] Dispatching payload to local stack");
|
||||||
self.muxer.dispatch_to_local(stream_id, payload).await;
|
self.muxer.dispatch_to_local(stream_id, payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,19 +144,19 @@ impl StreamHandler {
|
|||||||
stream_id,
|
stream_id,
|
||||||
"📲 [UDP] Dispatching connection payload to local stack"
|
"📲 [UDP] Dispatching connection payload to local stack"
|
||||||
);
|
);
|
||||||
self.muxer.dispatch_to_local(stream_id, payload).await;
|
self.muxer.dispatch_to_local(stream_id, payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_data(&self, stream_id: u32, payload: Bytes) {
|
async fn on_data(&self, stream_id: u32, payload: Bytes) {
|
||||||
// Здесь info может быть избыточным при большой нагрузке, но для отладки полезно
|
// Здесь info может быть избыточным при большой нагрузке, но для отладки полезно
|
||||||
debug!(stream_id, "📦 [TCP Data] Size: {} bytes", payload.len());
|
debug!(stream_id, "📦 [TCP Data] Size: {} bytes", payload.len());
|
||||||
self.muxer.dispatch_to_local(stream_id, payload).await;
|
self.muxer.dispatch_to_local(stream_id, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_udp_data(&self, stream_id: u32, payload: Bytes) {
|
async fn on_udp_data(&self, stream_id: u32, payload: Bytes) {
|
||||||
debug!(stream_id, "📦 [UDP Data] Size: {} bytes", payload.len());
|
debug!(stream_id, "📦 [UDP Data] Size: {} bytes", payload.len());
|
||||||
self.muxer.dispatch_to_local(stream_id, payload).await;
|
self.muxer.dispatch_to_local(stream_id, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_close(&self, stream_id: u32) {
|
async fn on_close(&self, stream_id: u32) {
|
||||||
@@ -168,9 +169,7 @@ impl StreamHandler {
|
|||||||
stream_id,
|
stream_id,
|
||||||
"📡 [Control] Sending CLOSE signal to remote peer"
|
"📡 [Control] Sending CLOSE signal to remote peer"
|
||||||
);
|
);
|
||||||
let _ = muxer
|
let _ = muxer.send_control(stream_id, FrameType::Close, Bytes::new());
|
||||||
.send_control(stream_id, FrameType::Close, Bytes::new())
|
|
||||||
.await;
|
|
||||||
muxer.remove_stream(stream_id);
|
muxer.remove_stream(stream_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use tokio::sync::mpsc::Sender;
|
|||||||
use crate::net::network::NetworkConfig;
|
use crate::net::network::NetworkConfig;
|
||||||
use crate::nrxp::FrameType;
|
use crate::nrxp::FrameType;
|
||||||
|
|
||||||
// Вспомогательная структура для отдельного TLS-соединения
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct MuxLeg {
|
struct MuxLeg {
|
||||||
control_tx: Sender<MuxMessage>,
|
control_tx: Sender<MuxMessage>,
|
||||||
@@ -52,11 +51,9 @@ impl Muxer {
|
|||||||
streams: Arc::new(DashMap::new()),
|
streams: Arc::new(DashMap::new()),
|
||||||
id_gen: Arc::new(IdGenerator::new(is_client)),
|
id_gen: Arc::new(IdGenerator::new(is_client)),
|
||||||
leg_selector: Arc::new(AtomicU32::new(0)),
|
leg_selector: Arc::new(AtomicU32::new(0)),
|
||||||
// leg_id_gen удален!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ТЕПЕРЬ leg_id ПЕРЕДАЕТСЯ СНАРУЖИ
|
|
||||||
pub fn add_leg(
|
pub fn add_leg(
|
||||||
&self,
|
&self,
|
||||||
leg_id: u32,
|
leg_id: u32,
|
||||||
@@ -82,7 +79,6 @@ impl Muxer {
|
|||||||
self.legs.len()
|
self.legs.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Возвращаем кортеж (leg_id, MuxLeg), чтобы знать, кого удалять в случае ошибки
|
|
||||||
fn select_leg(&self, frame_type: &FrameType) -> Option<(u32, MuxLeg)> {
|
fn select_leg(&self, frame_type: &FrameType) -> Option<(u32, MuxLeg)> {
|
||||||
if self.legs.is_empty() {
|
if self.legs.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
@@ -90,7 +86,6 @@ impl Muxer {
|
|||||||
|
|
||||||
match frame_type {
|
match frame_type {
|
||||||
FrameType::UdpData | FrameType::UdpConnect => {
|
FrameType::UdpData | FrameType::UdpConnect => {
|
||||||
// Пытаемся взять UDP ногу (1), если её нет — берем TCP (0) как фоллбэк
|
|
||||||
if let Some(leg) = self.legs.get(&1) {
|
if let Some(leg) = self.legs.get(&1) {
|
||||||
Some((1, leg.clone()))
|
Some((1, leg.clone()))
|
||||||
} else if let Some(leg) = self.legs.get(&0) {
|
} else if let Some(leg) = self.legs.get(&0) {
|
||||||
@@ -100,7 +95,6 @@ impl Muxer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Пытаемся взять TCP ногу (0), фоллбэк на UDP (1)
|
|
||||||
if let Some(leg) = self.legs.get(&0) {
|
if let Some(leg) = self.legs.get(&0) {
|
||||||
Some((0, leg.clone()))
|
Some((0, leg.clone()))
|
||||||
} else if let Some(leg) = self.legs.get(&1) {
|
} else if let Some(leg) = self.legs.get(&1) {
|
||||||
@@ -112,7 +106,8 @@ impl Muxer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_to_network(&self, message: MuxMessage) -> Result<(), String> {
|
// ТЕПЕРЬ СИНХРОННАЯ ФУНКЦИЯ БЕЗ БЛОКИРОВОК
|
||||||
|
pub fn send_to_network(&self, message: MuxMessage) -> Result<(), String> {
|
||||||
let (leg_id, leg) = self
|
let (leg_id, leg) = self
|
||||||
.select_leg(&message.frame_type)
|
.select_leg(&message.frame_type)
|
||||||
.ok_or_else(|| "MUXER: No active legs available".to_string())?;
|
.ok_or_else(|| "MUXER: No active legs available".to_string())?;
|
||||||
@@ -125,16 +120,27 @@ impl Muxer {
|
|||||||
_ => &leg.data_tx,
|
_ => &leg.data_tx,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Если канал мертв (TunnelEngine упал), удаляем ногу из роутинга!
|
// Используем try_send вместо send().await
|
||||||
if let Err(e) = target_tx.send(message).await {
|
match target_tx.try_send(message) {
|
||||||
self.remove_leg(leg_id);
|
Ok(_) => Ok(()),
|
||||||
return Err(format!("MUXER: Leg {} died during send: {}", leg_id, e));
|
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
|
||||||
}
|
// Если очередь переполнена - просто дропаем пакет!
|
||||||
|
// Возвращаем Ok(()), чтобы не убивать соединение.
|
||||||
|
netrunner_logger::warn!(
|
||||||
|
leg_id,
|
||||||
|
"MUXER: Network queue full! Dropping outbound packet."
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
|
||||||
|
self.remove_leg(leg_id);
|
||||||
|
Err(format!("MUXER: Leg {} died during send", leg_id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn send_data_safe(
|
// ТЕПЕРЬ СИНХРОННАЯ ФУНКЦИЯ БЕЗ БЛОКИРОВОК
|
||||||
|
pub fn send_data_safe(
|
||||||
&self,
|
&self,
|
||||||
stream_id: u32,
|
stream_id: u32,
|
||||||
mut data: Bytes,
|
mut data: Bytes,
|
||||||
@@ -153,19 +159,16 @@ impl Muxer {
|
|||||||
|
|
||||||
self.send_to_network(MuxMessage {
|
self.send_to_network(MuxMessage {
|
||||||
stream_id,
|
stream_id,
|
||||||
frame_type,
|
frame_type: frame_type.clone(),
|
||||||
data: chunk,
|
data: chunk,
|
||||||
})
|
})?;
|
||||||
.await?;
|
// task::yield_now().await удален, так как отправка теперь мгновенная
|
||||||
|
|
||||||
if !data.is_empty() {
|
|
||||||
tokio::task::yield_now().await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_control(
|
// ТЕПЕРЬ СИНХРОННАЯ ФУНКЦИЯ БЕЗ БЛОКИРОВОК
|
||||||
|
pub fn send_control(
|
||||||
&self,
|
&self,
|
||||||
stream_id: u32,
|
stream_id: u32,
|
||||||
f_type: FrameType,
|
f_type: FrameType,
|
||||||
@@ -176,7 +179,6 @@ impl Muxer {
|
|||||||
frame_type: f_type,
|
frame_type: f_type,
|
||||||
data,
|
data,
|
||||||
})
|
})
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_stream(&self, stream_id: u32, tx: Sender<Bytes>) {
|
pub fn register_stream(&self, stream_id: u32, tx: Sender<Bytes>) {
|
||||||
@@ -187,11 +189,28 @@ impl Muxer {
|
|||||||
self.streams.remove(&stream_id);
|
self.streams.remove(&stream_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
|
// ТЕПЕРЬ СИНХРОННАЯ ФУНКЦИЯ БЕЗ БЛОКИРОВОК
|
||||||
if let Some(tx) = self.streams.get(&stream_id) {
|
pub fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
|
||||||
if let Err(_) = tx.send(data).await {
|
// Узнаем, мертв ли канал, не блокируя DashMap
|
||||||
|
let is_closed = if let Some(tx) = self.streams.get(&stream_id) {
|
||||||
|
match tx.try_send(data) {
|
||||||
|
Ok(_) => false,
|
||||||
|
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
|
||||||
|
netrunner_logger::warn!(
|
||||||
|
stream_id,
|
||||||
|
"MUXER: Local worker queue full! Dropping inbound packet."
|
||||||
|
);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => true,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Удаляем стрим только после того, как отпустили ссылку на DashMap
|
||||||
|
if is_closed {
|
||||||
self.remove_stream(stream_id);
|
self.remove_stream(stream_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
+11
-11
@@ -80,7 +80,6 @@ pub struct NetworkConfig {
|
|||||||
pub udp_stream_capacity: usize, // Локальные каналы 1 сокета (Engine <-> Muxer)
|
pub udp_stream_capacity: usize, // Локальные каналы 1 сокета (Engine <-> Muxer)
|
||||||
|
|
||||||
pub smoltcp_socket_buf: usize,
|
pub smoltcp_socket_buf: usize,
|
||||||
pub tcp_max_pending: usize,
|
|
||||||
pub tcp_chunk_size: usize,
|
pub tcp_chunk_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,19 +94,20 @@ impl NetworkConfig {
|
|||||||
max_wire_frame_size: max_wire_frame,
|
max_wire_frame_size: max_wire_frame,
|
||||||
safe_payload_size: safe_payload,
|
safe_payload_size: safe_payload,
|
||||||
|
|
||||||
// Увеличиваем пропускную способность для видео
|
// Буферы ОС/Tokio (держим с запасом под быстрые всплески)
|
||||||
tcp_buffer_size: 512 * 1024,
|
tcp_buffer_size: 1024 * 1024,
|
||||||
udp_buffer_size: 256 * 1024,
|
udp_buffer_size: 512 * 1024,
|
||||||
|
|
||||||
// Уменьшаем очереди, чтобы не копить задержку
|
// Глобальная очередь. 4096 * 16KB = ~65 MB в памяти (безопасно)
|
||||||
muxer_capacity: 64,
|
// Это спасет от заторов, если 10 сокетов решат отправить данные одновременно
|
||||||
tcp_stream_capacity: 4,
|
muxer_capacity: 2048,
|
||||||
udp_stream_capacity: 16, // Для видео (QUIC/UDP) лучше чуть больше
|
|
||||||
|
tcp_stream_capacity: 128,
|
||||||
|
udp_stream_capacity: 64,
|
||||||
|
|
||||||
// Настройки smoltcp
|
// Настройки smoltcp
|
||||||
smoltcp_socket_buf: 256 * 1024, // Даем Ютубу раздышаться
|
smoltcp_socket_buf: 512 * 1024,
|
||||||
tcp_max_pending: 32, // Но не даем ему "тупить" в очереди
|
tcp_chunk_size: 16 * 1024,
|
||||||
tcp_chunk_size: 16 * 1024, // Чанки побольше — меньше системных вызовов
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user