1 /// <summary> 2 /// 从 TCP 网络客户端侦听连接。 3 /// </summary> 4 public class TcpServer 5 { 6 private bool isClosing = false; // 服务器 closing 标志位 7 private TcpListener server; // 服务器的 TcpListener 8 private Action<TcpConnection> connectinAction; // 每个客户端连接的委托 9 10 /// <summary> 11 /// 服务器运行状态 12 /// </summary> 13 public bool IsRunning { get; private set; } 14 15 /// <summary> 16 /// 使用指定的本地终结点初始化 TcpServer 类的新实例。 17 /// </summary> 18 /// <param name="localEP">将 TcpServer 绑定到的本地终结点。</param> 19 public TcpServer(IPEndPoint localEP) 20 { 21 IsRunning = false; 22 server = new TcpListener(localEP); 23 } 24 25 /// <summary> 26 /// 初始化 TcpServer 类的新实例,该类在指定的本地 IP 地址和端口号上侦听是否有传入的连接尝试。 27 /// </summary> 28 /// <param name="localaddr">本地 IP 地址</param> 29 /// <param name="port">用来侦听传入的连接尝试的端口。</param> 30 public TcpServer(IPAddress localaddr, int port) : this(new IPEndPoint(localaddr, port)) 31 { 32 } 33 34 /// <summary> 35 /// 初始化 TcpServer 类的新实例,该类在指定的本地 IP 地址和端口号上侦听是否有传入的连接尝试。 36 /// </summary> 37 /// <param name="address">本地 IP 地址字符串</param> 38 /// <param name="port">用来侦听传入的连接尝试的端口。</param> 39 public TcpServer(string address, int port) : this(IPAddress.Parse(address), port) 40 { 41 } 42 43 /// <summary> 44 /// 开始侦听传入的连接请求。 45 /// </summary> 46 /// <param name="action">每个客户端连接对应的委托</param> 47 public void Start(Action<TcpConnection> action) 48 { 49 if (!IsRunning) 50 { 51 IsRunning = true; 52 isClosing = false; 53 connectinAction = action; 54 server.Start(); 55 StartAccept(); 56 } 57 } 58 59 /// <summary> 60 /// 获取当前实例的基础 EndPoint。 61 /// </summary> 62 public EndPoint LocalEndPoint 63 { 64 get { return server.LocalEndpoint; } 65 } 66 67 /// <summary> 68 /// 获取或设置一个 bool 值,该值指定该实例是否只允许一个基础套接字来侦听特定端口。 69 /// </summary> 70 public bool ExclusiveAddressUse 71 { 72 get { return server.ExclusiveAddressUse; } 73 set { server.ExclusiveAddressUse = value; } 74 } 75 76 /// <summary> 77 /// 获取基础网络 Socket 78 /// </summary> 79 public Socket Server 80 { 81 get { return server.Server; } 82 } 83 84 /// <summary> 85 /// 关闭侦听器。 86 /// </summary> 87 public void Stop() 88 { 89 isClosing = true; 90 server.Stop(); 91 IsRunning = false; 92 } 93 94 /// <summary> 95 /// 开始一个异步操作来接受一个传入的连接尝试。 96 /// </summary> 97 private void StartAccept() 98 { 99 server.BeginAcceptTcpClient(iar => 100 { 101 try 102 { 103 if (isClosing) return; 104 TcpClient tcpClient = server.EndAcceptTcpClient(iar); 105 TcpConnection client = new TcpConnection(tcpClient, connectinAction); 106 } 107 catch (Exception ex) 108 { 109 Trace.TraceError(ex.Message); 110 } 111 StartAccept(); 112 }, null); 113 114 } 115 116 /// <summary> 117 /// 返回指定终结点的 IP 地址和端口号。 118 /// </summary> 119 /// <returns>包含指定终结点(例如,192.168.1.2:80)的 IP 地址和端口号的字符串。</returns> 120 public override string ToString() 121 { 122 return LocalEndPoint.ToString(); 123 } 124 125 /// <summary> 126 /// 获取本地IP地址 127 /// </summary> 128 /// <param name="addressFamily">地址族,默认IPv4</param> 129 /// <returns>本地IP地址数组</returns> 130 public static IPAddress[] GetLocalAddress(AddressFamily addressFamily = AddressFamily.InterNetwork) 131 { 132 List<IPAddress> list = new List<IPAddress>(); 133 list.Add(IPAddress.Any); 134 list.Add(IPAddress.Loopback); 135 foreach (var item in Dns.GetHostAddresses(Dns.GetHostName())) 136 { 137 if (item.AddressFamily == addressFamily) 138 list.Add(item); 139 } 140 return list.ToArray(); 141 } 142 143 /// <summary> 144 /// 获取正在使用中的端口 145 /// </summary> 146 /// <param name="address">指定IP地址,默认全部地址</param> 147 /// <returns>正在使用中的端口数组</returns> 148 public static int[] GetInUsedPort(string address = null) 149 { 150 List<IPEndPoint> localEP = new List<IPEndPoint>(); 151 List<int> localPort = new List<int>(); 152 IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); 153 localEP.AddRange(ipGlobalProperties.GetActiveTcpListeners()); 154 localEP.AddRange(ipGlobalProperties.GetActiveUdpListeners()); 155 localEP.AddRange(ipGlobalProperties.GetActiveTcpConnections().Select(item => item.LocalEndPoint)); 156 foreach (var item in localEP.Distinct()) 157 { 158 if (address == null || item.Address.ToString() == address) 159 localPort.Add(item.Port); 160 } 161 localPort.Sort(); 162 return localPort.Distinct().ToArray(); 163 } 164 165 /// <summary> 166 /// 随机获取一个大于等于 min 的空闲端口 167 /// </summary> 168 /// <param name="min">指定最小空闲端口,默认1024</param> 169 /// <param name="address">指定IP地址,默认全部地址</param> 170 /// <returns>一个指定IP地址与范围的随机空闲端口</returns> 171 public static int GetFreePort(int min = 1024, string address = null) 172 { 173 int freePort = -1; 174 Random random = new Random(); 175 int[] freePorts = GetInUsedPort(address) 176 .Where(x => x >= (min = min <= 0 ? 1 : min)) 177 .ToArray(); 178 while (freePort < 0) 179 { 180 freePort = random.Next(min, 65536); 181 foreach (var item in freePorts) 182 { 183 if (freePort == item) 184 freePort = -1; 185 } 186 } 187 return freePort; 188 } 189 } 190 191 /// <summary> 192 /// TCP 网络服务的一个客户端连接。 193 /// </summary> 194 public class TcpConnection : IDisposable 195 { 196 private bool isClosing = false; // 当前连接 closing 标志位 197 private byte[] receiveBuffer; // 当前连接用于 receive 的数据缓冲区 198 private TcpClient tcpClient; // 当前连接的 TcpClient 对象 199 private NetworkStream networkStream; // 当前连接的 NetworkerStream 对象 200 private readonly Action<TcpConnection> initialize; // 当前连接的委托 201 202 /// <summary> 203 /// 通过指定的 TcpClient 和 Action 实例化一个与服务器建立连接的客户端。 204 /// </summary> 205 /// <param name="client">指定的 TcpClient</param> 206 /// <param name="action">客户端委托</param> 207 public TcpConnection(TcpClient client, Action<TcpConnection> action) 208 { 209 tcpClient = client; 210 initialize = action; 211 networkStream = tcpClient.GetStream(); 212 RemoteEndPoint = CopyEndPoint(tcpClient.Client.RemoteEndPoint); 213 OnAccept = x => { }; 214 OnReceive = (x, y) => { }; 215 OnClose = (x, y) => { }; 216 OnError = (x, y) => { }; 217 initialize(this); 218 OnAccept(this); 219 receiveBuffer = new byte[tcpClient.ReceiveBufferSize]; 220 StartReceive(); 221 } 222 223 /// <summary> 224 /// 获取 EndPoint 的深拷贝 225 /// </summary> 226 /// <param name="endPoint">需要拷贝的源对象</param> 227 /// <returns>目标副本</returns> 228 private EndPoint CopyEndPoint(EndPoint endPoint) 229 { 230 return endPoint.Create(endPoint.Serialize()); 231 } 232 233 /// <summary> 234 /// 从当前连接开始异步读取。 235 /// </summary> 236 private void StartReceive() 237 { 238 try 239 { 240 networkStream.BeginRead(receiveBuffer, 0, receiveBuffer.Length, iar => 241 { 242 try 243 { 244 if (isClosing) return; 245 int size = networkStream.EndRead(iar); 246 if (size == 0 || !tcpClient.Connected || !networkStream.CanRead) 247 { 248 Close(true); 249 return; 250 } 251 byte[] data = new byte[size]; 252 Buffer.BlockCopy(receiveBuffer, 0, data, 0, size); 253 OnReceive(this, data); 254 } 255 catch (Exception ex) 256 { 257 OnError(this, ex); 258 } 259 StartReceive(); 260 }, null); 261 } 262 catch (Exception ex) 263 { 264 OnError(this, ex); 265 } 266 } 267 268 /// <summary> 269 /// 获取或设置一个值,该值在发送或接收缓冲区未满时禁用延迟。 270 /// </summary> 271 public bool NoDelay 272 { 273 get { return tcpClient.NoDelay; } 274 set { tcpClient.NoDelay = value; } 275 } 276 277 /// <summary> 278 /// 获取已经从网络接收且可供读取的数据量。 279 /// </summary> 280 public int Available 281 { 282 get { return tcpClient.Available; } 283 } 284 285 /// <summary> 286 /// 获取或设置基础 Socket。 287 /// </summary> 288 public Socket Client 289 { 290 get { return tcpClient.Client; } 291 } 292 293 /// <summary> 294 /// 获取当前连接的终节点 295 /// </summary> 296 public EndPoint RemoteEndPoint 297 { 298 get; private set; 299 } 300 301 /// <summary> 302 /// 获取一个 bool 值,该值指示 Socket 是否已连接到远程主机。 303 /// </summary> 304 public bool Connected 305 { 306 get { return tcpClient.Connected; } 307 } 308 309 /// <summary> 310 /// 获取或设置 bool 值,该值指定是否只允许一个客户端使用端口。 311 /// </summary> 312 public bool ExclusiveAddressUse 313 { 314 get { return tcpClient.ExclusiveAddressUse; } 315 set { tcpClient.ExclusiveAddressUse = value; } 316 } 317 318 /// <summary> 319 /// 开始向当前连接异步写入。 320 /// </summary> 321 /// <param name="data">类型 Byte 的数组,该数组包含要写入的数据。</param> 322 /// <returns></returns> 323 public IAsyncResult Send(byte[] data) 324 { 325 try 326 { 327 return networkStream.BeginWrite(data, 0, data.Length, iar => 328 { 329 try 330 { 331 if (!tcpClient.Connected || !networkStream.CanRead) 332 { 333 Close(true); 334 return; 335 } 336 networkStream.EndWrite(iar); 337 } 338 catch (Exception ex) 339 { 340 OnError(this, ex); 341 } 342 }, null); 343 } 344 catch (Exception ex) 345 { 346 OnError(this, ex); 347 } 348 return null; 349 } 350 351 /// <summary> 352 /// 开始向当前连接异步写入。 353 /// </summary> 354 /// <param name="message">该 string 包含要写入的数据。</param> 355 /// <returns></returns> 356 public IAsyncResult Send(string message) 357 { 358 return Send(Encoding.UTF8.GetBytes(message)); 359 } 360 361 /// <summary> 362 /// 关闭基础连接并释放所有资源 363 /// </summary> 364 /// <param name="activeExit">是否主动关闭</param> 365 private void Close(bool activeExit) 366 { 367 isClosing = true; 368 networkStream.Close(); 369 tcpClient.Close(); 370 OnClose(this, activeExit); 371 } 372 373 /// <summary> 374 /// 关闭基础连接并释放所有资源 375 /// </summary> 376 public void Close() 377 { 378 Close(false); 379 } 380 381 /// <summary> 382 /// 关闭基础连接并释放所有资源,和Close()效果相同 383 /// </summary> 384 public void Dispose() 385 { 386 Close(false); 387 } 388 389 /// <summary> 390 /// 返回指定终结点的 IP 地址和端口号。 391 /// </summary> 392 /// <returns>包含指定终结点(例如,192.168.1.2:80)的 IP 地址和端口号的字符串。</returns> 393 public override string ToString() 394 { 395 return RemoteEndPoint.ToString(); 396 } 397 398 /// <summary> 399 /// TcpConnection 的 accept 事件 400 /// </summary> 401 public Action<TcpConnection> OnAccept { get; set; } 402 403 /// <summary> 404 /// TcpConnection 的 receive 事件 405 /// </summary> 406 public Action<TcpConnection, byte[]> OnReceive { get; set; } 407 408 /// <summary> 409 /// TcpConnection 的 close 事件 410 /// </summary> 411 public Action<TcpConnection, bool> OnClose { get; set; } 412 413 /// <summary> 414 /// TcpConnection 的 error 事件 415 /// </summary> 416 public Action<TcpConnection, Exception> OnError { get; set; } 417 418 }