Multiple implementations of C # High performance TCP Services

Source: Internet
Author: User
Tags apm
The main thrust of this article is to use the. net/c# different ways to implement TCP high performance services, including but not limited to the following:




    • APM mode, i.e. asynchronous programming Model

    • TAP mode, i.e. task-based Asynchronous Pattern

    • Saea Way, namely SocketAsyncEventArgs

    • RIO mode, i.e. registered I/O


The support for sockets in. Net/c# is based on the encapsulation of the Windows I/O completion Ports completion port technology, which satisfies different programming requirements through different non-blocking encapsulation architectures. All of these methods have been fully implemented in Cowboy.sockets, and APM and TAP methods have been applied in the actual project. Cowboy.sockets is still evolving and perfecting, if you have any questions please correct me in time.



Although there are so many ways to do this, the abstract looks like they are the same, with two loops to describe:Accept loop and Read loop, as shown in. (the "loop" mentioned here refers to a loop , not a keyword such as while/for.) )





    • In any TCP Server implementation, there must be an accept Socket Loop that receives a CONNECT request from the client to establish a TCP Connection.

    • In any TCP Server implementation, there must be a Read Socket Loop that receives the client Write data.


If the Accept loop is blocked, it will result in the inability to quickly establish a connection, and the service-side Pending Backlog, causing the client side to receive a connect Timeout exception. If the Read loop is blocked, it will obviously lead to the inability to receive data from the client side in time, resulting in a client-side send Buffer full and no longer able to send data.



From the implementation details point of view, the location that can cause service blocking may be:


    1. Accept to the new Socket, build a new Connection need to allocate various resources, allocation of resources is slow;

    2. Accept to the new Socket, did not trigger the next time to accept;

    3. Read to the new Buffer, determine the length of the Payload message, the decision process is long;

    4. Read to the new buffer, found that the Payload has not been fully received, continue to Read, "may" cause a Buffer Copy;

    5. Payload received, the de-serialization turned into a recognizable Protocol Message, deserialization slow;

    6. The business Module handles the corresponding Protocol Message, and the processing process is slow;


1-2 involves the process of establishing the Accept process and the Connection, 3-4 involves the processing of receivebuffer, and 5-6 involves the implementation of the application logic side.



The famous Netty network library in Java, starting with version 4.0, has made a fresh attempt at the buffer section, using a design called BYTEBUF, which implements buffer Zero copy to reduce the performance loss and GC pressure caused by the buffer copy under high concurrency conditions. Dotnetty,orleans, Helios and other projects are trying to implement similar BYTEBUF implementations in C #.



APM mode: Tcpsocketserver



The implementation of Tcpsocketserver is based on the further encapsulation of the. NET Framework's own TcpListener and TcpClient, implemented using APM-based BeginXXX and ENDXXX interfaces.



The Accept Loop in tcpsocketserver refers to the


    • BeginAccept, endaccept->, BeginAccept, endaccept, beginaccept ...


Each successful Connection is handled by tcpsocketsession, so the tcpsocketsession contains a Read Loop,


    • BeginRead, EndRead, BeginRead, EndRead, BeginRead ...


Tcpsocketserver enables the establishment and disconnection of Connection and the notification of data reception by exposing the Event.

event EventHandler<TcpClientConnectedEventArgs> ClientConnected;  
event EventHandler<TcpClientDisconnectedEventArgs> ClientDisconnected;  
event EventHandler<TcpClientDataReceivedEventArgs> ClientDataReceived;



Use is also straightforward, direct subscription to event notifications.


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 mode: Asynctcpsocketserver



The implementation of Asynctcpsocketserver is based on the further encapsulation of the. NET Framework's TcpListener and TcpClient, using the XXXAsync interface of the TAP-based async/await.



However, in fact XXXAsync does not create any magical effect, its internal implementation simply transforms the APM method into a TAP invocation.








//************* Task-based async public methods *************************

[HostProtection(ExternalThreading = true)]  public Task<Socket> AcceptSocketAsync()

