using Apewer.Internals; using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Threading; namespace Apewer.Network { /// TCP 服务端。 public sealed class TcpServer : IDisposable { Socket _server = null; List _clients = new List(1024); /// 获取当前监听的端口。 public int Port { get => GetLocalPort(); } /// 启动服务端,并在所有接口上监听端口。 public TcpServer(int port) : this(IPAddress.Any, port) { } /// 启动服务端,并监听端口。 public TcpServer(string ip, int port) : this(IPAddress.Parse(ip), port) { } /// 启动服务端,并监听端口。 public TcpServer(IPAddress address, int port) : this(new IPEndPoint(address, port)) { } /// 启动服务端,并监听端口。 public TcpServer(IPEndPoint endpoint) { _server = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); _server.Bind(endpoint); _server.Listen(500); _server.SendTimeout = 5000; } /// 关闭连接,释放系统资源。 public void Dispose() { lock (_clients) { foreach (var client in _clients) { try { client.Disconnect(false); } catch { } try { client.Close(100); } catch { } #if !NET20 try { client.Dispose(); } catch { } #endif } _clients.Clear(); } if (_server != null) { try { _server.Disconnect(false); } catch { } try { _server.Close(100); } catch { } #if !NET20 try { _server.Dispose(); } catch { } #endif } } /// 获取当前所有客户端连接。 public Socket[] GetClients() { lock (_clients) { var array = _clients.ToArray(); return array; } } /// 等待接收客户端连接。此方法将会阻塞当前线程直到关闭监听的端口。 /// 处理客户端连接,当此回调结束时将释放此连接。 /// public void Accept(Action callback) { if (callback == null) throw new ArgumentNullException(nameof(callback)); while (true) { var socket = null as Socket; try { socket = _server.Accept(); if (socket != null) { Callback.Enqueue(this, socket, callback); } } catch { if (socket != null) { try { socket.Close(); } catch { } try { socket.Disconnect(false); } catch { } #if !NET20 try { socket.Dispose(); } catch { } #endif } } } } int GetLocalPort() { var socket = _server; if (socket != null) { var endpoint = socket.LocalEndPoint as IPEndPoint; if (endpoint != null) return endpoint.Port; } return 0; } sealed class Callback { TcpServer _server; Socket _socket; Action _action; public static void Enqueue(TcpServer server, Socket socket, Action callback) { var state = new Callback(); state._server = server ?? throw new ArgumentNullException(nameof(server)); state._socket = socket ?? throw new ArgumentNullException(nameof(socket)); state._action = callback ?? throw new ArgumentNullException(nameof(callback)); ThreadPool.QueueUserWorkItem(InThreadPool, state); } static void InThreadPool(object state) { var instance = state as Callback; if (instance == null) return; if (instance._server == null) return; if (instance._socket == null) return; if (instance._action == null) return; var socket = instance._socket; lock (instance._server._clients) { instance._server._clients.Add(instance._socket); } try { instance._action.Invoke(instance._socket); } catch { } finally { lock (instance._server._clients) { instance._server._clients.Remove(instance._socket); } try { socket.Disconnect(false); } catch { } try { socket.Close(100); } catch { } #if !NET20 try { socket.Dispose(); } catch { } #endif } } } } }