范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

C高性能TCP服务的多种实现方式

  哎~~ 想想大部分园友应该对 "高性能" 字样更感兴趣,为了吸引眼球所以标题中一定要突出,其实我更喜欢的标题是 《猴赛雷,C# 编写 TCP 服务的花样姿势!》 。
  本篇文章的主旨是使用 .NET/C# 实现 TCP 高性能服务的不同方式,包括但不限于如下内容: APM 方式,即 Asynchronous Programming Model TAP 方式,即 Task-based Asynchronous Pattern SAEA 方式,即 SocketAsyncEventArgs RIO 方式,即 Registered I/O
  在 .NET/C# 中对于 Socket 的支持均是基于 Windows I/O Completion Ports 完成端口技术的封装,通过不同的 Non-Blocking 封装结构来满足不同的编程需求。以上方式均已在 Cowboy.Sockets 中有完整实现,并且 APM 和 TAP 方式已经在实际项目中应用。Cowboy.Sockets 还在不断的进化和完善中,如有任何问题请及时指正。
  虽然有这么多种实现方式,但抽象的看,它们是一样一样的,用两个 Loop 即可描述: Accept Loop  和  Read Loop ,如下图所示。(这里提及的 " Loop " 指的是一种 循环方式 ,而非特指 while/for 等关键字。)
  在任何 TCP Server 的实现中,一定存在一个 Accept Socket Loop,用于接收 Client 端的 Connect 请求以建立 TCP Connection。 在任何 TCP Server 的实现中,一定存在一个 Read Socket Loop,用于接收 Client 端 Write 过来的数据。
  如果 Accept 循环阻塞,则会导致无法快速的建立连接,服务端 Pending Backlog 满,进而导致 Client 端收到 Connect Timeout 的异常。如果 Read 循环阻塞,则显然会导致无法及时收到 Client 端发过来的数据,进而导致 Client 端 Send Buffer 满,无法再发送数据。
  从实现细节的角度看,能够导致服务阻塞的位置可能在: Accept 到新的 Socket,构建新的 Connection 需要分配各种资源,分配资源慢; Accept 到新的 Socket,没有及时触发下一次 Accept; Read 到新的 Buffer,判定 Payload 消息长度,判定过程长; Read 到新的 Buffer,发现 Payload 还没有收全,继续 Read,则 "可能" 会导致一次 Buffer Copy; Payload 接收完毕,进行 De-Serialization 转成可识别的 Protocol Message,反序列化慢; 由 Business Module 来处理相应的 Protocol Message,处理过程慢;
  1-2 涉及到 Accept 过程和 Connection 的建立过程,3-4 涉及到 ReceiveBuffer 的处理过程,5-6 涉及到应用逻辑侧的实现。
  Java 中著名的 Netty 网络库从 4.0 版本开始对于 Buffer 部分做了全新的尝试,采用了名叫 ByteBuf 的设计,实现 Buffer Zero Copy 以减少高并发条件下 Buffer 拷贝带来的性能损失和 GC 压力。DotNetty,Orleans ,Helios 等项目正在尝试在 C# 中进行类似的 ByteBuf 的实现。 APM 方式:TcpSocketServer
  TcpSocketServer 的实现是基于 .NET Framework 自带的 TcpListener 和 TcpClient 的更进一步的封装,采用基于 APM 的 BeginXXX 和 EndXXX 接口实现。
  TcpSocketServer 中的 Accept Loop 指的就是, BeginAccept -> EndAccept-> BeginAccept -> EndAccept -> BeginAccept -> ...
  每一个建立成功的 Connection 由 TcpSocketSession 来处理,所以 TcpSocketSession 中会包含 Read Loop, BeginRead -> EndRead -> BeginRead -> EndRead -> BeginRead -> ...
  TcpSocketServer 通过暴露 Event 来实现 Connection 的建立与断开和数据接收的通知。   event EventHandler ClientConnected;   event EventHandler ClientDisconnected;   event EventHandler ClientDataReceived;
  使用也是简单直接,直接订阅事件通知。
  private static void StartServer()   {       _server = new TcpSocketServer(22222);       _server.ClientConnected += server_ClientConnected;       _server.ClientDisconnected += server_ClientDisconnected;       _server.ClientDataReceived += server_ClientDataReceived;       _server.Listen();   }      static void server_ClientConnected(object sender, TcpClientConnectedEventArgs e)   {       Console.WriteLine(string.Format("TCP client {0} has connected {1}.", e.Session.RemoteEndPoint, e.Session));   }      static void server_ClientDisconnected(object sender, TcpClientDisconnectedEventArgs e)   {       Console.WriteLine(string.Format("TCP client {0} has disconnected.", e.Session));   }      static void server_ClientDataReceived(object sender, TcpClientDataReceivedEventArgs e)   {       var text = Encoding.UTF8.GetString(e.Data, e.DataOffset, e.DataLength);       Console.Write(string.Format("Client : {0} {1} --> ", e.Session.RemoteEndPoint, e.Session));       Console.WriteLine(string.Format("{0}", text));       _server.Broadcast(Encoding.UTF8.GetBytes(text));   }
  TAP 方式:AsyncTcpSocketServer
  AsyncTcpSocketServer 的实现是基于 .NET Framework 自带的 TcpListener 和 TcpClient 的更进一步的封装,采用基于 TAP 的 async/await 的 XXXAsync 接口实现。
  然而,实际上 XXXAsync 并没有创建什么神奇的效果,其内部实现只是将 APM 的方法转换成了 TAP 的调用方式。
  //************* Task-based async public methods *************************   [HostProtection(ExternalThreading = true)]   public Task AcceptSocketAsync()   {       return Task.Factory.FromAsync(BeginAcceptSocket, EndAcceptSocket, null);   }      [HostProtection(ExternalThreading = true)]   public Task AcceptTcpClientAsync()   {       return Task.Factory.FromAsync(BeginAcceptTcpClient, EndAcceptTcpClient, null);   }
  AsyncTcpSocketServer 中的 Accept Loop 指的就是,   while (IsListening)   {       var tcpClient = await _listener.AcceptTcpClientAsync();   }
  每一个建立成功的 Connection 由 AsyncTcpSocketSession 来处理,所以 AsyncTcpSocketSession 中会包含 Read Loop,   while (State == TcpSocketConnectionState.Connected)   {       int receiveCount = await _stream.ReadAsync(_receiveBuffer, 0, _receiveBuffer.Length);   }
  为了将 async/await 异步到底,AsyncTcpSocketServer 所暴露的接口也同样是 Awaitable 的。
  public interface IAsyncTcpSocketServerMessageDispatcher   {       Task OnSessionStarted(AsyncTcpSocketSession session);       Task OnSessionDataReceived(AsyncTcpSocketSession session, byte[] data, int offset, int count);       Task OnSessionClosed(AsyncTcpSocketSession session);   }
  使用时仅需将一个实现了该接口的对象注入到 AsyncTcpSocketServer 的构造函数中即可。
  public class SimpleMessageDispatcher : IAsyncTcpSocketServerMessageDispatcher   {       public async Task OnSessionStarted(AsyncTcpSocketSession session)       {           Console.WriteLine(string.Format("TCP session {0} has connected {1}.", session.RemoteEndPoint, session));           await Task.CompletedTask;       }          public async Task OnSessionDataReceived(AsyncTcpSocketSession session, byte[] data, int offset, int count)       {           var text = Encoding.UTF8.GetString(data, offset, count);           Console.Write(string.Format("Client : {0} --> ", session.RemoteEndPoint));           Console.WriteLine(string.Format("{0}", text));              await session.SendAsync(Encoding.UTF8.GetBytes(text));       }          public async Task OnSessionClosed(AsyncTcpSocketSession session)       {           Console.WriteLine(string.Format("TCP session {0} has disconnected.", session));           await Task.CompletedTask;       }   }
  当然,对于接口的实现也不是强制了,也可以在构造函数中直接注入方法的实现。
  public AsyncTcpSocketServer(       IPEndPoint listenedEndPoint,       Func onSessionDataReceived = null,       Func onSessionStarted = null,       Func onSessionClosed = null,       AsyncTcpSocketServerConfiguration configuration = null)   {}
  SAEA 方式:TcpSocketSaeaServer
  SAEA 是 SocketAsyncEventArgs 的简写。SocketAsyncEventArgs 是 .NET Framework 3.5 开始支持的一种支持高性能 Socket 通信的实现。SocketAsyncEventArgs 相比于 APM 方式的主要优点可以描述如下:
  The main feature of these enhancements is the  avoidance of the repeated allocation and synchronization of objects  during high-volume asynchronous socket I/O. The Begin/End design pattern currently implemented by the Socket class for asynchronous socket I/O requires a System.IAsyncResult object be allocated for each asynchronous socket operation.
  也就是说,优点就是无需为每次调用都生成 IAsyncResult 等对象,向原生 Socket 更靠近一些。
  使用 SocketAsyncEventArgs 的推荐步骤如下: Allocate a new SocketAsyncEventArgs context object, or get a free one from an application pool. Set properties on the context object to the operation about to be performed (the callback delegate method and data buffer, for example). Call the appropriate socket method (xxxAsync) to initiate the asynchronous operation. If the asynchronous socket method (xxxAsync) returns true in the callback, query the context properties for completion status. If the asynchronous socket method (xxxAsync) returns false in the callback, the operation completed synchronously. The context properties may be queried for the operation result. Reuse the context for another operation, put it back in the pool, or discard it.
  重点在于池化(Pooling),池化的目的就是为了重用和减少运行时分配和垃圾回收的压力。
  TcpSocketSaeaServer 即是对 SocketAsyncEventArgs 的应用和封装,并实现了 Pooling 技术。TcpSocketSaeaServer 中的重点是 SaeaAwaitable 类,SaeaAwaitable 中内置了一个 SocketAsyncEventArgs,并通过 GetAwaiter 返回 SaeaAwaiter 来支持 async/await 操作。同时,通过 SaeaExtensions 扩展方法对来扩展 SocketAsyncEventArgs 的 Awaitable 实现。   public static SaeaAwaitable AcceptAsync(this Socket socket, SaeaAwaitable awaitable)   public static SaeaAwaitable ConnectAsync(this Socket socket, SaeaAwaitable awaitable)   public static SaeaAwaitable DisonnectAsync(this Socket socket, SaeaAwaitable awaitable)   public static SaeaAwaitable ReceiveAsync(this Socket socket, SaeaAwaitable awaitable)   public static SaeaAwaitable SendAsync(this Socket socket, SaeaAwaitable awaitable)
  SaeaPool 则是一个 QueuedObjectPool 的衍生实现,用于池化 SaeaAwaitable 实例。同时,为了减少 TcpSocketSaeaSession 的构建过程,也实现了 SessionPool 即 QueuedObjectPool。
  TcpSocketSaeaServer 中的 Accept Loop 指的就是,
  while (IsListening)   {       var saea = _acceptSaeaPool.Take();          var socketError = await _listener.AcceptAsync(saea);       if (socketError == SocketError.Success)       {           var acceptedSocket = saea.Saea.AcceptSocket;       }          _acceptSaeaPool.Return(saea);   }
  每一个建立成功的 Connection 由 TcpSocketSaeaSession 来处理,所以 TcpSocketSaeaSession 中会包含 Read Loop,
  var saea = _saeaPool.Take();   saea.Saea.SetBuffer(_receiveBuffer, 0, _receiveBuffer.Length);      while (State == TcpSocketConnectionState.Connected)   {       saea.Saea.SetBuffer(0, _receiveBuffer.Length);          var socketError = await _socket.ReceiveAsync(saea);       if (socketError != SocketError.Success)           break;          var receiveCount = saea.Saea.BytesTransferred;       if (receiveCount == 0)           break;   }
  同样,TcpSocketSaeaServer 对外所暴露的接口也同样是 Awaitable 的。
  public interface ITcpSocketSaeaServerMessageDispatcher   {       Task OnSessionStarted(TcpSocketSaeaSession session);       Task OnSessionDataReceived(TcpSocketSaeaSession session, byte[] data, int offset, int count);       Task OnSessionClosed(TcpSocketSaeaSession session);   }
  使用起来也是简单直接:
  public class SimpleMessageDispatcher : ITcpSocketSaeaServerMessageDispatcher   {       public async Task OnSessionStarted(TcpSocketSaeaSession session)       {           Console.WriteLine(string.Format("TCP session {0} has connected {1}.", session.RemoteEndPoint, session));           await Task.CompletedTask;       }          public async Task OnSessionDataReceived(TcpSocketSaeaSession session, byte[] data, int offset, int count)       {           var text = Encoding.UTF8.GetString(data, offset, count);           Console.Write(string.Format("Client : {0} --> ", session.RemoteEndPoint));           Console.WriteLine(string.Format("{0}", text));              await session.SendAsync(Encoding.UTF8.GetBytes(text));       }          public async Task OnSessionClosed(TcpSocketSaeaSession session)       {           Console.WriteLine(string.Format("TCP session {0} has disconnected.", session));           await Task.CompletedTask;       }   }
  RIO 方式:TcpSocketRioServer
  从 Windows 8.1 / Windows Server 2012 R2 开始,微软推出了 Registered I/O Networking Extensions 来支持高性能 Socket 服务的实现,简称 RIO。
  The following functions are supported for Windows Store apps on Windows 8.1, Windows Server 2012 R2, and later. Microsoft Visual Studio 2013 Update 3 or later is required for Windows Store apps. RIOCloseCompletionQueue RIOCreateCompletionQueue RIOCreateRequestQueue RIODequeueCompletion RIODeregisterBuffer RIONotify RIOReceive RIOReceiveEx RIORegisterBuffer RIOResizeCompletionQueue RIOResizeRequestQueue RIOSend RIOSendEx
  到目前为止,.NET Framework 还没有推出对 RIO 的支持,所以若想在 C# 中实现 RIO 则只能通过 P/Invoke 方式,RioSharp 是开源项目中的一个比较完整的实现。
  Cowboy.Sockets 直接引用了 RioSharp 的源代码,放置在  Cowboy.Sockets.Experimental  名空间下,以供实验和测试使用。
  同样,通过 TcpSocketRioServer 来实现 Accept Loop,
  _listener.OnAccepted = (acceptedSocket) => {     Task.Run(async () =>     {         await Process(acceptedSocket);     })     .Forget(); };
  通过 TcpSocketRioSession 来处理 Read Loop,   while (State == TcpSocketConnectionState.Connected)   {       int receiveCount = await _stream.ReadAsync(_receiveBuffer, 0, _receiveBuffer.Length);       if (receiveCount == 0)           break;   }
  测试代码一如既往的类似:
  public class SimpleMessageDispatcher : ITcpSocketRioServerMessageDispatcher   {       public async Task OnSessionStarted(TcpSocketRioSession session)       {           //Console.WriteLine(string.Format("TCP session {0} has connected {1}.", session.RemoteEndPoint, session));           Console.WriteLine(string.Format("TCP session has connected {0}.", session));           await Task.CompletedTask;       }          public async Task OnSessionDataReceived(TcpSocketRioSession session, byte[] data, int offset, int count)       {           var text = Encoding.UTF8.GetString(data, offset, count);           //Console.Write(string.Format("Client : {0} --> ", session.RemoteEndPoint));           Console.Write(string.Format("Client : --> "));           Console.WriteLine(string.Format("{0}", text));              await session.SendAsync(Encoding.UTF8.GetBytes(text));       }          public async Task OnSessionClosed(TcpSocketRioSession session)       {           Console.WriteLine(string.Format("TCP session {0} has disconnected.", session));           await Task.CompletedTask;       }   }

从口感上来讲,是南方的大米好吃还是东北的大米好吃?谢谢邀请。当然是东北大米好吃了,这还用问吗?全国各地都在卖五常大米,山东那边还有人用湖北大米冒充五常大米。许多年前,我就在深圳见过五常大米。至今,还没见过东北有人卖南方大米。为什么文化课成绩不好,想走艺术生(美术)怎么样?看到这个问题,我很想回答一下,因为我的孩子就是艺术生,今年刚参加完高考。从孩子开始学艺术到高考结束,这一年半的时间,可谓是一种煎熬。大家一直认为学艺术就是为了走高考捷径,其实未然。男朋友不愿意孩子跟我姓该怎么办?他不答应你就不生!就怀着!就不生!等他答应了你再生下来!那就离婚,再找一个和你同姓的人结婚,至于孩子姓什么,那就不是问题了。孩子和男人,以后你来养,就跟你信自古以来,男尊女卑,子随除了钢琴外,小朋友学什么乐器好?很多家长还在纠结让小朋友学习什么乐器,其实中国有很多名族乐器可以弘扬国民文化,传承课余爱好习惯,培养孩子从小情操,让自己家孩子比别人优秀,那么哪些是除了钢琴之外适合孩子学习的乐器呢想给女朋友挑选一部手机,价位在4000到5000元,有什么推荐?看到有人推荐华为Mate40,这款手机属实不错。可我想说这手机发布快两年了,再加上这手机的商务属性,貌似也不适合小姐姐使用呀!本来13香还不错,可预算只有40005000元,超得有第一次买苹果手机时,应该注意些什么?以下所有,想到啥就答啥第一,一定要注册属于你的AppleID并牢记密码!想要体验到苹果手机的优良性能及连带服务,注册ID是非常必要的。第二,一定要记得开启查找我的iPhone的功能我儿子上的陆军炮兵防空兵学院前途怎么样?我是岭南通途,我来回答!小孩上的陆军炮兵防空兵学院前途如何?炮兵防空兵学院可以说是我军炮兵和防空兵指挥干部的摇篮,也是我军炮兵最重要的培训基地,可以说绝大多数的炮兵指挥人员都是从这湖南城市学院和邵阳学院哪个好?邵阳学院实力略强于湖南城市学院,但湖南城市学院招生分数略高于邵阳学院。从一个过来人的角度来说为什么邵阳学院在实力上略强于湖南城市学院反而录取分数线比湖南城市学院低呢?我总结出以下几民办的本科学校有些出来之后的学历是不承认的,是吗?有的,有很多野鸡民办大学,未经政府批准,以虚假宣传骗取学生高额费用为目的,国家不予承认学历,同学们需擦亮眼睛,千万别上当受骗。民办的本科学校很少,大多数是国家不承认学历的野鸡大学。江西赣州有哪些高校?江西理工大学,赣南师范大学,赣南医学院,赣南科技学院,赣南师范大学科技学院,江西应用技术职业学院,江西环境工程职业学院,赣州师范高等专科学校,赣州职业技术学院,赣南卫生健康职业学院山西省最好的五所大学应该怎么挑选?山西的煤老板是真有钱,但山西政府真不怎么富有,最新公布的GDP排名,山西省只排在21位。经济不发达,导致山西现在的高等教育建设非常滞后,所以也没什么名校,和别的省份相比,缺一所名片
省市界阻不断襄宛荆兄弟情南阳荆门数万市民涌入襄阳走亲访友旅游元旦小长假期间,南阳荆门数万群众涌入襄阳探亲访友旅游!游客表示省市界阻不断襄宛荆兄弟城市之间的深情厚谊,让我们合力推进汉江流域南襄盆地一体化!市民感谢豫R鄂H助力襄阳打造汉江流域烟冬日,黄蒿活成了雕像这种草你认识吗?它叫黄蒿,今年我从夏天拍到了冬天。盛夏的黄蒿长得意气昂扬勃勃向上,是夏长植物的最好代表,把我每天去的万灵山装扮成了绿色原野,每天我走在长得半人高的绿色黄蒿中,让我如约起!株洲新添一家省级旅游休闲街区1月3日,湖南省文化和旅游厅发布公告,经检查和公示,株洲市钟鼓岭旅游休闲街区等10家单位达到省级旅游休闲街区标准,拟认定为省级旅游休闲街区。钟鼓岭步行街位于株洲芦淞区,地处被誉为三中国疾控中心专家释疑XBB毒株回应公众五大关切问题央视网消息近期,奥密克戎新的变异分支XBB。1。5毒株引发关注,这个毒株在一些国家成为优势毒株,并且已经在我国本土病例中被检出。那么,什么是XBB。1。5毒株?致病力如何?其引发的这10家定了省级旅游休闲街区,有你住的街区吗湖南日报新湖南客户端1月3日讯(记者刘涛)今天,湖南省文化和旅游厅在官网发布公告,正式确定长沙市雨花区圭塘河街等10家单位为省级旅游休闲街区。圭塘河畔。湖南日报全媒体记者徐行摄公告110个街区和78个路段获评浙江省级示范!有你去过的吗?近日,省建设厅公布2022年度浙江省高品质示范街区街容示范街名单。其中,杭州市上城区湖滨商业步行街区等110个街区为高品质示范街区,杭州市上城区平海路(湖滨路中河中路)等78个路段钟南山和张文宏当务之急,是赶紧挽回公众对专家的信任!自从放开后,阳的人越来越多了,公众对专家的信任也越来越低了。因为专家说过最有名的话是新的病毒就是大号感冒。我们当然很相信专家的话了。钟南山老人家曾说过同其他的病毒株比较,奥密克戎患请查收一份2023温馨跨年文案!Hi,这里是知知日记。01。hr今年的遗憾,明年的动力。02。hr忘掉旧的不安,拥有新的快乐。03。hr喜欢的都拥有,失去的都释怀。04。hr生活要温暖顺心,其他别无所求呀。05。官宣文案那些浪漫的跨年官宣文案头条创作挑战赛One11。我和你跨过分秒也想和你跨过余生岁月。2。和你,今年,明年,年年3。新的一年只想牵你的手,从天光乍破走到暮雪白头。4。从跨年开始,以后的我们会在一起看雪,会双旦氛围感穿搭模板,聂小雨百变造型总有你的菜2022年马上就要跟大家saygoodbye了,大家有没有觉得最近的社交场合也变多了,特别是年底圣诞节和元旦纷至沓来,各种大大小小的聚会少不了,无论是回家过节还是跟朋友出门聚餐或者事关货币政策房地产平台经济!央行这场会议带来重要信息作为下一阶段货币政策施策的风向标,中国人民银行货币政策委员会日前召开2022年第四季度例会提出,要坚持稳字当头稳中求进,强化跨周期和逆周期调节,加大稳健货币政策实施力度,要精准有力