using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; namespace WIDESEAWCS_Tasks.SocketServer { public partial class TcpSocketServer { /// /// Òì²½Æô¶¯TCP·þÎñÆ÷£¬Ê¹Æä¿ªÊ¼½ÓÊÜ´«ÈëµÄ¿Í»§¶ËÁ¬½Ó¡£ /// /// Èç¹û·þÎñÆ÷ÒÑÔÚÔËÐлòͨ¹ýÅäÖýûÓ㬴˷½·¨½«Á¢¼´·µ»Ø¶ø²»Æô¶¯·þÎñÆ÷¡£ /// ºóÐøµÄ¿Í»§¶Ë¼à¿ØºÍ½ÓÊܲÙ×÷ÔÚºǫ́ÈÎÎñÖÐÔËÐС£´Ë·½·¨²»»á×èÈûµ÷ÓÃÏ̡߳£ /// ¿ÉÓÃÓÚÇëÇóÈ¡Ïû·þÎñÆ÷Æô¶¯¼°ºóÐøºǫ́²Ù×÷µÄÈ¡ÏûÁîÅÆ¡£ /// ±íʾÒì²½Æô¶¯²Ù×÷µÄÈÎÎñ¡£µ±·þÎñÆ÷¿ªÊ¼¼àÌýÁ¬½ÓʱÈÎÎñÍê³É¡£ public Task StartAsync(CancellationToken cancellationToken) { if (IsRunning || !_options.Enabled) { return Task.CompletedTask; } IPAddress ipAddress = IPAddress.Any; if (IPAddress.TryParse(_options.IpAddress, out IPAddress? parsedAddress)) { ipAddress = parsedAddress; } _listener = new TcpListener(ipAddress, _options.Port); _listener.Start(_options.Backlog); _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); IsRunning = true; _ = AcceptLoopAsync(_cts.Token); _monitorTask = Task.Run(() => MonitorClientsAsync(_cts.Token)); return Task.CompletedTask; } //// /// Ò첽ֹͣ·þÎñÆ÷²¢µÈ´ýËùÓл¿Í»§¶ËÁ¬½ÓÍê³É¡£ /// /// Èç¹û·þÎñÆ÷δÔËÐУ¬´Ë·½·¨½«Á¢¼´·µ»Ø¶ø²»Ö´ÐÐÈκβÙ×÷¡£ /// ´Ë·½·¨È·±£ËùÓпͻ§¶ËÈÎÎñÍê³Éºó²Å½«·þÎñÆ÷±ê¼ÇΪÒÑÍ£Ö¹¡£ /// ¿ÉÓÃÓÚÔÚÍê³ÉǰȡÏûÍ£Ö¹²Ù×÷µÄÈ¡ÏûÁîÅÆ¡£ /// ±íʾÒ첽ֹͣ²Ù×÷µÄÈÎÎñ¡£ public async Task StopAsync(CancellationToken cancellationToken) { if (!IsRunning) { return; } _cts?.Cancel(); _listener?.Stop(); Task[] tasks; lock (_syncRoot) { tasks = _clientTasks.ToArray(); } if (tasks.Length > 0) { await Task.WhenAll(tasks); } IsRunning = false; } /// /// ³ÖÐø½ÓÊÜ´«ÈëµÄTCP¿Í»§¶ËÁ¬½Ó£¬Ö±µ½ÇëÇóÈ¡Ïû¡£ /// /// ´Ë·½·¨Ö¼ÔÚºǫ́ÔËÐÐÒÔ´¦ÀíеĿͻ§¶ËÁ¬½Ó¡£ /// Èç¹û¼àÌýÆ÷±»ÊÍ·Å»òͨ¹ýÌṩµÄÁîÅÆÇëÇóÈ¡Ïû£¬Ñ­»·½«Í˳ö¡£ /// ¿ÉÓÃÓÚÇëÇóÈ¡Ïû½ÓÊÜÑ­»·µÄÁîÅÆ¡£µ±ÇëÇóÈ¡Ïûʱ£¬Ñ­»·½«Á¢¼´ÖÕÖ¹¡£ /// ±íʾÒì²½½ÓÊÜÑ­»·²Ù×÷µÄÈÎÎñ¡£ private async Task AcceptLoopAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { TcpClient? client = null; try { client = await _listener!.AcceptTcpClientAsync().WaitAsync(cancellationToken); } catch (OperationCanceledException) { break; } catch (ObjectDisposedException) { break; } catch { if (cancellationToken.IsCancellationRequested) { break; } } if (client == null) { continue; } string clientId = GetClientId(client); lock (_syncRoot) { _clients[clientId] = client; _clientLocks[clientId] = new SemaphoreSlim(1, 1); } } } /// /// ´ÓÄÚ²¿¼¯ºÏÖÐÒÆ³ýÖ¸¶¨±êʶ·ûµÄ¿Í»§¶Ë£¬²¢ÊÍ·ÅÏà¹Ø×ÊÔ´¡£ /// /// ´Ë·½·¨¹Ø±Õ¿Í»§¶ËÁ¬½Ó£¬ÊÍ·ÅÈκιØÁªµÄËø£¬²¢ÒƳý¶Ô¿Í»§¶ËµÄËùÓÐÒýÓ㬠/// °üÀ¨É豸°ó¶¨ºÍ±àÂëÐÅÏ¢¡£Í¨¹ý¶ÔÄÚ²¿Í¬²½¶ÔÏó¼ÓËøÈ·±£Ḭ̈߳²È«¡£ /// ÒªÒÆ³ýµÄ¿Í»§¶ËµÄΨһ±êʶ·û¡£²»ÄÜΪnull»ò¿Õ¡£ private void RemoveClient(string clientId) { lock (_syncRoot) { if (_clients.TryGetValue(clientId, out var client)) { try { client.Close(); } catch { } _clients.Remove(clientId); } if (_clientLocks.TryGetValue(clientId, out var sem)) { _clientLocks.Remove(clientId); sem.Dispose(); } _clientLastActive.Remove(clientId); _clientEncodings.Remove(clientId); var deviceIds = _deviceBindings.Where(kv => kv.Value == clientId).Select(kv => kv.Key).ToList(); foreach (var deviceId in deviceIds) { _deviceBindings.Remove(deviceId); } } } /// /// Òì²½¼à¿ØÒÑÁ¬½ÓµÄ¿Í»§¶Ë£¬²¢¶Ï¿ª³¬¹ýÅäÖó¬Ê±Ê±¼äÏÐÖõĿͻ§¶ËÁ¬½Ó¡£ /// /// ´Ë·½·¨³ÖÐø¼ì²éÏÐÖÿͻ§¶Ë£¬Èç¹ûÆä²»»î¶¯Ê±¼ä³¬¹ýÖ¸¶¨µÄ¿ÕÏг¬Ê±£¬Ôò¶Ï¿ªÁ¬½Ó¡£ /// ¼à¿ØÑ­»·½«³ÖÐøÔËÐУ¬Ö±µ½Í¨¹ýÌṩµÄÁîÅÆÇëÇóÈ¡Ïû¡£ /// ¿ÉÓÃÓÚÇëÇóÖÕÖ¹¼à¿ØÑ­»·µÄÈ¡ÏûÁîÅÆ¡£ /// ±íʾÒì²½¼à¿Ø²Ù×÷µÄÈÎÎñ¡£ private async Task MonitorClientsAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { List toRemove = new(); lock (_syncRoot) { foreach (var kv in _clientLastActive) { if (_options.IdleTimeoutSeconds > 0 && DateTime.Now - kv.Value > TimeSpan.FromSeconds(_options.IdleTimeoutSeconds)) { toRemove.Add(kv.Key); } } } foreach (var cid in toRemove) { RemoveClient(cid); Log($"[{DateTime.Now}] TcpSocketServer disconnect idle client {cid}"); } } catch { } try { await Task.Delay(1000, cancellationToken); } catch { } } } /// /// »ùÓÚÔ¶³ÌÖն˵ã»ñȡָ¶¨TCP¿Í»§¶ËµÄΨһ±êʶ·û×Ö·û´®¡£ /// /// ·µ»ØµÄ±êʶ·ûÊÊÓÃÓÚÔÚÈÕÖ¾¼Ç¼»ò¸ú×Ù³¡¾°ÖÐÇø·Ö¿Í»§¶Ë¡£ /// Èç¹û¿Í»§¶ËµÄÔ¶³ÌÖն˵㲻¿ÉÓ㬽«Éú³ÉGUIDÒÔÈ·±£Î¨Ò»ÐÔ¡£ /// Òª»ñÈ¡±êʶ·ûµÄTCP¿Í»§¶Ë¡£²»ÄÜΪnull¡£ /// ±íʾ¿Í»§¶ËÔ¶³ÌÖն˵ãµÄ×Ö·û´®£¨Èç¹û¿ÉÓã©£»·ñÔòΪÉú³ÉµÄÐÂGUID×Ö·û´®¡£ public static string GetClientId(TcpClient client) { return client.Client.RemoteEndPoint?.ToString() ?? Guid.NewGuid().ToString(); } } }