{"id":20585725,"url":"https://github.com/chronoxor/csharpserver","last_synced_at":"2025-04-14T21:10:13.870Z","repository":{"id":44156671,"uuid":"144031259","full_name":"chronoxor/CSharpServer","owner":"chronoxor","description":"Ultra fast and low latency asynchronous socket server \u0026 client C# library with support TCP, SSL, UDP protocols and 10K connections problem solution","archived":false,"fork":false,"pushed_at":"2024-03-09T22:48:50.000Z","size":1095,"stargazers_count":123,"open_issues_count":8,"forks_count":34,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-14T21:10:00.006Z","etag":null,"topics":["async","low-latency","performance","tcp-client","tcp-server","udp-client","udp-server"],"latest_commit_sha":null,"homepage":"https://chronoxor.github.io/CSharpServer","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chronoxor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2018-08-08T15:11:53.000Z","updated_at":"2025-04-06T09:35:33.000Z","dependencies_parsed_at":"2024-03-09T13:36:38.383Z","dependency_job_id":null,"html_url":"https://github.com/chronoxor/CSharpServer","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chronoxor%2FCSharpServer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chronoxor%2FCSharpServer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chronoxor%2FCSharpServer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chronoxor%2FCSharpServer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chronoxor","download_url":"https://codeload.github.com/chronoxor/CSharpServer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248961237,"owners_count":21189993,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","low-latency","performance","tcp-client","tcp-server","udp-client","udp-server"],"created_at":"2024-11-16T07:09:10.794Z","updated_at":"2025-04-14T21:10:13.802Z","avatar_url":"https://github.com/chronoxor.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CSharpServer\n\n[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n[![Release](https://img.shields.io/github/release/chronoxor/CSharpServer.svg?sort=semver)](https://github.com/chronoxor/CSharpServer/releases)\n[![Windows (Visual Studio)](https://github.com/chronoxor/CSharpServer/actions/workflows/build-windows-vs.yml/badge.svg)](https://github.com/chronoxor/CSharpServer/actions/workflows/build-windows-vs.yml)\n\n**This project is not supported anymore! Please considering migrating to [NetCoreServer](https://github.com/chronoxor/NetCoreServer)**\n\nUltra fast and low latency asynchronous socket server \u0026 client C# library with\nsupport TCP, SSL, UDP protocols and [10K connections problem](https://en.wikipedia.org/wiki/C10k_problem)\nsolution.\n\n[CSharpServer documentation](https://chronoxor.github.io/CSharpServer)\u003cbr/\u003e\n[CSharpServer downloads](https://github.com/chronoxor/CSharpServer/releases)\u003cbr/\u003e\n\n# Contents\n  * [Features](#features)\n  * [Requirements](#requirements)\n  * [How to build?](#how-to-build)\n  * [Examples](#examples)\n    * [Example: Asio timer](#example-asio-timer)\n    * [Example: TCP chat server](#example-tcp-chat-server)\n    * [Example: TCP chat client](#example-tcp-chat-client)\n    * [Example: SSL chat server](#example-ssl-chat-server)\n    * [Example: SSL chat client](#example-ssl-chat-client)\n    * [Example: UDP echo server](#example-udp-echo-server)\n    * [Example: UDP echo client](#example-udp-echo-client)\n    * [Example: UDP multicast server](#example-udp-multicast-server)\n    * [Example: UDP multicast client](#example-udp-multicast-client)\n  * [Performance](#performance)\n    * [Benchmark: Round-Trip](#benchmark-round-trip)\n      * [TCP echo server](#tcp-echo-server)\n      * [SSL echo server](#ssl-echo-server)\n      * [UDP echo server](#udp-echo-server)\n    * [Benchmark: Multicast](#benchmark-multicast)\n      * [TCP multicast server](#tcp-multicast-server)\n      * [SSL multicast server](#ssl-multicast-server)\n      * [UDP multicast server](#udp-multicast-server)\n  * [OpenSSL certificates](#openssl-certificates)\n    * [Production](#production)\n    * [Development](#development)\n    * [Certificate Authority](#certificate-authority)\n    * [SSL Server certificate](#ssl-server-certificate)\n    * [SSL Client certificate](#ssl-client-certificate)\n    * [Diffie-Hellman key exchange](#diffie-hellman-key-exchange)\n\n# Features\n* [Asynchronous communication](https://think-async.com)\n* Supported CPU scalability designs: IO service per thread, thread pool\n* Supported transport protocols: [TCP](#example-tcp-chat-server), [SSL](#example-ssl-chat-server),\n  [UDP](#example-udp-echo-server), [UDP multicast](#example-udp-multicast-server)\n\n# Requirements\n* Windows\n* [7-Zip](https://www.7-zip.org)\n* [cmake](https://www.cmake.org)\n* [git](https://git-scm.com)\n* [gil](https://github.com/chronoxor/gil.git)\n* [python3](https://www.python.org)\n* [Visual Studio](https://www.visualstudio.com)\n\n# How to build?\n\n### Install [gil (git links) tool](https://github.com/chronoxor/gil)\n```shell\npip3 install gil\n```\n\n### Setup repository\n```shell\ngit clone https://github.com/chronoxor/CSharpServer.git\ncd CSharpServer\ngil update\n```\n\n### Generate CMake projects\nRun CMake script to generate C++ projects:\n```shell\ncd modules/CppServer/build/VisualStudio\n01-generate.bat\n```\n\n### Windows (Visual Studio)\nOpen and build [CSharpServer.sln](https://github.com/chronoxor/CSharpServer/blob/master/CSharpServer.sln) or run the build script:\n```shell\ncd build\nvs.bat\n```\n\nThe build script will create \"release\" directory with zip files:\n* CSharpServer.zip - C# Server assembly\n* Benchmarks.zip - C# Server benchmarks\n* Examples.zip - C# Server examples\n\n# Examples\n\n## Example: Asio timer\nHere comes the example of Asio timer. It can be used to wait for some action\nin future with providing absolute time or relative time span. Asio timer can\nbe used in synchronous or asynchronous modes.\n```c#\nusing System;\nusing System.Threading;\nusing CSharpServer;\n\nnamespace AsioTimer\n{\n    class AsioTimer : CSharpServer.Timer\n    {\n        public AsioTimer(Service service) : base(service) {}\n\n        protected override void OnTimer(bool canceled)\n        {\n            Console.WriteLine(\"Asio timer \" + (canceled ? \"canceled\" : \"expired\"));\n        }\n\n        protected override void OnError(int error, string category, string message)\n        {\n            Console.WriteLine($\"Asio timer caught an error with code {error} and category '{category}': {message}\");\n        }\n    }\n\n    class Program\n    {\n        static void Main()\n        {\n            // Create a new service\n            var service = new Service();\n\n            // Start the service\n            Console.Write(\"Service starting...\");\n            service.Start();\n            Console.WriteLine(\"Done!\");\n\n            // Create a new Asio timer\n            var timer = new AsioTimer(service);\n\n            // Setup and synchronously wait for the timer\n            timer.Setup(DateTime.UtcNow.AddSeconds(1));\n            timer.WaitSync();\n\n            // Setup and asynchronously wait for the timer\n            timer.Setup(TimeSpan.FromSeconds(1));\n            timer.WaitAsync();\n\n            // Wait for a while...\n            Thread.Sleep(2000);\n\n            // Setup and asynchronously wait for the timer\n            timer.Setup(TimeSpan.FromSeconds(1));\n            timer.WaitAsync();\n\n            // Wait for a while...\n            Thread.Sleep(500);\n\n            // Cancel the timer\n            timer.Cancel();\n\n            // Wait for a while...\n            Thread.Sleep(500);\n\n            // Stop the service\n            Console.Write(\"Service stopping...\");\n            service.Stop();\n            Console.WriteLine(\"Done!\");\n        }\n    }\n}\n```\n\nOutput of the above example is the following:\n```\nService starting...Done!\nAsio timer expired\nAsio timer canceled\nService stopping...Done!\n```\n\n## Example: TCP chat server\nHere comes the example of the TCP chat server. It handles multiple TCP client\nsessions and multicast received message from any session to all ones. Also it\nis possible to send admin message directly from the server.\n\n```c#\nusing System;\nusing System.Text;\nusing CSharpServer;\n\nnamespace TcpChatServer\n{\n    class ChatSession : TcpSession\n    {\n        public ChatSession(TcpServer server) : base(server) {}\n\n        protected override void OnConnected()\n        {\n            Console.WriteLine($\"Chat TCP session with Id {Id} connected!\");\n\n            // Send invite message\n            string message = \"Hello from TCP chat! Please send a message or '!' to disconnect the client!\";\n            SendAsync(message);\n        }\n\n        protected override void OnDisconnected()\n        {\n            Console.WriteLine($\"Chat TCP session with Id {Id} disconnected!\");\n        }\n\n        protected override void OnReceived(byte[] buffer, long size)\n        {\n            string message = Encoding.UTF8.GetString(buffer, 0, (int)size);\n            Console.WriteLine(\"Incoming: \" + message);\n\n            // Multicast message to all connected sessions\n            Server.Multicast(message);\n\n            // If the buffer starts with '!' the disconnect the current session\n            if (message == \"!\")\n                DisconnectAsync();\n        }\n\n        protected override void OnError(int error, string category, string message)\n        {\n            Console.WriteLine($\"Chat TCP session caught an error with code {error} and category '{category}': {message}\");\n        }\n    }\n\n    class ChatServer : TcpServer\n    {\n        public ChatServer(Service service, int port, InternetProtocol protocol) : base(service, port, protocol) {}\n\n        protected override TcpSession CreateSession() { return new ChatSession(this); }\n\n        protected override void OnError(int error, string category, string message)\n        {\n            Console.WriteLine($\"Chat TCP server caught an error with code {error} and category '{category}': {message}\");\n        }\n    }\n\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            // TCP server port\n            int port = 1111;\n            if (args.Length \u003e 0)\n                port = int.Parse(args[0]);\n\n            Console.WriteLine($\"TCP server port: {port}\");\n\n            // Create a new service\n            var service = new Service();\n\n            // Start the service\n            Console.Write(\"Service starting...\");\n            service.Start();\n            Console.WriteLine(\"Done!\");\n\n            // Create a new TCP chat server\n            var server = new ChatServer(service, port, InternetProtocol.IPv4);\n\n            // Start the server\n            Console.Write(\"Server starting...\");\n            server.Start();\n            Console.WriteLine(\"Done!\");\n\n            Console.WriteLine(\"Press Enter to stop the server or '!' to restart the server...\");\n\n            // Perform text input\n            for (;;)\n            {\n                string line = Console.ReadLine();\n                if (line == string.Empty)\n                    break;\n\n                // Restart the server\n                if (line == \"!\")\n                {\n                    Console.Write(\"Server restarting...\");\n                    server.Restart();\n                    Console.WriteLine(\"Done!\");\n                    continue;\n                }\n\n                // Multicast admin message to all sessions\n                line = \"(admin) \" + line;\n                server.Multicast(line);\n            }\n\n            // Stop the server\n            Console.Write(\"Server stopping...\");\n            server.Stop();\n            Console.WriteLine(\"Done!\");\n\n            // Stop the service\n            Console.Write(\"Service stopping...\");\n            service.Stop();\n            Console.WriteLine(\"Done!\");\n        }\n    }\n}\n```\n\n## Example: TCP chat client\nHere comes the example of the TCP chat client. It connects to the TCP chat\nserver and allows to send message to it and receive new messages.\n\n```c#\nusing System;\nusing System.Text;\nusing System.Threading;\nusing CSharpServer;\n\nnamespace TcpChatClient\n{\n    class ChatClient : TcpClient\n    {\n        public ChatClient(Service service, string address, int port) : base(service, address, port) {}\n\n        public void DisconnectAndStop()\n        {\n            _stop = true;\n            DisconnectAsync();\n            while (IsConnected)\n                Thread.Yield();\n        }\n\n        protected override void OnConnected()\n        {\n            Console.WriteLine($\"Chat TCP client connected a new session with Id {Id}\");\n        }\n\n        protected override void OnDisconnected()\n        {\n            Console.WriteLine($\"Chat TCP client disconnected a session with Id {Id}\");\n\n            // Wait for a while...\n            Thread.Sleep(1000);\n\n            // Try to connect again\n            if (!_stop)\n                ConnectAsync();\n        }\n\n        protected override void OnReceived(byte[] buffer, long size)\n        {\n            Console.WriteLine(Encoding.UTF8.GetString(buffer, 0, (int)size));\n        }\n\n        protected override void OnError(int error, string category, string message)\n        {\n            Console.WriteLine($\"Chat TCP client caught an error with code {error} and category '{category}': {message}\");\n        }\n\n        private bool _stop;\n    }\n\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            // TCP server address\n            string address = \"127.0.0.1\";\n            if (args.Length \u003e 0)\n                address = args[0];\n\n            // TCP server port\n            int port = 1111;\n            if (args.Length \u003e 1)\n                port = int.Parse(args[1]);\n\n            Console.WriteLine($\"TCP server address: {address}\");\n            Console.WriteLine($\"TCP server port: {port}\");\n\n            // Create a new service\n            var service = new Service();\n\n            // Start the service\n            Console.Write(\"Service starting...\");\n            service.Start();\n            Console.WriteLine(\"Done!\");\n\n            // Create a new TCP chat client\n            var client = new ChatClient(service, address, port);\n\n            // Connect the client\n            Console.Write(\"Client connecting...\");\n            client.ConnectAsync();\n            Console.WriteLine(\"Done!\");\n\n            Console.WriteLine(\"Press Enter to stop the client or '!' to reconnect the client...\");\n\n            // Perform text input\n            for (;;)\n            {\n                string line = Console.ReadLine();\n                if (line == string.Empty)\n                    break;\n\n                // Disconnect the client\n                if (line == \"!\")\n                {\n                    Console.Write(\"Client disconnecting...\");\n                    client.DisconnectAsync();\n                    Console.WriteLine(\"Done!\");\n                    continue;\n                }\n\n                // Send the entered text to the chat server\n                client.SendAsync(line);\n            }\n\n            // Disconnect the client\n            Console.Write(\"Client disconnecting...\");\n            client.DisconnectAndStop();\n            Console.WriteLine(\"Done!\");\n\n            // Stop the service\n            Console.Write(\"Service stopping...\");\n            service.Stop();\n            Console.WriteLine(\"Done!\");\n        }\n    }\n}\n```\n\n## Example: SSL chat server\nHere comes the example of the SSL chat server. It handles multiple SSL client\nsessions and multicast received message from any session to all ones. Also it\nis possible to send admin message directly from the server.\n\nThis example is very similar to the TCP one except the code that prepares SSL\ncontext and handshake handler.\n\n```c#\nusing System;\nusing System.Text;\nusing CSharpServer;\n\nnamespace SslChatServer\n{\n    class ChatSession : SslSession\n    {\n        public ChatSession(SslServer server) : base(server) {}\n\n        protected override void OnConnected()\n        {\n            Console.WriteLine($\"Chat SSL session with Id {Id} connected!\");\n        }\n\n        protected override void OnHandshaked()\n        {\n            Console.WriteLine($\"Chat SSL session with Id {Id} handshaked!\");\n\n            // Send invite message\n            string message = \"Hello from SSL chat! Please send a message or '!' to disconnect the client!\";\n            SendAsync(message);\n        }\n\n        protected override void OnDisconnected()\n        {\n            Console.WriteLine($\"Chat SSL session with Id {Id} disconnected!\");\n        }\n\n        protected override void OnReceived(byte[] buffer, long size)\n        {\n            string message = Encoding.UTF8.GetString(buffer, 0, (int)size);\n            Console.WriteLine(\"Incoming: \" + message);\n\n            // Multicast message to all connected sessions\n            Server.Multicast(message);\n\n            // If the buffer starts with '!' the disconnect the current session\n            if (message == \"!\")\n                DisconnectAsync();\n        }\n\n        protected override void OnError(int error, string category, string message)\n        {\n            Console.WriteLine($\"Chat SSL session caught an error with code {error} and category '{category}': {message}\");\n        }\n    }\n\n    class ChatServer : SslServer\n    {\n        public ChatServer(Service service, SslContext context, int port, InternetProtocol protocol) : base(service, context, port, protocol) {}\n\n        protected override SslSession CreateSession() { return new ChatSession(this); }\n\n        protected override void OnError(int error, string category, string message)\n        {\n            Console.WriteLine($\"Chat SSL server caught an error with code {error} and category '{category}': {message}\");\n        }\n    }\n\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            // SSL server port\n            int port = 2222;\n            if (args.Length \u003e 0)\n                port = int.Parse(args[0]);\n\n            Console.WriteLine($\"SSL server port: {port}\");\n\n            // Create a new service\n            var service = new Service();\n\n            // Start the service\n            Console.Write(\"Service starting...\");\n            service.Start();\n            Console.WriteLine(\"Done!\");\n\n            // Create and prepare a new SSL server context\n            var context = new SslContext(SslMethod.TLSV12);\n            context.SetPassword(\"qwerty\");\n            context.UseCertificateChainFile(\"server.pem\");\n            context.UsePrivateKeyFile(\"server.pem\", SslFileFormat.PEM);\n            context.UseTmpDHFile(\"dh4096.pem\");\n\n            // Create a new SSL chat server\n            var server = new ChatServer(service, context, port, InternetProtocol.IPv4);\n\n            // Start the server\n            Console.Write(\"Server starting...\");\n            server.Start();\n            Console.WriteLine(\"Done!\");\n\n            Console.WriteLine(\"Press Enter to stop the server or '!' to restart the server...\");\n\n            // Perform text input\n            for (;;)\n            {\n                string line = Console.ReadLine();\n                if (line == string.Empty)\n                    break;\n\n                // Restart the server\n                if (line == \"!\")\n                {\n                    Console.Write(\"Server restarting...\");\n                    server.Restart();\n                    Console.WriteLine(\"Done!\");\n                    continue;\n                }\n\n                // Multicast admin message to all sessions\n                line = \"(admin) \" + line;\n                server.Multicast(line);\n            }\n\n            // Stop the server\n            Console.Write(\"Server stopping...\");\n            server.Stop();\n            Console.WriteLine(\"Done!\");\n\n            // Stop the service\n            Console.Write(\"Service stopping...\");\n            service.Stop();\n            Console.WriteLine(\"Done!\");\n        }\n    }\n}\n```\n\n## Example: SSL chat client\nHere comes the example of the SSL chat client. It connects to the SSL chat\nserver and allows to send message to it and receive new messages.\n\nThis example is very similar to the TCP one except the code that prepares SSL\ncontext and handshake handler.\n\n```c#\nusing System;\nusing System.Text;\nusing System.Threading;\nusing CSharpServer;\n\nnamespace SslChatClient\n{\n    class ChatClient : SslClient\n    {\n        public ChatClient(Service service, SslContext context, string address, int port) : base(service, context, address, port) {}\n\n        public void DisconnectAndStop()\n        {\n            _stop = true;\n            DisconnectAsync();\n            while (IsConnected)\n                Thread.Yield();\n        }\n\n        protected override void OnConnected()\n        {\n            Console.WriteLine($\"Chat SSL client connected a new session with Id {Id}\");\n        }\n\n        protected override void OnHandshaked()\n        {\n            Console.WriteLine($\"Chat SSL client handshaked a new session with Id {Id}\");\n        }\n\n        protected override void OnDisconnected()\n        {\n            Console.WriteLine($\"Chat SSL client disconnected a session with Id {Id}\");\n\n            // Wait for a while...\n            Thread.Sleep(1000);\n\n            // Try to connect again\n            if (!_stop)\n                ConnectAsync();\n        }\n\n        protected override void OnReceived(byte[] buffer, long size)\n        {\n            Console.WriteLine(Encoding.UTF8.GetString(buffer, 0, (int)size));\n        }\n\n        protected override void OnError(int error, string category, string message)\n        {\n            Console.WriteLine($\"Chat SSL client caught an error with code {error} and category '{category}': {message}\");\n        }\n\n        private bool _stop;\n    }\n\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            // SSL server address\n            string address = \"127.0.0.1\";\n            if (args.Length \u003e 0)\n                address = args[0];\n\n            // SSL server port\n            int port = 2222;\n            if (args.Length \u003e 1)\n                port = int.Parse(args[1]);\n\n            Console.WriteLine($\"SSL server address: {address}\");\n            Console.WriteLine($\"SSL server port: {port}\");\n\n            // Create a new service\n            var service = new Service();\n\n            // Start the service\n            Console.Write(\"Service starting...\");\n            service.Start();\n            Console.WriteLine(\"Done!\");\n\n            // Create and prepare a new SSL client context\n            var context = new SslContext(SslMethod.TLSV12);\n            context.SetDefaultVerifyPaths();\n            context.SetRootCerts();\n            context.SetVerifyMode(SslVerifyMode.VerifyPeer | SslVerifyMode.VerifyFailIfNoPeerCert);\n            context.LoadVerifyFile(\"ca.pem\");\n\n            // Create a new SSL chat client\n            var client = new ChatClient(service, context, address, port);\n\n            // Connect the client\n            Console.Write(\"Client connecting...\");\n            client.ConnectAsync();\n            Console.WriteLine(\"Done!\");\n\n            Console.WriteLine(\"Press Enter to stop the client or '!' to reconnect the client...\");\n\n            // Perform text input\n            for (;;)\n            {\n                string line = Console.ReadLine();\n                if (line == string.Empty)\n                    break;\n\n                // Disconnect the client\n                if (line == \"!\")\n                {\n                    Console.Write(\"Client disconnecting...\");\n                    client.DisconnectAsync();\n                    Console.WriteLine(\"Done!\");\n                    continue;\n                }\n\n                // Send the entered text to the chat server\n                client.SendAsync(line);\n            }\n\n            // Disconnect the client\n            Console.Write(\"Client disconnecting...\");\n            client.DisconnectAndStop();\n            Console.WriteLine(\"Done!\");\n\n            // Stop the service\n            Console.Write(\"Service stopping...\");\n            service.Stop();\n            Console.WriteLine(\"Done!\");\n        }\n    }\n}\n```\n\n## Example: UDP echo server\nHere comes the example of the UDP echo server. It receives a datagram mesage\nfrom any UDP client and resend it back without any changes.\n\n```c#\nusing System;\nusing System.Text;\nusing CSharpServer;\n\nnamespace UdpEchoServer\n{\n    class EchoServer : UdpServer\n    {\n        public EchoServer(Service service, int port, InternetProtocol protocol) : base(service, port, protocol) {}\n\n        protected override void OnStarted()\n        {\n            // Start receive datagrams\n            ReceiveAsync();\n        }\n\n        protected override void OnReceived(UdpEndpoint endpoint, byte[] buffer, long size)\n        {\n            Console.WriteLine(\"Incoming: \" + Encoding.UTF8.GetString(buffer, 0, (int)size));\n\n            // Echo the message back to the sender\n            SendAsync(endpoint, buffer, 0, size);\n        }\n\n        protected override void OnSent(UdpEndpoint endpoint, long sent)\n        {\n            // Continue receive datagrams\n            ReceiveAsync();\n        }\n\n        protected override void OnError(int error, string category, string message)\n        {\n            Console.WriteLine($\"Echo UDP server caught an error with code {error} and category '{category}': {message}\");\n        }\n    }\n\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            // UDP server port\n            int port = 3333;\n            if (args.Length \u003e 0)\n                port = int.Parse(args[0]);\n\n            Console.WriteLine($\"UDP server port: {port}\");\n\n            // Create a new service\n            var service = new Service();\n\n            // Start the service\n            Console.Write(\"Service starting...\");\n            service.Start();\n            Console.WriteLine(\"Done!\");\n\n            // Create a new UDP echo server\n            var server = new EchoServer(service, port, InternetProtocol.IPv4);\n\n            // Start the server\n            Console.Write(\"Server starting...\");\n            server.Start();\n            Console.WriteLine(\"Done!\");\n\n            Console.WriteLine(\"Press Enter to stop the server or '!' to restart the server...\");\n\n            // Perform text input\n            for (;;)\n            {\n                string line = Console.ReadLine();\n                if (line == string.Empty)\n                    break;\n\n                // Restart the server\n                if (line == \"!\")\n                {\n                    Console.Write(\"Server restarting...\");\n                    server.Restart();\n                    Console.WriteLine(\"Done!\");\n                }\n            }\n\n            // Stop the server\n            Console.Write(\"Server stopping...\");\n            server.Stop();\n            Console.WriteLine(\"Done!\");\n\n            // Stop the service\n            Console.Write(\"Service stopping...\");\n            service.Stop();\n            Console.WriteLine(\"Done!\");\n        }\n    }\n}\n```\n\n## Example: UDP echo client\nHere comes the example of the UDP echo client. It sends user datagram message\nto UDP server and listen for response.\n\n```c#\nusing System;\nusing System.Text;\nusing System.Threading;\nusing CSharpServer;\n\nnamespace UdpEchoClient\n{\n    class EchoClient : UdpClient\n    {\n        public EchoClient(Service service, string address, int port) : base(service, address, port) {}\n\n        public void DisconnectAndStop()\n        {\n            _stop = true;\n            DisconnectAsync();\n            while (IsConnected)\n                Thread.Yield();\n        }\n\n        protected override void OnConnected()\n        {\n            Console.WriteLine($\"Echo UDP client connected a new session with Id {Id}\");\n\n            // Start receive datagrams\n            ReceiveAsync();\n        }\n\n        protected override void OnDisconnected()\n        {\n            Console.WriteLine($\"Echo UDP client disconnected a session with Id {Id}\");\n\n            // Wait for a while...\n            Thread.Sleep(1000);\n\n            // Try to connect again\n            if (!_stop)\n                ConnectAsync();\n        }\n\n        protected override void OnReceived(UdpEndpoint endpoint, byte[] buffer, long size)\n        {\n            Console.WriteLine(\"Incoming: \" + Encoding.UTF8.GetString(buffer, 0, (int)size));\n\n            // Continue receive datagrams\n            ReceiveAsync();\n        }\n\n        protected override void OnError(int error, string category, string message)\n        {\n            Console.WriteLine($\"Echo UDP client caught an error with code {error} and category '{category}': {message}\");\n        }\n\n        private bool _stop;\n    }\n\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            // UDP server address\n            string address = \"127.0.0.1\";\n            if (args.Length \u003e 0)\n                address = args[0];\n\n            // UDP server port\n            int port = 3333;\n            if (args.Length \u003e 1)\n                port = int.Parse(args[1]);\n\n            Console.WriteLine($\"UDP server address: {address}\");\n            Console.WriteLine($\"UDP server port: {port}\");\n\n            // Create a new service\n            var service = new Service();\n\n            // Start the service\n            Console.Write(\"Service starting...\");\n            service.Start();\n            Console.WriteLine(\"Done!\");\n\n            // Create a new TCP chat client\n            var client = new EchoClient(service, address, port);\n\n            // Connect the client\n            Console.Write(\"Client connecting...\");\n            client.ConnectAsync();\n            Console.WriteLine(\"Done!\");\n\n            Console.WriteLine(\"Press Enter to stop the client or '!' to reconnect the client...\");\n\n            // Perform text input\n            for (;;)\n            {\n                string line = Console.ReadLine();\n                if (line == string.Empty)\n                    break;\n\n                // Disconnect the client\n                if (line == \"!\")\n                {\n                    Console.Write(\"Client disconnecting...\");\n                    client.DisconnectAsync();\n                    Console.WriteLine(\"Done!\");\n                    continue;\n                }\n\n                // Send the entered text to the chat server\n                client.SendSync(line);\n            }\n\n            // Disconnect the client\n            Console.Write(\"Client disconnecting...\");\n            client.DisconnectAndStop();\n            Console.WriteLine(\"Done!\");\n\n            // Stop the service\n            Console.Write(\"Service stopping...\");\n            service.Stop();\n            Console.WriteLine(\"Done!\");\n        }\n    }\n}\n```\n\n## Example: UDP multicast server\nHere comes the example of the UDP multicast server. It use multicast IP address\nto multicast datagram messages to all client that joined corresponding UDP\nmulticast group.\n\n```c#\nusing System;\nusing System.Text;\nusing CSharpServer;\n\nnamespace UdpMulticastServer\n{\n    class MulticastServer : UdpServer\n    {\n        public MulticastServer(Service service, int port, InternetProtocol protocol) : base(service, port, protocol) {}\n\n        protected override void OnError(int error, string category, string message)\n        {\n            Console.WriteLine($\"Multicast UDP server caught an error with code {error} and category '{category}': {message}\");\n        }\n    }\n\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            // UDP multicast address\n            string multicastAddress = \"239.255.0.1\";\n            if (args.Length \u003e 0)\n                multicastAddress = args[0];\n\n            // UDP multicast port\n            int multicastPort = 3334;\n            if (args.Length \u003e 1)\n                multicastPort = int.Parse(args[1]);\n\n            Console.WriteLine($\"UDP multicast address: {multicastAddress}\");\n            Console.WriteLine($\"UDP multicast port: {multicastPort}\");\n\n            // Create a new service\n            var service = new Service();\n\n            // Start the service\n            Console.Write(\"Service starting...\");\n            service.Start();\n            Console.WriteLine(\"Done!\");\n\n            // Create a new UDP multicast server\n            var server = new MulticastServer(service, 0, InternetProtocol.IPv4);\n\n            // Start the multicast server\n            Console.Write(\"Server starting...\");\n            server.Start(multicastAddress, multicastPort);\n            Console.WriteLine(\"Done!\");\n\n            Console.WriteLine(\"Press Enter to stop the server or '!' to restart the server...\");\n\n            // Perform text input\n            for (;;)\n            {\n                string line = Console.ReadLine();\n                if (line == string.Empty)\n                    break;\n\n                // Restart the server\n                if (line == \"!\")\n                {\n                    Console.Write(\"Server restarting...\");\n                    server.Restart();\n                    Console.WriteLine(\"Done!\");\n                    continue;\n                }\n\n                // Multicast admin message to all sessions\n                line = \"(admin) \" + line;\n                server.MulticastSync(line);\n            }\n\n            // Stop the server\n            Console.Write(\"Server stopping...\");\n            server.Stop();\n            Console.WriteLine(\"Done!\");\n\n            // Stop the service\n            Console.Write(\"Service stopping...\");\n            service.Stop();\n            Console.WriteLine(\"Done!\");\n        }\n    }\n}\n```\n\n## Example: UDP multicast client\nHere comes the example of the UDP multicast client. It use multicast IP address\nand joins UDP multicast group in order to receive multicasted datagram messages\nfrom UDP server.\n\n```c#\nusing System;\nusing System.Text;\nusing System.Threading;\nusing CSharpServer;\n\nnamespace UdpMulticastClient\n{\n    class MulticastClient : UdpClient\n    {\n        public string Multicast;\n\n        public MulticastClient(Service service, string address, int port) : base(service, address, port) {}\n\n        public void DisconnectAndStop()\n        {\n            _stop = true;\n            DisconnectAsync();\n            while (IsConnected)\n                Thread.Yield();\n        }\n\n        protected override void OnConnected()\n        {\n            Console.WriteLine($\"Multicast UDP client connected a new session with Id {Id}\");\n\n            // Join UDP multicast group\n            JoinMulticastGroupAsync(Multicast);\n\n            // Start receive datagrams\n            ReceiveAsync();\n        }\n\n        protected override void OnDisconnected()\n        {\n            Console.WriteLine($\"Multicast UDP client disconnected a session with Id {Id}\");\n\n            // Wait for a while...\n            Thread.Sleep(1000);\n\n            // Try to connect again\n            if (!_stop)\n                ConnectAsync();\n        }\n\n        protected override void OnReceived(UdpEndpoint endpoint, byte[] buffer, long size)\n        {\n            Console.WriteLine(\"Incoming: \" + Encoding.UTF8.GetString(buffer, 0, (int)size));\n\n            // Continue receive datagrams\n            ReceiveAsync();\n        }\n\n        protected override void OnError(int error, string category, string message)\n        {\n            Console.WriteLine($\"Multicast UDP client caught an error with code {error} and category '{category}': {message}\");\n        }\n\n        private bool _stop;\n    }\n\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            // UDP listen address\n            string listenAddress = \"0.0.0.0\";\n            if (args.Length \u003e 0)\n                listenAddress = args[0];\n\n            // UDP multicast address\n            string multicastAddress = \"239.255.0.1\";\n            if (args.Length \u003e 1)\n                multicastAddress = args[1];\n\n            // UDP multicast port\n            int multicastPort = 3334;\n            if (args.Length \u003e 2)\n                multicastPort = int.Parse(args[2]);\n\n            Console.WriteLine($\"UDP listen address: {listenAddress}\");\n            Console.WriteLine($\"UDP multicast address: {multicastAddress}\");\n            Console.WriteLine($\"UDP multicast port: {multicastPort}\");\n\n            // Create a new service\n            var service = new Service();\n\n            // Start the service\n            Console.Write(\"Service starting...\");\n            service.Start();\n            Console.WriteLine(\"Done!\");\n\n            // Create a new TCP chat client\n            var client = new MulticastClient(service, listenAddress, multicastPort);\n            client.SetupMulticast(true);\n            client.Multicast = multicastAddress;\n\n            // Connect the client\n            Console.Write(\"Client connecting...\");\n            client.ConnectAsync();\n            Console.WriteLine(\"Done!\");\n\n            Console.WriteLine(\"Press Enter to stop the client or '!' to reconnect the client...\");\n\n            // Perform text input\n            for (;;)\n            {\n                string line = Console.ReadLine();\n                if (line == string.Empty)\n                    break;\n\n                // Disconnect the client\n                if (line == \"!\")\n                {\n                    Console.Write(\"Client disconnecting...\");\n                    client.DisconnectAsync();\n                    Console.WriteLine(\"Done!\");\n                    continue;\n                }\n            }\n\n            // Disconnect the client\n            Console.Write(\"Client disconnecting...\");\n            client.DisconnectAndStop();\n            Console.WriteLine(\"Done!\");\n\n            // Stop the service\n            Console.Write(\"Service stopping...\");\n            service.Stop();\n            Console.WriteLine(\"Done!\");\n        }\n    }\n}\n```\n\n# Performance\n\nHere comes several communication scenarios with timing measurements.\n\nBenchmark environment is the following:\n```\nCPU architecutre: Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz\nCPU logical cores: 8\nCPU physical cores: 4\nCPU clock speed: 3.998 GHz\nCPU Hyper-Threading: enabled\nRAM total: 31.962 GiB\nRAM free: 21.623 GiB\n\nOS version: Microsoft Windows 8 Enterprise Edition (build 9200), 64-bit\nOS bits: 64-bit\nProcess bits: 64-bit\nProcess configuaraion: release\n```\n\n## Benchmark: Round-Trip\n\n![Round-trip](https://github.com/chronoxor/CSharpServer/raw/master/images/round-trip.png)\n\nThis scenario sends lots of messages from several clients to a server.\nThe server responses to each message and resend the similar response to\nthe client. The benchmark measures total round-trip time to send all\nmessages and receive all responses, messages \u0026 data throughput, count\nof errors.\n\n### TCP echo server\n\n* [TcpEchoServer](https://github.com/chronoxor/CSharpServer/blob/master/performance/TcpEchoServer/Program.cs)\n* [TcpEchoClient](https://github.com/chronoxor/CSharpServer/blob/master/performance/TcpEchoClient/Program.cs) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 1111\nWorking threads: 1\nWorking clients: 1\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.001 s\nTotal data: 1.493 GiB\nTotal messages: 49715465\nData throughput: 151.709 MiB/s\nMessage latency: 201 ns\nMessage throughput: 4970671 msg/s\n```\n\n* [TcpEchoServer](https://github.com/chronoxor/CSharpServer/blob/master/performance/TcpEchoServer/Program.cs)\n* [TcpEchoClient](https://github.com/chronoxor/CSharpServer/blob/master/performance/TcpEchoClient/Program.cs) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 1111\nWorking threads: 4\nWorking clients: 100\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.011 s\nTotal data: 1.055 GiB\nTotal messages: 35386331\nData throughput: 107.882 MiB/s\nMessage latency: 282 ns\nMessage throughput: 3534415 msg/s\n```\n\n### SSL echo server\n\n* [SslEchoServer](https://github.com/chronoxor/CSharpServer/blob/master/performance/SslEchoServer/Program.cs)\n* [SslEchoClient](https://github.com/chronoxor/CSharpServer/blob/master/performance/SslEchoClient/Program.cs) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 2222\nWorking threads: 1\nWorking clients: 1\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.013 s\nTotal data: 151.424 MiB\nTotal messages: 4961559\nData throughput: 15.124 MiB/s\nMessage latency: 2.018 mcs\nMessage throughput: 495500 msg/s\n```\n\n* [SslEchoServer](https://github.com/chronoxor/CSharpServer/blob/master/performance/SslEchoServer/Program.cs)\n* [SslEchoClient](https://github.com/chronoxor/CSharpServer/blob/master/performance/SslEchoClient/Program.cs) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 2222\nWorking threads: 4\nWorking clients: 100\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.305 s\nTotal data: 411.159 MiB\nTotal messages: 13472751\nData throughput: 39.917 MiB/s\nMessage latency: 764 ns\nMessage throughput: 1307300 msg/s\n```\n\n### UDP echo server\n\n* [UdpEchoServer](https://github.com/chronoxor/CSharpServer/blob/master/performance/UdpEchoServer/Program.cs)\n* [UdpEchoClient](https://github.com/chronoxor/CSharpServer/blob/master/performance/UdpEchoClient/Program.cs) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 3333\nWorking threads: 1\nWorking clients: 1\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 208.608 ms\nTotal data: 220.736 KiB\nTotal messages: 7063\nData throughput: 1.034 MiB/s\nMessage latency: 29.535 mcs\nMessage throughput: 33857 msg/s\n```\n\n* [UdpEchoServer](https://github.com/chronoxor/CSharpServer/blob/master/performance/UdpEchoServer/Program.cs)\n* [UdpEchoClient](https://github.com/chronoxor/CSharpServer/blob/master/performance/UdpEchoClient/Program.cs) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 3333\nWorking threads: 4\nWorking clients: 100\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.008 s\nTotal data: 41.094 MiB\nTotal messages: 1346505\nData throughput: 4.108 MiB/s\nMessage latency: 7.432 mcs\nMessage throughput: 134540 msg/s\n```\n\n## Benchmark: Multicast\n\n![Multicast](https://github.com/chronoxor/CSharpServer/raw/master/images/multicast.png)\n\nIn this scenario server multicasts messages to all connected clients.\nThe benchmark counts total messages received by all clients for all\nthe working time and measures messages \u0026 data throughput, count\nof errors.\n\n### TCP multicast server\n\n* [TcpMulticastServer](https://github.com/chronoxor/CSharpServer/blob/master/performance/TcpMulticastServer/Program.cs)\n* [TcpMulticastClient](https://github.com/chronoxor/CSharpServer/blob/master/performance/TcpMulticastClient/Program.cs) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 1111\nWorking threads: 1\nWorking clients: 1\nMessage size: 32\n\nErrors: 0\n\nTotal time: 10.003 s\nTotal data: 947.864 MiB\nTotal messages: 31058956\nData throughput: 94.764 MiB/s\nMessage latency: 322 ns\nMessage throughput: 3104662 msg/s\n```\n\n* [TcpMulticastServer](https://github.com/chronoxor/CSharpServer/blob/master/performance/TcpMulticastServer/Program.cs)\n* [TcpMulticastClient](https://github.com/chronoxor/CSharpServer/blob/master/performance/TcpMulticastClient/Program.cs) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 1111\nWorking threads: 4\nWorking clients: 100\nMessage size: 32\n\nErrors: 0\n\nTotal time: 10.008 s\nTotal data: 330.068 MiB\nTotal messages: 10815633\nData throughput: 32.1001 MiB/s\nMessage latency: 925 ns\nMessage throughput: 1080613 msg/s\n```\n\n### SSL multicast server\n\n* [SslMulticastServer](https://github.com/chronoxor/CSharpServer/blob/master/performance/SslMulticastServer/Program.cs)\n* [SslMulticastClient](https://github.com/chronoxor/CSharpServer/blob/master/performance/SslMulticastClient/Program.cs) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 2222\nWorking threads: 1\nWorking clients: 1\nMessage size: 32\n\nErrors: 0\n\nTotal time: 10.012 s\nTotal data: 974.081 MiB\nTotal messages: 31918634\nData throughput: 97.291 MiB/s\nMessage latency: 313 ns\nMessage throughput: 3187826 msg/s\n```\n\n* [SslMulticastServer](https://github.com/chronoxor/CSharpServer/blob/master/performance/SslMulticastServer/Program.cs)\n* [SslMulticastClient](https://github.com/chronoxor/CSharpServer/blob/master/performance/SslMulticastClient/Program.cs) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 2222\nWorking threads: 4\nWorking clients: 100\nMessage size: 32\n\nErrors: 0\n\nTotal time: 10.401 s\nTotal data: 1.121 GiB\nTotal messages: 37543146\nData throughput: 110.149 MiB/s\nMessage latency: 277 ns\nMessage throughput: 3609250 msg/s\n```\n\n### UDP multicast server\n\n* [UdpMulticastServer](https://github.com/chronoxor/CSharpServer/blob/master/performance/UdpMulticastServer/Program.cs)\n* [UdpMulticastClient](https://github.com/chronoxor/CSharpServer/blob/master/performance/UdpMulticastClient/Program.cs) --clients 1 --threads 1\n\n```\nServer address: 239.255.0.1\nServer port: 3333\nWorking threads: 1\nWorking clients: 1\nMessage size: 32\n\nErrors: 0\n\nTotal time: 10.002 s\nTotal data: 15.1000 MiB\nTotal messages: 523548\nData throughput: 1.611 MiB/s\nMessage latency: 19.106 mcs\nMessage throughput: 52339 msg/s\n```\n\n* [UdpMulticastServer](https://github.com/chronoxor/CSharpServer/blob/master/performance/UdpMulticastServer/Program.cs)\n* [UdpMulticastClient](https://github.com/chronoxor/CSharpServer/blob/master/performance/UdpMulticastClient/Program.cs) --clients 100 --threads 4\n\n```\nServer address: 239.255.0.1\nServer port: 3333\nWorking threads: 4\nWorking clients: 100\nMessage size: 32\n\nErrors: 0\n\nTotal time: 10.013 s\nTotal data: 57.876 MiB\nTotal messages: 1895816\nData throughput: 5.796 MiB/s\nMessage latency: 5.281 mcs\nMessage throughput: 189335 msg/s\n```\n\n# OpenSSL certificates\nIn order to create OpenSSL based server and client you should prepare a set of\nSSL certificates.\n\n## Production\nDepending on your project, you may need to purchase a traditional SSL\ncertificate signed by a Certificate Authority. If you, for instance,\nwant some else's web browser to talk to your WebSocket project, you'll\nneed a traditional SSL certificate.\n\n## Development\nThe commands below entered in the order they are listed will generate a\nself-signed certificate for development or testing purposes.\n\nIf you want to save some time, download and run [Generate-Certs](https://github.com/asheroto/NetCoreServer-Generate-Certs)\nand it will generate the certs for you in less than a minute.\nSupports Windows + Linux.\n\nIf you'd rather enter in the commands to generate the certificates manually,\nhere is the list in order below.\n\n## Certificate Authority\n\n* Create CA private key\n```shell\nopenssl genrsa -passout pass:qwerty -out ca-secret.key 4096\n```\n\n* Remove passphrase\n```shell\nopenssl rsa -passin pass:qwerty -in ca-secret.key -out ca.key\n```\n\n* Create CA self-signed certificate\n```shell\nopenssl req -new -x509 -days 3650 -subj '/C=BY/ST=Belarus/L=Minsk/O=Example root CA/OU=Example CA unit/CN=example.com' -key ca.key -out ca.crt\n```\n\n* Convert CA self-signed certificate to PFX\n```shell\nopenssl pkcs12 -export -passout pass:qwerty -inkey ca.key -in ca.crt -out ca.pfx\n```\n\n* Convert CA self-signed certificate to PEM\n```shell\nopenssl pkcs12 -passin pass:qwerty -passout pass:qwerty -in ca.pfx -out ca.pem\n```\n\n## SSL Server certificate\n\n* Create private key for the server\n```shell\nopenssl genrsa -passout pass:qwerty -out server-secret.key 4096\n```\n\n* Remove passphrase\n```shell\nopenssl rsa -passin pass:qwerty -in server-secret.key -out server.key\n```\n\n* Create CSR for the server\n```shell\nopenssl req -new -subj '/C=BY/ST=Belarus/L=Minsk/O=Example server/OU=Example server unit/CN=server.example.com' -key server.key -out server.csr\n```\n\n* Create certificate for the server\n```shell\nopenssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt\n```\n\n* Convert the server certificate to PFX\n```shell\nopenssl pkcs12 -export -passout pass:qwerty -inkey server.key -in server.crt -out server.pfx\n```\n\n* Convert the server certificate to PEM\n```shell\nopenssl pkcs12 -passin pass:qwerty -passout pass:qwerty -in server.pfx -out server.pem\n```\n\n## SSL Client certificate\n\n* Create private key for the client\n```shell\nopenssl genrsa -passout pass:qwerty -out client-secret.key 4096\n```\n\n* Remove passphrase\n```shell\nopenssl rsa -passin pass:qwerty -in client-secret.key -out client.key\n```\n\n* Create CSR for the client\n```shell\nopenssl req -new -subj '/C=BY/ST=Belarus/L=Minsk/O=Example client/OU=Example client unit/CN=client.example.com' -key client.key -out client.csr\n```\n\n* Create the client certificate\n```shell\nopenssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt\n```\n\n* Convert the client certificate to PFX\n```shell\nopenssl pkcs12 -export -passout pass:qwerty -inkey client.key -in client.crt -out client.pfx\n```\n\n* Convert the client certificate to PEM\n```shell\nopenssl pkcs12 -passin pass:qwerty -passout pass:qwerty -in client.pfx -out client.pem\n```\n\n## Diffie-Hellman key exchange\n\n* Create DH parameters\n```shell\nopenssl dhparam -out dh4096.pem 4096\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchronoxor%2Fcsharpserver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchronoxor%2Fcsharpserver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchronoxor%2Fcsharpserver/lists"}