C# .NET Socket 简单实用框架

背景:

首先向各位前辈,大哥哥小姐姐问一声好~

这是我第一次写博客,目前为一个即将步入大四的学生,上学期在一家公司实习了半年,后期发现没有动力,而且由于薪水问题(废话嘛),于是跳槽到这家新的公司。

说到Socket,想必大家都或多或少有所涉及,从最初的计算机网络课程,讲述了tcp协议,而Socket就是对协议的进一步封装,使我们开发人员能够更加容易轻松的进行软件之间的通信。

这个星期刚好接受一个共享车位锁的项目,需要使用Socket与硬件进行通信控制,说白了也就是给锁发送指令,控制其打开或者关闭,再就是对App开放操作接口,使其方便测试以及用户的使用。这其中核心就是Socket的使用,再开发出这个功能之后,我发现使用起来很不方便,于是耗时2天抽象其核心功能并封装成框架,最后使用这个框架将原来的项目重构并上线,极大的提高了软件的可拓展性,健壮性,容错率。

个人坚信的原则:万物皆对象

好了,不废话了,下面进入正文

正文:

1、首先简单讲下C#中Socket的简单使用。

第一步:服务端监听某个端口

第二步:客户端向服务端地址和端口发起Socket连接请求

第三步:服务端收到连接请求后创建Socket连接,并维护这个连接队列。

第四步:客户端和服务端已经建立双工通信(即双向通信),客户端和服务端可以轻松方便的给彼此发送信息。

至于简单使用的具体实现代码全部被我封装到项目中了,如果需要学习简单的实现,可以看我的源码,也可以自行百度,有很多的教程

2、核心,框架的使用

其实,说其为框架,可能有点牵强,因为每个人对框架都有自己的理解,但是类库和框架又有什么本质区别呢?全部都是代码~哈哈,扯远了

首先,空说无凭,先放上所有的代码:

服务端源文件:

