Home

Awesome

Standard Network Library

High Performance, easy to use network library supporting 16k+ concurrent clients on all provided topologies. </br>Designed for distributed realtime concurrent applications over LAN or Internet. </br>Provides infrastructure for high throughput message passing, P2P, Nat Traversal, Reliable Udp. </br>This repository consist of main core assembly and several serialization spesific sub assemblies.

Please check out Wiki page for detailed documentation.

Core Network Library

Network Library, Includes all logic associated with the network systems, starting from raw bytes to abstractions such as P2P lobbies. It provides generic templates to be used with any type of serialization.

Low Level

Plug&Play high performance models, working with raw bytes. Also used as base for higher level models.

High Level

Involves generic models which can work with any serialization protocol.

Internal Features

Message Buffering/Queueing

Advanced Memory Management

Build In Serialization

Library is tested with as many clients as OS(Windows) supports (around 16k dynamic ports). Data reliability includung RUDP is tested over the internet extensively. Nat Traversal Udp holepunching is also tested over the internet with success.

Note: Libary has unsafe code and stack memory allocations. Unsafe sections are well tested and not subject to change.

Sub Assemblies

Generic models from main assembly are implemented with the spesific serializer.Reason for this division is to avoid unnecessary dependencies. All method signatures and usage are identical. It includes:

Supported Runtimes

Nuget Packages are available:

Core Network LibraryProtobufMessagePackNetSeralizerJson
NuGetNuGetNuGetNuGetNuGet

Benchmarks

Infinite Echo benchmarks are done by sending set of messages to server and getting echo response. Each response causes new request. Each server response is counted as 1 Echo.

TCP/SSL Byte Message Server

1000 seed messages (32 bytes message + 4 header) each:

AMD Ryzen 7 5800H Laptop

Mumber Of ClientsTCP Echo per SecondSSL Echo per Second
10053,400,00041,600,000
100043,600,00022,200,000
500043,400,00021,800,000
1000042,800,00021,700,000

Intel i9 13980HX Laptop

Mumber Of ClientsTCP Echo per SecondSSL Echo per Second
100128,800,00079,400,000
1000110,400,00072,100,000
5000102,100,00067,500,000
10000100,500,00065,500,000

MessageProtocol

1000 seed message envelopes ( 32 byte payload, 48 byte total):

AMD Ryzen 7 5800H Laptop

Mumber Of ClientsProtobuf Echo per SecondSecure Protobuf Echo per Second
1009,440,0008,050,000
10008,780,0007,480,000
50008,360,0007,390,000
100008,340,0007,350,000

Intel i9 13980HX Laptop

Mumber Of ClientsProtobuf Echo per SecondSecure Protobuf Echo per Second
10031,200,00020,650,000
100030,500,00019,500,000
500028,200,00017,650,000
1000026,400,00016,000,000

Note

This benchmarks is only sending message envelope with raw byte payload. For serialization spesific performance please refer to: SerializationBenchmarks

Quick Documentation & Code Samples

Byte Message TCP Server/Client

For Detailed info Check out AsyncTcpClient/Server and ByteMessageTcpClient/Server

Any chunk of byte array or array segment will reach the destination without fragmentation.

        private static void ExampleByteMessage()
        {
             ByteMessageTcpServer server = new ByteMessageTcpServer(20008);
            server.OnBytesReceived += ServerBytesReceived;
            server.StartServer();

            ByteMessageTcpClient client = new ByteMessageTcpClient();
            client.OnBytesReceived += ClientBytesReceived;
            client.Connect("127.0.0.1", 20008);

            client.SendAsync(Encoding.UTF8.GetBytes("Hello I'm a client!"));

            void ServerBytesReceived(Guid clientId, byte[] bytes, int offset, int count)
            {
                Console.WriteLine(Encoding.UTF8.GetString(bytes, offset, count));
                server.SendBytesToClient(clientId, Encoding.UTF8.GetBytes("Hello I'm the server"));
            }

            void ClientBytesReceived(byte[] bytes, int offset, int count)
            {
                Console.WriteLine(Encoding.UTF8.GetString(bytes, offset, count));
            }
        }
output:
Hello I'm a client!
Hello I'm the server

Note: Important performance setting here is whether to use a buffer or a queue as buffering policy. Use Buffer if messages are mainly regions of byte[] such as (buffer, offset,count). Use Queue if the messages are full byte[] (0 to end).

    client.GatherConfig = ScatterGatherConfig.UseQueue;
    server.GatherConfig = ScatterGatherConfig.UseBuffer;

