TcpListener、TcpClient 、NetworkStream
1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Security.Cryptography; 5 using System.Net.Sockets; 6 using System.Net; 7 using System.IO; 8 using System.Threading; 9 10 namespace XuanJiSocket 11 { 12 /// <summary> 13 /// ClassName 玄机SocketHelper 14 /// Coding By 君临 15 /// </summary> 16 public class SocketHelper 17 { 18 #region 推送器 19 public delegate void PushSockets(Sockets sockets); 20 public static PushSockets pushSockets; 21 #endregion 22 /// <summary> 23 /// Tcp同步服务端,SocketObject继承抽象类 24 /// 服务端采用TcpListener封装. 25 /// 使用Semaphore 来控制并发,每次处理5个.最大处理5000 26 /// </summary> 27 public class TcpServer : SocketObject 28 { 29 bool IsStop = false; 30 object obj = new object(); 31 /// <summary> 32 /// 信号量 33 /// </summary> 34 private Semaphore semap = new Semaphore(5, 5000); 35 /// <summary> 36 /// 客户端队列集合 37 /// </summary> 38 public List<Sockets> ClientList = new List<Sockets>(); 39 /// <summary> 40 /// 服务端 41 /// </summary> 42 private TcpListener listener; 43 /// <summary> 44 /// 当前IP地址 45 /// </summary> 46 private IPAddress Ipaddress; 47 /// <summary> 48 /// 欢迎消息 49 /// </summary> 50 private string boundary = "www.xuanjics.com"; 51 /// <summary> 52 /// 当前监听端口 53 /// </summary> 54 private int Port; 55 /// <summary> 56 /// 当前IP,端口对象 57 /// </summary> 58 private IPEndPoint ip; 59 /// <summary> 60 /// 初始化服务端对象 61 /// </summary> 62 /// <param name="ipaddress">IP地址</param> 63 /// <param name="port">监听端口</param> 64 public override void InitSocket(IPAddress ipaddress, int port) 65 { 66 Ipaddress = ipaddress; 67 Port = port; 68 listener = new TcpListener(Ipaddress, Port); 69 } 70 /// <summary> 71 /// 初始化服务端对象 72 /// </summary> 73 /// <param name="ipaddress">IP地址</param> 74 /// <param name="port">监听端口</param> 75 public override void InitSocket(string ipaddress, int port) 76 { 77 Ipaddress = IPAddress.Parse(ipaddress); 78 Port = port; 79 ip = new IPEndPoint(Ipaddress, Port); 80 listener = new TcpListener(Ipaddress, Port); 81 } 82 /// <summary> 83 /// 启动监听,并处理连接 84 /// </summary> 85 public override void Start() 86 { 87 try 88 { 89 listener.Start(); 90 Thread AccTh = new Thread(new ThreadStart(delegate 91 { 92 while (true) 93 { 94 95 if (IsStop != false) 96 { 97 break; 98 } 99 GetAcceptTcpClient(); 100 Thread.Sleep(1); 101 } 102 })); 103 AccTh.Start(); 104 } 105 catch (SocketException skex) 106 { 107 Sockets sks = new Sockets(); 108 sks.ex = skex; 109 pushSockets.Invoke(sks);//推送至UI 110 111 } 112 } 113 /// <summary> 114 /// 等待处理新的连接 115 /// </summary> 116 private void GetAcceptTcpClient() 117 { 118 try 119 { 120 if (listener.Pending()) 121 { 122 semap.WaitOne(); 123 TcpClient tclient = listener.AcceptTcpClient(); 124 //维护客户端队列 125 Socket socket = tclient.Client; 126 NetworkStream stream = new NetworkStream(socket, true); //承载这个Socket 127 Sockets sks = new Sockets(tclient.Client.RemoteEndPoint as IPEndPoint, tclient, stream); 128 sks.NewClientFlag = true; 129 //推送新客户端 130 pushSockets.Invoke(sks); 131 //客户端异步接收 132 sks.nStream.BeginRead(sks.RecBuffer, 0, sks.RecBuffer.Length, new AsyncCallback(EndReader), sks); 133 //加入客户端集合. 134 AddClientList(sks); 135 //主动向客户端发送一条连接成功信息 136 if (stream.CanWrite) 137 { 138 byte[] buffer = Encoding.UTF8.GetBytes(boundary); 139 stream.Write(buffer, 0, buffer.Length); 140 } 141 semap.Release(); 142 } 143 } 144 catch 145 { 146 return; 147 } 148 } 149 /// <summary> 150 /// 异步接收发送的信息. 151 /// </summary> 152 /// <param name="ir"></param> 153 private void EndReader(IAsyncResult ir) 154 { 155 Sockets sks = ir.AsyncState as Sockets; 156 157 158 if (sks != null && listener != null) 159 { 160 try 161 { 162 if (sks.NewClientFlag || sks.Offset != 0) 163 { 164 sks.NewClientFlag = false; 165 sks.Offset = sks.nStream.EndRead(ir); 166 pushSockets.Invoke(sks);//推送至UI 167 sks.nStream.BeginRead(sks.RecBuffer, 0, sks.RecBuffer.Length, new AsyncCallback(EndReader), sks); 168 } 169 } 170 catch (Exception skex) 171 { 172 lock (obj) 173 { 174 //移除异常类 175 ClientList.Remove(sks); 176 Sockets sk = sks; 177 sk.ClientDispose = true;//客户端退出 178 sk.ex = skex; 179 pushSockets.Invoke(sks);//推送至UI 180 } 181 } 182 } 183 } 184 /// <summary> 185 /// 加入队列. 186 /// </summary> 187 /// <param name="sk"></param> 188 private void AddClientList(Sockets sk) 189 { 190 //虽然有信号量,还是用lock增加系数 191 lock (obj) 192 { 193 Sockets sockets = ClientList.Find(o => { return o.Ip == sk.Ip; }); 194 //如果不存在则添加,否则更新 195 if (sockets == null) 196 { 197 ClientList.Add(sk); 198 } 199 else 200 { 201 ClientList.Remove(sockets); 202 ClientList.Add(sk); 203 } 204 } 205 } 206 public override void Stop() 207 { 208 if (listener != null) 209 { 210 listener.Stop(); 211 listener = null; 212 IsStop = true; 213 SocketHelper.pushSockets = null; 214 } 215 } 216 /// <summary> 217 /// 向所有在线的客户端发送信息. 218 /// </summary> 219 /// <param name="SendData">发送的文本</param> 220 public void SendToAll(string SendData) 221 { 222 for (int i = 0; i < ClientList.Count; i++) 223 { 224 SendToClient(ClientList[i].Ip, SendData); 225 } 226 } 227 /// <summary> 228 /// 向某一位客户端发送信息 229 /// </summary> 230 /// <param name="ip">客户端IP+端口地址</param> 231 /// <param name="SendData">发送的数据包</param> 232 public void SendToClient(IPEndPoint ip, string SendData) 233 { 234 try 235 { 236 Sockets sks = ClientList.Find(o => { return o.Ip == ip; }); 237 if (sks == null || !sks.Client.Connected) 238 { 239 //没有连接时,标识退出 240 Sockets ks = new Sockets(); 241 sks.ClientDispose = true;//标识客户端下线 242 sks.ex = new Exception("客户端无连接"); 243 pushSockets.Invoke(sks);//推送至UI 244 } 245 if (sks.Client.Connected) 246 { 247 //获取当前流进行写入. 248 NetworkStream nStream = sks.nStream; 249 if (nStream.CanWrite) 250 { 251 byte[] buffer = Encoding.UTF8.GetBytes(SendData); 252 nStream.Write(buffer, 0, buffer.Length); 253 } 254 else 255 { 256 //避免流被关闭,重新从对象中获取流 257 nStream = sks.Client.GetStream(); 258 if (nStream.CanWrite) 259 { 260 byte[] buffer = Encoding.UTF8.GetBytes(SendData); 261 nStream.Write(buffer, 0, buffer.Length); 262 } 263 else 264 { 265 //如果还是无法写入,那么认为客户端中断连接. 266 ClientList.Remove(sks); 267 Sockets ks = new Sockets(); 268 sks.ClientDispose = true;//如果出现异常,标识客户端下线 269 sks.ex = new Exception("客户端无连接"); 270 pushSockets.Invoke(sks);//推送至UI 271 272 } 273 } 274 } 275 } 276 catch (Exception skex) 277 { 278 Sockets sks = new Sockets(); 279 sks.ClientDispose = true;//如果出现异常,标识客户端退出 280 sks.ex = skex; 281 pushSockets.Invoke(sks);//推送至UI 282 283 } 284 } 285 } 286 public class TcpClients : SocketObject 287 { 288 bool IsClose = false; 289 /// <summary> 290 /// 当前管理对象 291 /// </summary> 292 Sockets sk; 293 /// <summary> 294 /// 客户端 295 /// </summary> 296 TcpClient client; 297 /// <summary> 298 /// 当前连接服务端地址 299 /// </summary> 300 IPAddress Ipaddress; 301 /// <summary> 302 /// 当前连接服务端端口号 303 /// </summary> 304 int Port; 305 /// <summary> 306 /// 服务端IP+端口 307 /// </summary> 308 IPEndPoint ip; 309 /// <summary> 310 /// 发送与接收使用的流 311 /// </summary> 312 NetworkStream nStream; 313 /// <summary> 314 /// 初始化Socket 315 /// </summary> 316 /// <param name="ipaddress"></param> 317 /// <param name="port"></param> 318 public override void InitSocket(string ipaddress, int port) 319 { 320 Ipaddress = IPAddress.Parse(ipaddress); 321 Port = port; 322 ip = new IPEndPoint(Ipaddress, Port); 323 client = new TcpClient(); 324 } 325 public void SendData(string SendData) 326 { 327 try 328 { 329 330 if (client == null || !client.Connected) 331 { 332 Sockets sks = new Sockets(); 333 sks.ex = new Exception("客户端无连接.."); 334 sks.ClientDispose = true; 335 pushSockets.Invoke(sks);//推送至UI 336 } 337 if (client.Connected) //如果连接则发送 338 { 339 if (nStream == null) 340 { 341 nStream = client.GetStream(); 342 } 343 byte[] buffer = Encoding.UTF8.GetBytes(SendData); 344 nStream.Write(buffer, 0, buffer.Length); 345 } 346 } 347 catch (Exception skex) 348 { 349 Sockets sks = new Sockets(); 350 sks.ex = skex; 351 sks.ClientDispose = true; 352 pushSockets.Invoke(sks);//推送至UI 353 } 354 } 355 /// <summary> 356 /// 初始化Socket 357 /// </summary> 358 /// <param name="ipaddress"></param> 359 /// <param name="port"></param> 360 public override void InitSocket(IPAddress ipaddress, int port) 361 { 362 Ipaddress = ipaddress; 363 Port = port; 364 ip = new IPEndPoint(Ipaddress, Port); 365 client = new TcpClient(); 366 } 367 private void Connect() 368 { 369 client.Connect(ip); 370 nStream = new NetworkStream(client.Client, true); 371 sk = new Sockets(ip, client, nStream); 372 sk.nStream.BeginRead(sk.RecBuffer, 0, sk.RecBuffer.Length, new AsyncCallback(EndReader), sk); 373 } 374 private void EndReader(IAsyncResult ir) 375 { 376 377 Sockets s = ir.AsyncState as Sockets; 378 try 379 { 380 if (s != null) 381 { 382 383 if (IsClose && client == null) 384 { 385 sk.nStream.Close(); 386 sk.nStream.Dispose(); 387 return; 388 } 389 s.Offset = s.nStream.EndRead(ir); 390 pushSockets.Invoke(s);//推送至UI 391 sk.nStream.BeginRead(sk.RecBuffer, 0, sk.RecBuffer.Length, new AsyncCallback(EndReader), sk); 392 } 393 } 394 catch (Exception skex) 395 { 396 Sockets sks = s; 397 sks.ex = skex; 398 sks.ClientDispose = true; 399 pushSockets.Invoke(sks);//推送至UI 400 401 } 402 403 } 404 /// <summary> 405 /// 重写Start方法,其实就是连接服务端 406 /// </summary> 407 public override void Start() 408 { 409 Connect(); 410 } 411 public override void Stop() 412 { 413 Sockets sks = new Sockets(); 414 if (client != null) 415 { 416 client.Client.Shutdown(SocketShutdown.Both); 417 Thread.Sleep(10); 418 client.Close(); 419 IsClose = true; 420 client = null; 421 } 422 else 423 { 424 sks.ex = new Exception("客户端没有初始化.!"); 425 } 426 pushSockets.Invoke(sks);//推送至UI 427 } 428 429 } 430 /// <summary> 431 /// Socket基类(抽象类) 432 /// 抽象3个方法,初始化Socket(含一个构造),停止,启动方法. 433 /// 此抽象类为TcpServer与TcpClient的基类,前者实现后者抽象方法. 434 /// 作用: 纯属闲的蛋疼,不写个OO的我感觉不会写代码了...What The Fuck... 435 /// </summary> 436 public abstract class SocketObject 437 { 438 public abstract void InitSocket(IPAddress ipaddress, int port); 439 public abstract void InitSocket(string ipaddress, int port); 440 public abstract void Start(); 441 public abstract void Stop(); 442 } 443 /// <summary> 444 /// 自定义Socket对象 445 /// </summary> 446 public class Sockets 447 { 448 /// <summary> 449 /// 接收缓冲区 450 /// </summary> 451 public byte[] RecBuffer = new byte[8 * 1024]; 452 /// <summary> 453 /// 发送缓冲区 454 /// </summary> 455 public byte[] SendBuffer = new byte[8 * 1024]; 456 /// <summary> 457 /// 异步接收后包的大小 458 /// </summary> 459 public int Offset { get; set; } 460 /// <summary> 461 /// 空构造 462 /// </summary> 463 public Sockets() { } 464 /// <summary> 465 /// 创建Sockets对象 466 /// </summary> 467 /// <param name="ip">Ip地址</param> 468 /// <param name="client">TcpClient</param> 469 /// <param name="ns">承载客户端Socket的网络流</param> 470 public Sockets(IPEndPoint ip, TcpClient client, NetworkStream ns) 471 { 472 Ip = ip; 473 Client = client; 474 nStream = ns; 475 } 476 /// <summary> 477 /// 当前IP地址,端口号 478 /// </summary> 479 public IPEndPoint Ip { get; set; } 480 /// <summary> 481 /// 客户端主通信程序 482 /// </summary> 483 public TcpClient Client { get; set; } 484 /// <summary> 485 /// 承载客户端Socket的网络流 486 /// </summary> 487 public NetworkStream nStream { get; set; } 488 /// <summary> 489 /// 发生异常时不为null. 490 /// </summary> 491 public Exception ex { get; set; } 492 /// <summary> 493 /// 新客户端标识.如果推送器发现此标识为true,那么认为是客户端上线 494 /// 仅服务端有效 495 /// </summary> 496 public bool NewClientFlag { get; set; } 497 /// <summary> 498 /// 客户端退出标识.如果服务端发现此标识为true,那么认为客户端下线 499 /// 客户端接收此标识时,认为客户端异常. 500 /// </summary> 501 public bool ClientDispose { get; set; } 502 } 503 } 504 }