1SocketServer.cs 2 3using System; 4using System.Collections.Generic; 5using System.Net; 6using System.Net.Sockets; 7 8namespace Coldairarrow.Util.Sockets 9{ 10 /// <summary> 11 /// Socket服务端 12 /// </summary> 13 public class SocketServer 14 { 15 #region 构造函数 16 17 /// <summary> 18 /// 构造函数 19 /// </summary> 20 /// <param name="ip">监听的IP地址</param> 21 /// <param name="port">监听的端口</param> 22 public SocketServer(string ip, int port) 23 { 24 _ip = ip; 25 _port = port; 26 } 27 28 /// <summary> 29 /// 构造函数,监听IP地址默认为本机0.0.0.0 30 /// </summary> 31 /// <param name="port">监听的端口</param> 32 public SocketServer(int port) 33 { 34 _ip = "0.0.0.0"; 35 _port = port; 36 } 37 38 #endregion 39 40 #region 内部成员 41 42 private Socket _socket = null; 43 private string _ip = ""; 44 private int _port = 0; 45 private bool _isListen = true; 46 private void StartListen() 47 { 48 try 49 { 50 _socket.BeginAccept(asyncResult => 51 { 52 try 53 { 54 Socket newSocket = _socket.EndAccept(asyncResult); 55 56 //马上进行下一轮监听,增加吞吐量 57 if (_isListen) 58 StartListen(); 59 60 SocketConnection newClient = new SocketConnection(newSocket, this) 61 { 62 HandleRecMsg = HandleRecMsg == null ? null : new Action<byte[], SocketConnection, SocketServer>(HandleRecMsg), 63 HandleClientClose = HandleClientClose == null ? null : new Action<SocketConnection, SocketServer>(HandleClientClose), 64 HandleSendMsg = HandleSendMsg == null ? null : new Action<byte[], SocketConnection, SocketServer>(HandleSendMsg), 65 HandleException = HandleException == null ? null : new Action<Exception>(HandleException) 66 }; 67 68 newClient.StartRecMsg(); 69 ClientList.AddLast(newClient); 70 71 HandleNewClientConnected?.Invoke(this, newClient); 72 } 73 catch (Exception ex) 74 { 75 HandleException?.Invoke(ex); 76 } 77 }, null); 78 } 79 catch (Exception ex) 80 { 81 HandleException?.Invoke(ex); 82 } 83 } 84 85 #endregion 86 87 #region 外部接口 88 89 /// <summary> 90 /// 开始服务,监听客户端 91 /// </summary> 92 public void StartServer() 93 { 94 try 95 { 96 //实例化套接字(ip4寻址协议,流式传输,TCP协议) 97 _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 98 //创建ip对象 99 IPAddress address = IPAddress.Parse(_ip); 100 //创建网络节点对象包含ip和port 101 IPEndPoint endpoint = new IPEndPoint(address, _port); 102 //将 监听套接字绑定到 对应的IP和端口 103 _socket.Bind(endpoint); 104 //设置监听队列长度为Int32最大值(同时能够处理连接请求数量) 105 _socket.Listen(int.MaxValue); 106 //开始监听客户端 107 StartListen(); 108 HandleServerStarted?.Invoke(this); 109 } 110 catch (Exception ex) 111 { 112 HandleException?.Invoke(ex); 113 } 114 } 115 116 /// <summary> 117 /// 所有连接的客户端列表 118 /// </summary> 119 public LinkedList<SocketConnection> ClientList { get; set; } = new LinkedList<SocketConnection>(); 120 121 /// <summary> 122 /// 关闭指定客户端连接 123 /// </summary> 124 /// <param name="theClient">指定的客户端连接</param> 125 public void CloseClient(SocketConnection theClient) 126 { 127 theClient.Close(); 128 } 129 130 #endregion 131 132 #region 公共事件 133 134 /// <summary> 135 /// 异常处理程序 136 /// </summary> 137 public Action<Exception> HandleException { get; set; } 138 139 #endregion 140 141 #region 服务端事件 142 143 /// <summary> 144 /// 服务启动后执行 145 /// </summary> 146 public Action<SocketServer> HandleServerStarted { get; set; } 147 148 /// <summary> 149 /// 当新客户端连接后执行 150 /// </summary> 151 public Action<SocketServer, SocketConnection> HandleNewClientConnected { get; set; } 152 153 /// <summary> 154 /// 服务端关闭客户端后执行 155 /// </summary> 156 public Action<SocketServer, SocketConnection> HandleCloseClient { get; set; } 157 158 #endregion 159 160 #region 客户端连接事件 161 162 /// <summary> 163 /// 客户端连接接受新的消息后调用 164 /// </summary> 165 public Action<byte[], SocketConnection, SocketServer> HandleRecMsg { get; set; } 166 167 /// <summary> 168 /// 客户端连接发送消息后回调 169 /// </summary> 170 public Action<byte[], SocketConnection, SocketServer> HandleSendMsg { get; set; } 171 172 /// <summary> 173 /// 客户端连接关闭后回调 174 /// </summary> 175 public Action<SocketConnection, SocketServer> HandleClientClose { get; set; } 176 177 #endregion 178 } 179} 180 181using System; 182using System.Net.Sockets; 183using System.Text; 184 185namespace Coldairarrow.Util.Sockets 186{ 187 /// <summary> 188 /// Socket连接,双向通信 189 /// </summary> 190 public class SocketConnection 191 { 192 #region 构造函数 193 194 public SocketConnection(Socket socket,SocketServer server) 195 { 196 _socket = socket; 197 _server = server; 198 } 199 200 #endregion 201 202 #region 私有成员 203 204 private readonly Socket _socket; 205 private bool _isRec=true; 206 private SocketServer _server = null; 207 private bool IsSocketConnected() 208 { 209 bool part1 = _socket.Poll(1000, SelectMode.SelectRead); 210 bool part2 = (_socket.Available == 0); 211 if (part1 && part2) 212 return false; 213 else 214 return true; 215 } 216 217 #endregion 218 219 #region 外部接口 220 221 /// <summary> 222 /// 开始接受客户端消息 223 /// </summary> 224 public void StartRecMsg() 225 { 226 try 227 { 228 byte[] container = new byte[1024 * 1024 * 2]; 229 _socket.BeginReceive(container, 0, container.Length, SocketFlags.None, asyncResult => 230 { 231 try 232 { 233 int length = _socket.EndReceive(asyncResult); 234 235 //马上进行下一轮接受,增加吞吐量 236 if (length > 0 && _isRec && IsSocketConnected()) 237 StartRecMsg(); 238 239 if (length > 0) 240 { 241 byte[] recBytes = new byte[length]; 242 Array.Copy(container, 0, recBytes, 0, length); 243 244 //处理消息 245 HandleRecMsg?.Invoke(recBytes, this, _server); 246 } 247 else 248 Close(); 249 } 250 catch (Exception ex) 251 { 252 HandleException?.Invoke(ex); 253 Close(); 254 } 255 }, null); 256 } 257 catch (Exception ex) 258 { 259 HandleException?.Invoke(ex); 260 Close(); 261 } 262 } 263 264 /// <summary> 265 /// 发送数据 266 /// </summary> 267 /// <param name="bytes">数据字节</param> 268 public void Send(byte[] bytes) 269 { 270 try 271 { 272 _socket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, asyncResult => 273 { 274 try 275 { 276 int length = _socket.EndSend(asyncResult); 277 HandleSendMsg?.Invoke(bytes, this, _server); 278 } 279 catch (Exception ex) 280 { 281 HandleException?.Invoke(ex); 282 } 283 }, null); 284 } 285 catch (Exception ex) 286 { 287 HandleException?.Invoke(ex); 288 } 289 } 290 291 /// <summary> 292 /// 发送字符串(默认使用UTF-8编码) 293 /// </summary> 294 /// <param name="msgStr">字符串</param> 295 public void Send(string msgStr) 296 { 297 Send(Encoding.UTF8.GetBytes(msgStr)); 298 } 299 300 /// <summary> 301 /// 发送字符串(使用自定义编码) 302 /// </summary> 303 /// <param name="msgStr">字符串消息</param> 304 /// <param name="encoding">使用的编码</param> 305 public void Send(string msgStr,Encoding encoding) 306 { 307 Send(encoding.GetBytes(msgStr)); 308 } 309 310 /// <summary> 311 /// 传入自定义属性 312 /// </summary> 313 public object Property { get; set; } 314 315 /// <summary> 316 /// 关闭当前连接 317 /// </summary> 318 public void Close() 319 { 320 try 321 { 322 _isRec = false; 323 _socket.Disconnect(false); 324 _server.ClientList.Remove(this); 325 HandleClientClose?.Invoke(this, _server); 326 _socket.Close(); 327 _socket.Dispose(); 328 GC.Collect(); 329 } 330 catch (Exception ex) 331 { 332 HandleException?.Invoke(ex); 333 } 334 } 335 336 #endregion 337 338 #region 事件处理 339 340 /// <summary> 341 /// 客户端连接接受新的消息后调用 342 /// </summary> 343 public Action<byte[], SocketConnection, SocketServer> HandleRecMsg { get; set; } 344 345 /// <summary> 346 /// 客户端连接发送消息后回调 347 /// </summary> 348 public Action<byte[], SocketConnection, SocketServer> HandleSendMsg { get; set; } 349 350 /// <summary> 351 /// 客户端连接关闭后回调 352 /// </summary> 353 public Action<SocketConnection, SocketServer> HandleClientClose { get; set; } 354 355 /// <summary> 356 /// 异常处理程序 357 /// </summary> 358 public Action<Exception> HandleException { get; set; } 359 360 #endregion 361 } 362} 363 364using System; 365using System.Net; 366using System.Net.Sockets; 367using System.Text; 368 369namespace Coldairarrow.Util.Sockets 370{ 371 /// <summary> 372 /// Socket客户端 373 /// </summary> 374 public class SocketClient 375 { 376 #region 构造函数 377 378 /// <summary> 379 /// 构造函数,连接服务器IP地址默认为本机127.0.0.1 380 /// </summary> 381 /// <param name="port">监听的端口</param> 382 public SocketClient(int port) 383 { 384 _ip = "127.0.0.1"; 385 _port = port; 386 } 387 388 /// <summary> 389 /// 构造函数 390 /// </summary> 391 /// <param name="ip">监听的IP地址</param> 392 /// <param name="port">监听的端口</param> 393 public SocketClient(string ip, int port) 394 { 395 _ip = ip; 396 _port = port; 397 } 398 399 #endregion 400 401 #region 内部成员 402 403 private Socket _socket = null; 404 private string _ip = ""; 405 private int _port = 0; 406 private bool _isRec=true; 407 private bool IsSocketConnected() 408 { 409 bool part1 = _socket.Poll(1000, SelectMode.SelectRead); 410 bool part2 = (_socket.Available == 0); 411 if (part1 && part2) 412 return false; 413 else 414 return true; 415 } 416 417 /// <summary> 418 /// 开始接受客户端消息 419 /// </summary> 420 public void StartRecMsg() 421 { 422 try 423 { 424 byte[] container = new byte[1024 * 1024 * 2]; 425 _socket.BeginReceive(container, 0, container.Length, SocketFlags.None, asyncResult => 426 { 427 try 428 { 429 int length = _socket.EndReceive(asyncResult); 430 431 //马上进行下一轮接受,增加吞吐量 432 if (length > 0 && _isRec && IsSocketConnected()) 433 StartRecMsg(); 434 435 if (length > 0) 436 { 437 byte[] recBytes = new byte[length]; 438 Array.Copy(container, 0, recBytes, 0, length); 439 440 //处理消息 441 HandleRecMsg?.Invoke(recBytes, this); 442 } 443 else 444 Close(); 445 } 446 catch (Exception ex) 447 { 448 HandleException?.Invoke(ex); 449 Close(); 450 } 451 }, null); 452 } 453 catch (Exception ex) 454 { 455 HandleException?.Invoke(ex); 456 Close(); 457 } 458 } 459 460 #endregion 461 462 #region 外部接口 463 464 /// <summary> 465 /// 开始服务,连接服务端 466 /// </summary> 467 public void StartClient() 468 { 469 try 470 { 471 //实例化 套接字 (ip4寻址协议,流式传输,TCP协议) 472 _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 473 //创建 ip对象 474 IPAddress address = IPAddress.Parse(_ip); 475 //创建网络节点对象 包含 ip和port 476 IPEndPoint endpoint = new IPEndPoint(address, _port); 477 //将 监听套接字 绑定到 对应的IP和端口 478 _socket.BeginConnect(endpoint, asyncResult => 479 { 480 try 481 { 482 _socket.EndConnect(asyncResult); 483 //开始接受服务器消息 484 StartRecMsg(); 485 486 HandleClientStarted?.Invoke(this); 487 } 488 catch (Exception ex) 489 { 490 HandleException?.Invoke(ex); 491 } 492 }, null); 493 } 494 catch (Exception ex) 495 { 496 HandleException?.Invoke(ex); 497 } 498 } 499 500 /// <summary> 501 /// 发送数据 502 /// </summary> 503 /// <param name="bytes">数据字节</param> 504 public void Send(byte[] bytes) 505 { 506 try 507 { 508 _socket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, asyncResult => 509 { 510 try 511 { 512 int length = _socket.EndSend(asyncResult); 513 HandleSendMsg?.Invoke(bytes, this); 514 } 515 catch (Exception ex) 516 { 517 HandleException?.Invoke(ex); 518 } 519 }, null); 520 } 521 catch (Exception ex) 522 { 523 HandleException?.Invoke(ex); 524 } 525 } 526 527 /// <summary> 528 /// 发送字符串(默认使用UTF-8编码) 529 /// </summary> 530 /// <param name="msgStr">字符串</param> 531 public void Send(string msgStr) 532 { 533 Send(Encoding.UTF8.GetBytes(msgStr)); 534 } 535 536 /// <summary> 537 /// 发送字符串(使用自定义编码) 538 /// </summary> 539 /// <param name="msgStr">字符串消息</param> 540 /// <param name="encoding">使用的编码</param> 541 public void Send(string msgStr, Encoding encoding) 542 { 543 Send(encoding.GetBytes(msgStr)); 544 } 545 546 /// <summary> 547 /// 传入自定义属性 548 /// </summary> 549 public object Property { get; set; } 550 551 /// <summary> 552 /// 关闭与服务器的连接 553 /// </summary> 554 public void Close() 555 { 556 try 557 { 558 _isRec = false; 559 _socket.Disconnect(false); 560 HandleClientClose?.Invoke(this); 561 } 562 catch (Exception ex) 563 { 564 HandleException?.Invoke(ex); 565 } 566 } 567 568 #endregion 569 570 #region 事件处理 571 572 /// <summary> 573 /// 客户端连接建立后回调 574 /// </summary> 575 public Action<SocketClient> HandleClientStarted { get; set; } 576 577 /// <summary> 578 /// 处理接受消息的委托 579 /// </summary> 580 public Action<byte[], SocketClient> HandleRecMsg { get; set; } 581 582 /// <summary> 583 /// 客户端连接发送消息后回调 584 /// </summary> 585 public Action<byte[], SocketClient> HandleSendMsg { get; set; } 586 587 /// <summary> 588 /// 客户端连接关闭后回调 589 /// </summary> 590 public Action<SocketClient> HandleClientClose { get; set; } 591 592 /// <summary> 593 /// 异常处理程序 594 /// </summary> 595 public Action<Exception> HandleException { get; set; } 596 597 #endregion 598 } 599}

上面放上的是框架代码,接下来介绍下如何使用

首先,服务端使用方式:

1using Coldairarrow.Util.Sockets; 2using System; 3using System.Text; 4 5namespace Console_Server 6{ 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 //创建服务器对象,默认监听本机0.0.0.0,端口12345 12 SocketServer server = new SocketServer(12345); 13 14 //处理从客户端收到的消息 15 server.HandleRecMsg = new Action<byte[], SocketConnection, SocketServer>((bytes, client, theServer) => 16 { 17 string msg = Encoding.UTF8.GetString(bytes); 18 Console.WriteLine($"收到消息:{msg}"); 19 }); 20 21 //处理服务器启动后事件 22 server.HandleServerStarted = new Action<SocketServer>(theServer => 23 { 24 Console.WriteLine("服务已启动************"); 25 }); 26 27 //处理新的客户端连接后的事件 28 server.HandleNewClientConnected = new Action<SocketServer, SocketConnection>((theServer, theCon) => 29 { 30 Console.WriteLine($@"一个新的客户端接入,当前连接数:{theServer.ClientList.Count}"); 31 }); 32 33 //处理客户端连接关闭后的事件 34 server.HandleClientClose = new Action<SocketConnection, SocketServer>((theCon, theServer) => 35 { 36 Console.WriteLine($@"一个客户端关闭,当前连接数为:{theServer.ClientList.Count}"); 37 }); 38 39 //处理异常 40 server.HandleException = new Action<Exception>(ex => 41 { 42 Console.WriteLine(ex.Message); 43 }); 44 45 //服务器启动 46 server.StartServer(); 47 48 while (true) 49 { 50 Console.WriteLine("输入:quit,关闭服务器"); 51 string op = Console.ReadLine(); 52 if (op == "quit") 53 break; 54 } 55 } 56 } 57}

客户端使用方式:

1using Coldairarrow.Util.Sockets; 2using System; 3using System.Text; 4 5namespace Console_Client 6{ 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 //创建客户端对象,默认连接本机127.0.0.1,端口为12345 12 SocketClient client = new SocketClient(12345); 13 14 //绑定当收到服务器发送的消息后的处理事件 15 client.HandleRecMsg = new Action<byte[], SocketClient>((bytes, theClient) => 16 { 17 string msg = Encoding.UTF8.GetString(bytes); 18 Console.WriteLine($"收到消息:{msg}"); 19 }); 20 21 //绑定向服务器发送消息后的处理事件 22 client.HandleSendMsg = new Action<byte[], SocketClient>((bytes, theClient) => 23 { 24 string msg = Encoding.UTF8.GetString(bytes); 25 Console.WriteLine($"向服务器发送消息:{msg}"); 26 }); 27 28 //开始运行客户端 29 client.StartClient(); 30 31 while (true) 32 { 33 Console.WriteLine("输入:quit关闭客户端,输入其它消息发送到服务器"); 34 string str = Console.ReadLine(); 35 if (str == "quit") 36 { 37 client.Close(); 38 break; 39 } 40 else 41 { 42 client.Send(str); 43 } 44 } 45 } 46 } 47}

最后运行测试截图:

总结:

其最方便之处在于,将如何创建连接封装掉,使用人员只需关注连接后发送什么数据,接收到数据后应该如何处理,等等其它的很多事件的处理,这其中主要依托于匿名委托的使用,Lambda表达式的使用。

框架里面主要使用了异步通讯,以及如何控制连接,详细我就不多说了,大家应该一看就懂,我只希望能给大家带来便利,最后大家有任何问题、意见、想法,都可以给我留言。

最后,附上所有源码项目地址,若觉得有一定价值,还请点赞~

GitHub地址:https://github.com/Coldairarrow/Sockets

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

C# .NET Socket 简单实用框架 - HelloWorld