{      return Task<Socket>.Factory.FromAsync(BeginAcceptSocket, EndAcceptSocket, null);

}

 

[HostProtection(ExternalThreading = true)]  public Task<TcpClient> AcceptTcpClientAsync()

{      return Task<TcpClient>.Factory.FromAsync(BeginAcceptTcpClient, EndAcceptTcpClient, null);

}


The Accept Loop in asynctcpsocketserver refers to the


while (IsListening)

{      var tcpClient = await _listener.AcceptTcpClientAsync();

}



each of the successful Connection was created by Async tcpsocketsession to deal with, so Async The tcpsocketsession will contain the Read Loop,


while (State == TcpSocketConnectionState.Connected)

{      int receiveCount = await _stream.ReadAsync(_receiveBuffer, 0, _receiveBuffer.Length);

}



In order to async/await asynchronous to the end, Asynctcpsocketserver exposed interface is also awaitable.


public interface IAsyncTcpSocketServerMessageDispatcher

{

    Task OnSessionStarted(AsyncTcpSocketSession session);

    Task OnSessionDataReceived(AsyncTcpSocketSession session, byte[] data, int offset, int count);

    Task OnSessionClosed(AsyncTcpSocketSession session);

}



Only one object that implements the interface can be injected into the Asynctcpsocketserver constructor when it is used.


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;

    }

}



Of course, the implementation of the interface is not mandatory, you can also directly inject the implementation of the method in the constructor.


public AsyncTcpSocketServer(

    IPEndPoint listenedEndPoint,

    Func<AsyncTcpSocketSession, byte[], int, int, Task> onSessionDataReceived = null,

    Func<AsyncTcpSocketSession, Task> onSessionStarted = null,

    Func<AsyncTcpSocketSession, Task> onSessionClosed = null,

    AsyncTcpSocketServerConfiguration configuration = null)

{}



Saea Way: Tcpsocketsaeaserver



Saea is a shorthand for SocketAsyncEventArgs. SocketAsyncEventArgs is a support for the implementation of high-performance Socket communication that is supported by the. NET Framework 3.5. The main advantages of SocketAsyncEventArgs compared to the APM approach can be described as follows:


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.iasyn Cresult object is allocated for each asynchronous socket operation.


That is, the advantage is that you do not have to generate objects such as IAsyncResult for each call, but closer to the native Socket.



The recommended steps for using SocketAsyncEventArgs are as follows:


    1. Allocate a new SocketAsyncEventArgs context object, or get a free one from the application pool.

    2. Set properties on the context object to the operation is performed (the callback delegate method and data buffer, For example).

    3. Call the appropriate socket method (XxxAsync) To initiate the asynchronous operation.

    4. If the asynchronous socket Method (XxxAsync) returns true in the callback, query the context properties for completion STA Tus.

    5. If the asynchronous socket Method (XxxAsync) returns false in the callback, the operation completed synchronously. The context properties is queried for the operation result.

    6. Reuse the context for another operation, put it back in the pool, or discard it.


The focus is on pooling (Pooling), the purpose of pooling is to reuse and reduce the pressure of runtime allocation and garbage collection.



Tcpsocketsaeaserver is the application and encapsulation of SocketAsyncEventArgs, and implements the Pooling technology. The focus in Tcpsocketsaeaserver is the Saeaawaitable class, where a SocketAsyncEventArgs is built into the saeaawaitable and supported by Getawaiter return SaeaAwaiter Async/await operation. At the same time, the Saeaextensions extension method is used to extend the awaitable implementation of SocketAsyncEventArgs.





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 is a queuedobjectpool<saeaawaitable> derived implementation for pooling saeaawaitable instances. At the same time, in order to reduce the tcpsocketsaeasession construction process, it also realizes the Sessionpool namely queuedobjectpool<tcpsocketsaeasession>.



The Accept Loop in tcpsocketsaeaserver refers to the







while (IsListening)