</br>For SSL variants difference is:

   var ccert = new X509Certificate2("client.pfx", "greenpass");
   // null certificate or default constructor will generate self signed certificate
   client = new SslByteMessageClient(ccert);
 
   var scert = new X509Certificate2("server.pfx", "greenpass");
   // null certificate or default constructor will generate self signed certificate
   server = new SslByteMessageServer(8888,scert);
   
   // You can override the SSL cerificate validation callback
   server.RemoteCertificateValidationCallback+= ...
   client.RemoteCertificateValidationCallback+= ...

For more info check out SSLClient/Server And SSLByteMessageClient/Server

Base Server/Client where raw bytes are transfered. Method and callback signarures are identical to byte message models. There is no protocol implemented over base Server/Client, hence bytes may come fragmented depending on your MTU size.

   AsyncTcpServer server = new AsyncTcpServer(port: 20000);
   AsyncTpcClient client = new AsyncTpcClient();
   
   // SSL variant
   // null certificate or default constructor will generate self signed certificate
   var ccert = new X509Certificate2("client.pfx", "greenpass");
   var scert = new X509Certificate2("server.pfx", "greenpass");
   SslServer server = new SslServer(2000, scert);
   SslClient client = new SslClient(ccert);

Serialized Networks

Serialized Networks are implementations of generic classes provided by Core Library It is applicable to all serialization protocols.

For more info : Serialised Network </br>Examples here is only given for Protobuf-net, but signature is identical for any other provided serialization protocol(MessagePack, Json etc).

Protobuf Example

Implements a server client model where serialized messages are transfered atomically. Declare your type:

    [ProtoContract]
    class SampleMessage
    {
        [ProtoMember(1)]
        public string sample;
    }
    PureProtoServer server = new PureProtoServer(11234);
    server.StartServer();

    server.BytesReceived += (clientId,bytes, offset, count) => 
    {
        SampleMessage msg = server.Serializer.Deserialize<SampleMessage>(bytes, offset, count);
        Console.WriteLine(msg.sample);
        msg.sample = "Jesse Lets cook";
        server.SendAsync(clientId,msg);
    };

    PureProtoClient client = new PureProtoClient();
    client.Connect("127.0.0.1", 11234);

    client.BytesReceived += (bytes, offset, count) =>
    {
        SampleMessage msg = client.Serializer.Deserialize<SampleMessage>(bytes, offset, count);
        Console.WriteLine(msg.sample);
    };

    client.SendAsync(new SampleMessage() { sample = "Yo! Mr White" });

MessageProtocol Server/Client

Message protocol is implemented for wrapping all dynamic message types with a standard header. Please refer to Message Protocol for detailed explanation.

You can declare your payload types, which any type that is serializable with protobuf.

        [ProtoContract]
        class SamplePayload :IProtoMessage
        {
            [ProtoMember(1)]
            public string sample;
        }

Example for the Secure Variant:

  private static async Task ExampleProtoSecure()
        {
            // null certificate or default constructor will generate self signed certificate
            var scert = new X509Certificate2("server.pfx", "greenpass");
            var cert = new X509Certificate2("client.pfx", "greenpass");

            SecureProtoMessageServer server = new SecureProtoMessageServer(20008, scert);
            server.StartServer();
            server.OnMessageReceived += ServerMessageReceived;

            var client = new SecureProtoMessageClient(cert);
            client.OnMessageReceived += ClientMessageReceived;
            client.Connect("127.0.0.1", 20008);

            var Payload = new SamplePayload() { sample = "Hello" };
            var messageEnvelope = new MessageEnvelope();
            messageEnvelope.Header = "PayloadTest";

            // You can just send a message, get replies on ClientMessageReceived.
            client.SendAsyncMessage(messageEnvelope);
            client.SendAsyncMessage(messageEnvelope, Payload);

            // Or you can wait for a reply async.
            MessageEnvelope result = await client.SendMessageAndWaitResponse(messageEnvelope, Payload);
            var payload = result.UnpackPayload<SamplePayload>();
            Console.WriteLine($"Client Got Response {payload.sample}");

            void ServerMessageReceived(Guid clientId, MessageEnvelope message)
            {
                Console.WriteLine($"Server Received message {message.Header}");
                server.SendAsyncMessage(clientId, message);
            }

            void ClientMessageReceived(MessageEnvelope message)
            {
            }
        }

Relay Server/Client and P2P

This model is what I personally use on my other projects such as P2PVideocall and Multiplayer Starfighter Game. Basically you have a Relay server somewhere in your network, which can act as a local network hub in LAN and/or open to connections from internet if port forwarding is enabled. <br/>Relay clients (Peers) connect to Relay server and get notifications about existince of other peers. Peers can send messages to each other through Relay Server, or directly to each other (Udp holepunch).

