using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using WIDESEAWCS_Core.Helper;
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);
ConsoleHelper.WriteSuccessLine($"¿Í»§¶ËÉÏÏß:{client.Client.RemoteEndPoint.ToString()}");
}
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();
}
}
}