{      var saea = _acceptSaeaPool.Take();  

    var socketError = await _listener.AcceptAsync(saea);      if (socketError == SocketError.Success)

    {          var acceptedSocket = saea.Saea.AcceptSocket;

    }

 

    _acceptSaeaPool.Return(saea);

}


Each successful Connection is made up of a Tcpsocketsaeasession , so the tcpsocketsaeasession will contain the 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;

}



Similarly, the interfaces exposed by Tcpsocketsaeaserver are also awaitable.


public interface ITcpSocketSaeaServerMessageDispatcher

{

    Task OnSessionStarted(TcpSocketSaeaSession session);

    Task OnSessionDataReceived(TcpSocketSaeaSession session, byte[] data, int offset, int count);

    Task OnSessionClosed(TcpSocketSaeaSession session);

}



It is also simple and straightforward to use:


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 Way: Tcpsocketrioserver



Starting with Windows 8.1/windows Server R2, Microsoft introduced the registered I/O Networking Extensions to support the implementation of the high-performance Socket service, referred to as RIO.



The following functions is supported for Windows Store apps on Windows 8.1, Windows Server, R2, and later. Microsoft Visual Studio Update 3 or later is required for Windows Store apps.


    • Rioclosecompletionqueue

    • Riocreatecompletionqueue

    • Riocreaterequestqueue

    • Riodequeuecompletion

    • Rioderegisterbuffer

    • Rionotify

    • Rioreceive

    • Rioreceiveex

    • Rioregisterbuffer

    • Rioresizecompletionqueue

    • Rioresizerequestqueue

    • Riosend

    • Riosendex


So far, the. NET Framework has not yet launched support for Rio, so if you want to implement Rio in C # only by P/invoke, Riosharp is a relatively complete implementation of the open source project.



Cowboy.sockets directly refers to the source code of the Riosharp, placed under the Cowboy.Sockets.Experimental name space for experimentation and test use.



Similarly, by using Tcpsocketrioserver to implement the Accept Loop,


_listener.OnAccepted = (acceptedSocket) =>{

    Task.Run(async () =>

    {        await Process(acceptedSocket);

    })

    .Forget();

};



To process the Read Loop through Tcpsocketriosession,








while (State == TcpSocketConnectionState.Connected)

{      int receiveCount = await _stream.ReadAsync(_receiveBuffer, 0, _receiveBuffer.Length);      if (receiveCount == 0)          break;

}

The test code is similar as always:


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;

    }

}


Resources


    • Asynchronous Programming Model (APM)

    • task-based asynchronous Pattern (TAP)

    • event-based asynchronous Pattern (EAP)

    • SocketAsyncEventArgs

    • Registered I/O

    • Netty:reference counted objects

    • Socket performance Enhancements in Version 3.5

    • What's New for Windows Sockets for Windows 8.1 and Windows Server R2

    • Rio_extension_function_table structure

    • Windows 8 Registered I/O Networking Extensions


This article "C # High-performance TCP services in many ways" by Dennis Gao published from the blog Garden personal blog, without the author's consent to prohibit in any form reproduced, any automatic or artificial crawler reproduction behavior are bullying.


Alibaba Cloud Hot Products

Elastic Compute Service (ECS) Dedicated Host (DDH) ApsaraDB RDS for MySQL (RDS) ApsaraDB for PolarDB(PolarDB) AnalyticDB for PostgreSQL (ADB for PG)
AnalyticDB for MySQL(ADB for MySQL) Data Transmission Service (DTS) Server Load Balancer (SLB) Global Accelerator (GA) Cloud Enterprise Network (CEN)
Object Storage Service (OSS) Content Delivery Network (CDN) Short Message Service (SMS) Container Service for Kubernetes (ACK) Data Lake Analytics (DLA)

ApsaraDB for Redis (Redis)

ApsaraDB for MongoDB (MongoDB) NAT Gateway VPN Gateway Cloud Firewall
Anti-DDoS Web Application Firewall (WAF) Log Service DataWorks MaxCompute
Elastic MapReduce (EMR) Elasticsearch

Alibaba Cloud Free Trail

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.