using System; using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; namespace SoundBeam { public class TcpServer : IDisposable { public IPAddress ListenIpAddress { get; } public int ListenPort { get; } private TcpListener _listener; private Task _processTcpClientsTask; private CancellationTokenSource _serverCancellationTokenSource; public TcpServer(string address, int port) { if (!IPAddress.TryParse(address, out var listenIpAddress)) { throw new ArgumentException(); } ListenIpAddress = listenIpAddress; ListenPort = port; } public void Dispose() { Stop(); _serverCancellationTokenSource?.Dispose(); _serverCancellationTokenSource = null; } public event EventHandler ClientConnected; public async Task ProcessTcpClients(CancellationToken token) { _listener.Start(); do { try { var client = await Task.Run(() => _listener.AcceptTcpClientAsync(), token); if (client == null) { continue; } ClientConnected?.Invoke(this, new TcpClientConnectedEventArgs(client)); } catch (OperationCanceledException) { // Going down and we don't care. return; } catch (Exception ex) { // } } while (!token.IsCancellationRequested); } public void Start() { _listener = new TcpListener(ListenIpAddress, ListenPort); _serverCancellationTokenSource = new CancellationTokenSource(); #pragma warning disable 4014 // Processing TCP clients will run asynchronously. _processTcpClientsTask = ProcessTcpClients(_serverCancellationTokenSource.Token); #pragma warning restore 4014 } public void Stop() { _serverCancellationTokenSource?.Cancel(); _processTcpClientsTask?.Wait(); _listener?.Stop(); } } }