C#异步的Socket通信(重构局域网聊天小工具)
五一在家看了一会儿<重构手册>, 想拿以前写的代码尝试着改进改进, 想起去年暑假写的局域网聊天小工具, 现在看自己那时写的代码已经不堪入目, 最不可思议的当属用了"多线程"处理网络请求, 现在觉得应该使用异步方法.
主要设计
简要说明
上图左边部分表示的是客户端的过程, 右边部分表示的是服务端的过程. 客户端相比服务端在建立连接之前步骤稍微少一些, 成功建立连接后客户端和服务端都有一个CommunicateSocket负责与对方通信, 如发送消息, 接收消息, 发送文件, 接收文件等.
服务端, 声明ServerSocket, 绑定(Bind)一个IP并指定这个IP的通信端口, 比如是127.0.0.1:9050, ServerSocket可以监听来自多个IP发送的连接请求, 监听(Listen)方法的参数可以设置允许的最多连接请求个数. 然后调用异步接受请求的方法(BeginAccept), 如果接受到某个客户端发来连接请求, 这时定义一个新的CommunicateSocket专门负责与这个客户端通信. 然后可以通过CommunicateSocket.BeginSend()方法给客户端发送数据, CommunicateSocket.BeginReceive()可以接收客户端发来的数据.
客户端, 有一个CommunicateSocket, 并绑定一个IP以及一个未被占用的端口, 定义IPEndPoint serverIP表示服务端Socket的IP和端口, 这样才可以进行端口对端口之间的通信, 接下来就可以尝试CommunicateSocket.BeginConnect(serverIP), 连接成功之后就可以发送和接收数据了, CommunicateSocket.BeginSend(), CommunicateSocket.BeginReceive().
有些异步方法有两种实现方式, 如BeginAccept()和AcceptAsync(), 这两个方法有什么区别呢? 以 Begin 和 End 开头的方法是以 APM(Asynchronous Programming Model)设计方法实现的异步操作, 以 Async 结尾的方法是利用称为 EAP (Event-based Asynchronous Pattern) 的设计方法实现的异步操作.
代码部分
1. SocketFunc类
SocketFunc是一个抽象类, 服务端和客户端只有建立连接的方法不同, 其它都相同, 所以把相同的部分放到这个类中.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public abstract class SocketFunc { //不管是服务端还是客户端, 建立连接后用这个Socket进行通信 public Socket communicateSocket = null ; //服务端和客户端建立连接的方式稍有不同, 子类会重载 public abstract void Access( string IP, System.Action AccessAciton); //发送消息的函数 public void Send( string message) { if (communicateSocket.Connected == false ) { throw new Exception( "还没有建立连接, 不能发送消息" ); } Byte[] msg = Encoding.UTF8.GetBytes(message); communicateSocket.BeginSend(msg,0, msg.Length, SocketFlags.None, ar => { }, null ); } //接受消息的函数 public void Receive(System.Action< string > ReceiveAction) { //如果消息超过1024个字节, 收到的消息会分为(总字节长度/1024 +1)条显示 Byte[] msg = new byte [1024]; //异步的接受消息 communicateSocket.BeginReceive(msg, 0, msg.Length, SocketFlags.None, ar => { //对方断开连接时, 这里抛出Socket Exception //An existing connection was forcibly closed by the remote host communicateSocket.EndReceive(ar); ReceiveAction(Encoding.UTF8.GetString(msg).Trim( '\0' , ' ' )); Receive(ReceiveAction); }, null ); } } |
2. ServerSocket:SocketFunc类
继承自SocketFunc类, 类中重载了Access方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class ServerSocket:SocketFunc { //服务端重载Access函数 public override void Access( string IP, System.Action AccessAciton) { Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //本机预使用的IP和端口 IPEndPoint serverIP = new IPEndPoint(IPAddress.Any, 9050); //绑定服务端设置的IP serverSocket.Bind(serverIP); //设置监听个数 serverSocket.Listen(1); //异步接收连接请求 serverSocket.BeginAccept(ar => { base .communicateSocket = serverSocket.EndAccept(ar); AccessAciton(); }, null ); } } |
3. ClientSocket:SocketFunc类
继承自SocketFunc类, 类中重载了Access方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public class ClientSocket:SocketFunc { //客户端重载Access函数 public override void Access( string IP, System.Action AccessAciton) { base .communicateSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); base .communicateSocket.Bind( new IPEndPoint(IPAddress.Any, 9051)); //服务器的IP和端口 IPEndPoint serverIP; try { serverIP = new IPEndPoint(IPAddress.Parse(IP), 9050); } catch { throw new Exception(String.Format( "{0}不是一个有效的IP地址!" , IP)); } //客户端只用来向指定的服务器发送信息,不需要绑定本机的IP和端口,不需要监听 try { base .communicateSocket.BeginConnect(serverIP, ar => { AccessAciton(); }, null ); } catch { throw new Exception( string .Format( "尝试连接{0}不成功!" , IP)); } } } |
程序截图
源码下载
作者:Create Chen
出处:http://technology.cnblogs.com
说明:文章为作者平时里的思考和练习,可能有不当之处,请博客园的园友们多提宝贵意见。
本作品采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库