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 }