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

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;       }   }

电视盒子哪个好?数码体验师公布五大畅销网络电视盒子推荐5G时代来临,科技发展真的很快,智能电视机已经普及到家家户户。不过,现在的智能电视机远没有我们小时候的质量来得好,用不了几年就各种卡顿,死机了。其实,这种情况往往是系统优化不到位,金米米参与虎牙CF周年庆典活动,站撸开枪都能拿人头?观众看呆了随着CF这款游戏,迎来14周年的庆典,虎牙直播也为大家准备了各种各样的庆典活动,让大家可以一同感受CF十四周年的快乐。各位CF玩家们,陪伴了这款游戏14年的时间,也是时候该放松一下苹果手机回收如何选择平台,正规平台更放心更高价今年9月,苹果公司将推出最新的iPhone14系列手机,除了最新的A16芯片处理器之外,还会搭载全新iOS16系统。据说售价也和iPhone13系列差不多,参考iPhone13今年我用天邑AX3000路由器,解决了网络差问题,填上没有预埋网线的坑装修的坑有很多,即使装修前做足功课,也难免会掉进坑里。去年,潮范君家装修,各种查资料做攻略,结果住进新家后,发现还是考虑不周,掉进了家庭网络环境差的大坑。在网络布线时,忘记预留书房最低449元全网最具性价比的32寸电视文张显秋责编吕东兴总编唐迪449元就能买电视,小编以为自己看错了,经过仔细计算,小编发现这款雷鸟电视到手价确实是449元,比显示器都便宜,性价比超级高啊!应该是全网最具性价比的显示月场均4498,这是巅峰哈登!四支球队6000万空间,下赛季精彩了火箭官方现在基本稳定在每日一更的节奏中,有时候甚至会两更,但大部分都是球员的训练动态,没什么劲爆新闻。今日,火箭官方继续更新动态,继续晒出球员们训练照,包括华盛顿格林马修斯史密斯托6000万薪金空间,火箭等4队明年领衔自由市场?近日,ESPN专家BobbyMarks撰写长文分析了2023年夏天自由市场,其中分析了下赛季总薪资低于奢侈税线同时具备薪金空间的球队。根据Marks的分析,下赛季共有13支球队有薪留给蔚小理的时间不多了,极氪或成新能源市场的宠儿随着时间进入2022年下半年,新能源汽车的角逐日趋明晰化,竞争格局也已在悄然发生着改变。从各车企刚刚发布的7月份交付数据来看,蔚小理7月交付量均环比下滑20左右。值得一提的是,7月2022年最热销的3款智能手表,颜值功能二合一智能手表可以说是智能手机的精华版。不仅有智能手机的各种智能程序,轻巧的质量,让佩戴起来更加方便舒适。智能手表佩戴在手上不仅时尚大方,还可以随时监测我们的健康数据。智能手表的优势非常为何人一老,各种病都找来了?人过50,不妨养成3个懒习惯生活中,很多老年人都会有这样的感觉人一老,这样那样的病就找来了,甚至一起找来。疾病缠身,不仅影响健康与寿命,时间长了,还会影响心理健康,大大降低生活质量。为什么人老了就容易生病?很小众又低调的德国手表?德表腕表选购小众手表个性轻奢低调手表我们一直都说小众品牌,但关于小众的定义一直没有确切的说法,按照维基百科的定义由独立公司或者制表师制造的,每年产量3002000pcs称为小众腕表。瑞士是手表制造的天堂,德国也有很多
用这种方式对你的人,无论是谁,都要绝交图源自网络侵权请联系删除我们在生活中会遇到各种各样的人,因为每个人性格的不同,相处的方式也会有所不同。有些人大大咧咧,可能在一些细节上不太注意伤害了你,但是他对你却是一片赤诚,没有它们的秘密,被发现了星空有约这两颗彗星的秘密被发现了!记者从中国科学院紫金山天文台获悉,该台研究人员以我国最早发现的两颗彗星为研究对象,揭示出彗星表面的喷发活动受到彗星成分与太阳距离彗核大小等多种因素这一路我们到底寻找到了什么?寻路东坡手记封面新闻记者张杰作为在中原长大的人,每每想起苏轼和他亲爱的弟弟苏辙一起埋骨河南平顶山郏县,都与有荣焉。外出求学假期返乡的高铁途中,多次经过平顶山,会有意识沿着广阔的华北平原往外极目又一个免门票的古村落,他说生我的是溪口,养我的是岩头同名岩头村,国内何其多也。有名的岩头古村,浙江就有两个。奉化的岩头村,永嘉的岩头村。笔者精力有限,只能一个个来。宁波奉化岩头村,蒋经国的外婆村。一个盛产民国将军的古村,号称浙江第一最新社保基金持仓曝光新进增持10家公司这些股票被连续五个季度持有本报记者任世碧随着上市公司年报的陆续披露,社保基金的持仓动向也逐渐明晰。Wind数据显示,截至3月15日收盘,在已披露2022年年报的140家A股上市公司中,有16家公司前十大流通2023蓝牙耳机选购指南什么蓝牙耳机性价比高?蓝牙耳机哪款好?自苹果取消3。5mm耳机孔开始,蓝牙耳机便逐渐取代有线耳机以强势的姿态闯入人们的日常生活当中。伴随着越来越多的蓝牙耳机涌入,人们在选择时不免有种眼花缭乱的错觉,不知道蓝牙耳机应该怎变形枪真的毁了使命召唤OL吗?看看当时的情况就知道答案了现如今提到使命召唤,可能很多玩家早已忘记了这个使命召唤曾经有个国区特供版的使命召唤,那便是我们熟悉的使命召唤OL了,这款游戏在当年,的确是很多国内COD老玩家的真爱,可是,现实却很flutter初体验demo踩坑指南NewFlutterProject创建项目不成功报错提示Fluttercreatecommandwasunsuccessful,然后打开项目文件里面就一个。idea文件夹,项目压根灯杆定位智慧地图,顾村公园客流高峰时段实现一分钟出警处置2023上海樱花节已于昨日开启,随着这两天顾村公园游客数量激增,突发的意外情况偶有发生。游客娄女士在游园时,将自己装着现金卡片的包遗失在公用厕所,而返回寻找时已经找不到了,于是她赶来嵊泗列岛,围观海洋牧场,感受海岛风情!记住乡愁乡村振兴篇播出内容嵊泗同舟共济扬帆起播出时间2023年3月15日(周三)2000播出频道CCTV4中文国际频道万船云集嵊州洋位于舟山群岛北部的嵊泗列岛,四处皆景,风光宜人。花景大道上线,泰安这4处公园藏着春天粉白交织的美好!记者霍嘉星徐慧赏心悦目的春日来临,泰安街头粉白交织。3月13日14日,泰安杏花毛樱桃花山桃花盛放。泰山广场龙泽湖公园泰山广场龙泽湖公园府东体育公园泮河公园里粉花似霞白花似雪,将泰安