Check out P2P Fore detailed info

Relay server

Server is completely passive, allowing other peers to discover and send messages to each other. Additionally NAT traversal methods such as UDP holepunching provided to allow direct communication via Internet or LAN (UDP only so far, but we have reliable udp).

Relay Server Is Serialization Agnostic which means any serialized network Peers, (Protobuff, MessagePack etc) can use the same relay server. <br/> To use the Relay server, simply declere your server as:

      var scert = new X509Certificate2("server.pfx", "greenpass");
      var server = new SecureProtoRelayServer(20010, scert);
      server.StartServer();

Relay server is already pre-configured.

Relay Client

Relay client is where your application logic is implemented. You can web your client applications to discover and talk with each other. </br>To declere a client:

     // null certificate or default constructor will generate self signed certificate
      var cert = new X509Certificate2("client.pfx", "greenpass");
      var client = new RelayClient(cert);

      client.OnPeerRegistered += (Guid peerId) => ..
      client.OnPeerUnregistered += (Guid peerId) => ..
      client.OnMessageReceived += (MessageEnvelope message) => .. 
      client.OnUdpMessageReceived += (MessageEnvelope message) => ..
      client.OnDisconnected += () => ..

      client.Connect("127.0.0.1", 20010);

Method signatures and callbacks are identical to proto client/server model (also with Payloads). Only difference is you have to specify the destination peer Guid Id. It comes from OnPeerRegistered event, whenever a new peer is connected to relay server. Relay Server guaranties syncronisation of current peer set with eventual consistency among all peers. So new peers will receive all other connected peers from this event and old peers will receive an update.

      client.SendAsyncMessage(destinationPeerId, new MessageEnvelope() { Header = "Hello" });
      client.SendUdpMesssage(destinationPeerId, new MessageEnvelope() { Header = "Hello" });
      // Or with an async reply
      MessageEnvelope response = await client.SendRequestAndWaitResponse(destinationPeerId,
                                            new MessageEnvelope() { Header = "Who Are You?" });

Udp messages can be more than the datagram limit of 65,527 bytes. The system detects large udp messages as Jumbo messages and sends them in chunks. Receiving end with will try to reconstruct the message. if all the parts does not arrive within a timeout message is dropped. Max message size for udp is 16,256,000 bytes.

Reliable udp protocol uses as TCP algorithm implemented over UDP.

      client.SendRudpMessage(peerId, envelope);
      client.SendRudpMessage(peerId, envelope, innerMessage);
      client.SendRudpMessageAndWaitResponse(peerId, envelope, innerMessage);
      client.SendRudpMessageAndWaitResponse(peerId, envelope);

Nat Traversal/Holepunch Support:

      // Udp
      bool result = await client.RequestHolePunchAsync(destinationPeerId, timeOut:10000);
      // Tcp
      bool result = await client.RequestTcpHolePunchAsync(destinationPeerId, timeOut:10000);

if succesfull, it will allow you to send direct udp messages between current and destination peers for the rest of the udp messages in both directions.

Room/Lobby Server

This is an extention of Relay Server/Client. The addition is the room system where peers can create or join rooms, query available rooms, sends message to rooms(multicast). Additionally keeping same message system to send 1-1 messages among peers.

You can join multiple rooms

Room Server Is Serialization Agnostic which means any serialized network Peers, (Protobuf, MessagePack etc) can use the same Room server.

Decleration of Server and client

     var server = new SecureProtoRoomServer(20010, scert);
     server.StartServer();

     var client1 = new SecureProtoRoomClient(cert);

To create/join and leave rooms simply:

     client1.CreateOrJoinRoom("Kitchen");
     client1.LeaveRoom("Kitchen");

Room callbacks are as followed. This callbacks are only triggered if you are in the same room.

    client1.OnPeerJoinedRoom += (roomName, peerId) =>..
    client1.OnPeerLeftRoom += (roomName, peerId) =>..
    client1.OnPeerDisconnected +=(peerId) =>..

Additionally to the standard 1-1 message callback we have room message callbacks.

    client1.OnTcpRoomMesssageReceived += (roomName, message) => ..
    client1.OnUdpRoomMesssageReceived += (roomName, message) => ..
    client1.OnTcpMessageReceived += (message) => ..
    client1.OnUdpMessageReceived += (message) => ..

P2P Remarks

Cyber Security

All secure TCP variants are implementing standard SSL socket with TLS authentication/validation. For more info check out : Security