From 7763cb3d53257b8d6d86cf02e85ecc495a2e5e9e Mon Sep 17 00:00:00 2001 From: wanghongenpin Date: Tue, 12 Aug 2025 14:59:11 +0800 Subject: [PATCH] socket close cleanup (#510)(#497) --- lib/network/channel/network.dart | 42 +++++++++++++++++++++++++++++++- lib/ui/launch/launch.dart | 8 ++++-- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/lib/network/channel/network.dart b/lib/network/channel/network.dart index f85bd75..2531324 100644 --- a/lib/network/channel/network.dart +++ b/lib/network/channel/network.dart @@ -70,29 +70,69 @@ class Server extends Network { late ServerSocket serverSocket; bool isRunning = false; EventListener? listener; + StreamSubscription? serverSubscription; + final List _connections = []; + Timer? _connectionCleanupTimer; Server(this.configuration, {this.listener}); Future bind(int port) async { serverSocket = await ServerSocket.bind(InternetAddress.anyIPv4, port); - serverSocket.listen((socket) { + serverSubscription = serverSocket.listen((socket) { var channel = Channel(socket); + _connections.add(channel); + + socket.done.whenComplete(() => _connections.remove(channel)); + ChannelContext channelContext = ChannelContext(); channelContext.clientChannel = channel; channelContext.listener = listener; listen(channel, channelContext); }); isRunning = true; + _connectionCleanupTimer = Timer.periodic(const Duration(seconds: 120), (timer) { + if (!isRunning) { + timer.cancel(); + _connectionCleanupTimer = null; + return; + } + cleanupConnections(); + }); return serverSocket; } Future stop() async { if (!isRunning) return serverSocket; isRunning = false; + for (var channel in _connections) { + if (channel.isClosed) continue; + try { + logger.d('Closing socket: ${channel.remoteSocketAddress.host}:${channel.remoteSocketAddress.port}'); + channel.close(); + } catch (e) { + logger.e('Error closing socket: $e'); + } + } + _connections.clear(); + //关闭监听 + serverSubscription?.cancel(); + serverSubscription = null; await serverSocket.close(); + _connectionCleanupTimer?.cancel(); + _connectionCleanupTimer = null; return serverSocket; } + void cleanupConnections() { + _connections.removeWhere((channel) { + if (channel.isClosed) { + logger.i('Cleaning up closed channel: ${channel.remoteSocketAddress.host}:${channel.remoteSocketAddress.port}'); + return true; + } + return false; + }); + } + @override Future onEvent(Uint8List data, ChannelContext channelContext, Channel channel) async { //手机扫码转发远程地址 diff --git a/lib/ui/launch/launch.dart b/lib/ui/launch/launch.dart index 7e8c5e9..807c70c 100644 --- a/lib/ui/launch/launch.dart +++ b/lib/ui/launch/launch.dart @@ -17,8 +17,9 @@ import 'dart:io'; import 'dart:ui'; import 'package:flutter/material.dart'; -import 'package:proxypin/l10n/app_localizations.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_toastr/flutter_toastr.dart'; +import 'package:proxypin/l10n/app_localizations.dart'; import 'package:proxypin/native/vpn.dart'; import 'package:proxypin/network/bin/server.dart'; import 'package:proxypin/network/util/logger.dart'; @@ -93,9 +94,13 @@ class _SocketLaunchState extends State with WindowListener, Widget } Future appExit() async { + logger.d("appExit"); await widget.proxyServer.stop(); started = false; + windowManager.setPreventClose(false); await windowManager.destroy(); + + await SystemNavigator.pop(); exit(0); } @@ -108,7 +113,6 @@ class _SocketLaunchState extends State with WindowListener, Widget @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { - if (widget.proxyServer.isRunning) { widget.proxyServer.retryBind(); }