Active development occurs on the 'dev' branch. For the stable release, refer to the 'main' branch.
Netly version 4 will be released soon, help validating the new way of interacting with netly. See more
⭐ Your star on Netly brightens our journey and makes a real impact! |
Version 4 Development Roadmap
🚀 Fully Implemented | Byter 3 • TCP Client • TCP Server • UDP Client • UDP Server • HTTP Client • HTTP Server • HTTP WebSocket • RUDP Client • RUDP Server |
---|---|
🔧 Work in Progress |
Documentation v4 #63 (New docs website) HTTP Body (Enctype Detector and Parser) #67 (Body Parser as Middleware) |
🔜 Pending Features | Adding Byter v4 *In development Adding RUDP tests Adding HTTP tests Adding Websocket tests |
Get basic information about this project called Netly
Overview |
Netly is a robust C# socket library designed to streamline network communication. It offers comprehensive support for multiple protocols, including HTTP, TCP, SSL/TLS, UDP, Reliable UDP (RUDP), and WebSocket. This versatility makes Netly an excellent choice for developing a wide range of applications, from multiplayer games and chat systems to real-time data exchanges. |
---|---|
Website |
Repository: github.com/alec1o/netly Documentation: netly.docs.kezero.com |
Sponsor |
|
Supporter |
Why Contribute to Netly?- Transform Network Communication:Join us in revolutionizing how software communicates. Your contributions will help build a library that sets new standards for efficiency and reliability. - Advance Your Career: Engage with innovative projects, solve complex problems, and collaborate with experts. Your involvement will sharpen your skills and expand your professional network. - Share Your Ideas: Whether you're a seasoned developer or just starting out, your ideas are valuable. Contribute thoughts and suggestions to shape the future of Netly and drive innovation. |
Official publisher
Nuget | Unity Asset Store |
---|---|
Install on Nuget | Install on Asset Store |
Notable changes
v1.x.x | v2.x.x | v3.x.x | v4.x.x |
---|---|---|---|
Legacy | Legacy | Stable | Development |
TCP Support | TCP with Message Framing support | TCP with TLS/SSL support | HTTP client and server support |
UDP Support | TCP and UDP performance increase | UDP with connection (timeout response) | Reliable UDP (RUDP) client and server support |
New Message Framing protocol and performance increase | WebSocket client and server support | ||
Upgrade to Byter 2.0 | Upgrade to Byter 3.0 | ||
Docsify as documentation framework | Documentation improvement by Docusaurus and DocFxMarkdownGen | ||
Syntax and internal improvement | |||
XML comments improvement |
Technical descriptions about integrations
List of tested platforms |
- [.NET](https://dotnet.microsoft.com) (SDK) - [Mono](https://mono-project.com) (SDK) - [Unity](https://unity.com) (Engine) - [Operating system](https://en.wikipedia.org/wiki/Operating_system) (OS) - Linux - Windows - Android - iOS - macOS Notice: This library might run on all devices. If it doesn't work on any device, it should be considered a bug and reported. |
---|---|
Dependencies |
Byter |
Build |
> ###### Build dependencies - [Git](http://git-scm.com/) - [.NET](http://dot.net) > ###### Build step-by-step ```rb # 1. clone project $ git clone "https://github.com/alec1o/Netly" netly # 2. build project $ dotnet build "netly/" -c Release -o "netly/bin/" # NOTE: # Netly.dll require Byter.dll because is Netly dependency # Netly.dll and Byter.dll have on build folder |
Features |
> Below are some missing features that are planned to be added in later versions. - ``N/A`` |
Code highlights
TCP |
📄 Client```csharp using Netly; TCP.Client client = new TCP.Client(framing: true); ``` ```csharp client.On.Open(() => { printf("connection opened"); }); client.On.Close(() => { printf("connetion closed"); }); client.On.Error((exception) => { printf("connection erro on open"); }); client.On.Data((bytes) => { printf("connection receive a raw data"); }); client.On.Event((name, data) => { printf("connection receive a event"); }); client.On.Modify((socket) => { printf("called before try open connection."); }); client.On.Encryption((certificate, chain, errors) => { // Only if client.IsEncrypted is enabled printf("validate ssl/tls certificate"); // return true if certificate is valid return true; }); ``` ```csharp // open connection if closed client.To.Open(new Host("127.0.0.1", 8080)); // close connection if opened client.To.Close(); // send raw data if connected client.To.Data(new byte[2] { 128, 255 }); client.To.Data("hello world", NE.Encoding.UTF8); // send event if connected client.To.Event("name", new byte[2] { 128, 255 }); client.To.Event("name", "hello world", NE.Encoding.UTF8); // enable encryption (must call before client.To.Open) client.To.Encryption(true); ```📄 Server```csharp using Netly; TCP.Server server = new TCP.Server(framing: true); ``` ```csharp server.On.Open(() => { printf("connection opened"); }); server.On.Close(() => { printf("connection closed"); }); server.On.Error((exception) => { printf("connection error on open"); }); server.On.Accept((client) => { client.On.Modify((socket) => { printf("modify client socket e.g Enable NoDelay"); }); client.On.Open(() => { printf("client connected"); }); client.On.Data((bytes) => { printf("client receive a raw data"); }); client.On.Event((name, bytes) => { printf("client receive a event"); }); client.On.Close(() => { printf("client disconnected"); }); }); server.On.Modify((socket) => { printf("called before try open connection."); }); ``` ```csharp // open connection server.To.Open(new Host("1.1.1.1", 1111)); // close connection server.To.Close(); // enable encryption support (must called before server.To.Open) server.To.Encryption(enable: true, @mypfx, @mypfxpassword, SslProtocols.Tls12); // broadcast raw data for all connected client server.To.DataBroadcast("text buffer"); server.To.DataBroadcast(new byte[] { 1, 2, 3 }); // broadcast event (netly event) for all connected client server.To.EventBroadcast("event name", "text buffer"); server.To.EventBroadcast("event name", new byte[] { 1, 2, 3 }); ``` |
---|---|
UDP |
📄 Client```csharp using Netly; UDP.Client client = new UDP.Client(); ``` ```csharp client.On.Open(() => { printf("connection opened"); }); client.On.Close(() => { printf("connection closed"); }); client.On.Error((exception) => { printf("connection error on open"); }); client.On.Data((bytes) => { printf("connection received a raw data"); }); client.On.Event((name, eventBytes) => { printf("connection received a event"); }); client.On.Modify((socket) => { printf("called before try open connection."); }); ``` ```csharp // open connection if closed client.To.Open(new Host("127.0.0.1", 8080)); // close connection if opened client.To.Close(); // send raw data if connected client.To.Data(new byte[2] { 128, 255 }); client.To.Data("hello world", NE.Encoding.UTF8); // send event if connected client.To.Event("name", new byte[2] { 128, 255 }); client.To.Event("name", "hello world", NE.Encoding.UTF8); ```📄 Server```csharp using Netly; UDP.Server server = new UDP.Server(); ``` ```csharp server.On.Open(() => { printf("connection opened"); }); server.On.Close(() => { printf("connection closed"); }); server.On.Error((exception) => { printf("connection error on open"); }); server.On.Accept((client) => { client.On.Open(() => { printf("client connected"); }); client.On.Close(() => { // Only if use connection is enabled. printf("client disconnected"); }); client.On.Data((bytes) => { printf("client received a raw data"); }); client.On.Event((name, bytes) => { printf("client received a event"); }); }); ``` ```csharp // open connection server.To.Open(new Host("127.0.0.1", 8080)); // close connection server.To.Close(); // broadcast raw data for all connected client server.To.DataBroadcast("text buffer"); server.To.DataBroadcast(new byte[] { 1, 2, 3 }); // broadcast event (netly event) for all connected client server.To.EventBroadcast("event name", "text buffer"); server.To.EventBroadcast("event name", new byte[] { 1, 2, 3 }); ``` |
HTTP |
📄 Client```csharp using Netly; HTTP.Client client = new HTTP.Client(); // add http header for request client.Headers.Add("Content-Type", "json"); client.Headers.Add("Token", "ImGui.h"); // add http url queries e.g: https://www.alec1o.com/?page=about&version=4 client.Queries.Add("page", "about"); client.Queries.Add("version", "4"); // set request timeout (ms) default 15s (15000ms), 0 or negative value means infinite timeout. client.Timeout = 6000; // 6s // is opened: while is requesting bool isFetching = client.IsOpened; ``` ```csharp HttpClient http = null; // called before try connect to server // modify the HttpClient object client.On.Modify((HttpClient instance) => { http = instance; }); // connection is opened and fetch server. client.On.Open((response) => { // you can use "http" instance on this scope (isn't null) if (http.📄 Server```csharp using Netly; HTTP.Server server = new HTTP.Server(); // return true if server is serve http context bool isServe = server.IsOpened; ``` ```csharp server.On.Open(() => { // http server opened }); server.On.Close(() => { // http server closed }); server.On.Error((exception) => { // http server open error }); server.On.Modify((httpListener) => { // HttpListener instance, called before try open connection. }); // Open http server connection server.To.Open(new Uri("http://127.0.0.1:8080/")); // Close http server connection server.To.Close(); ``` ##### Map ```csharp // Map path server.Map.Get("/", async (req, res) => { // Handle async: GET }) server.Map.Post("/user", (req, res) => { // Handle sync: POST }); // map using dynamic URL server.Map.Delete("/post/{userId}/group/{groupId}", async (req, res)) => { string userId = req.Param["userId"]; string groupId = req.Param["groupId"]; // Handle async: Delete from dynamic URL path }); server.Map.WebSocket("/echo", (req, ws) => { // Handle websocket connection from path }); /* You can map: * Get # get request * Post # post request * Delete # delete request * Put # put request * Patch # patch request * Trace # trace request * Options # options request * Head # head request, (only head) * All # all http nethod request * WebSocket # websocket request */ ``` ##### Middleware ```csharp /* Note: Middlewares is executed in added order */ // Global Middleware (*don't have workflow path) server.Middleware.Add(async (req, res, next) => { // verify request timer Stopwatch watch = new Stopwatch(); // init timer next(); // call another middleware. watch.Stop(); // stop timer res.Header.Add("Request-Timer", watch.ElapsedMilliseconds.ToString()); }); // Local middleware (have workflow path) server.Middleware.Add("/admin", async (req, res, next) => { if (MyApp.CheckAdminByHeader(req.Header)) { res.Header.Add("Admin-Token", MyApp.RefreshAdminHeaderToken(req)); // call next middleware next(); // now. all middleware is executed. (because this is two way middleware) res.Header.Add("Request-Delay", (DateTime.UtcNow - timer)()); } else { res.Header.Add("Content-Type", "application/json;charset=UTF-8"); await res.Send(404, "{ 'error': 'invalid request.' }"); // skip other middlewares: // next(); } }); ``` |
RUDP |
📄 Client```csharp using Netly; RUDP.Client client = new RUDP.Client(); ``` ```csharp client.On.Open(() => { printf("connection opened"); }); client.On.Close(() => { printf("connection closed"); }); client.On.Error((exception) => { printf("connection error on open"); }); client.On.Data((bytes, type) => { printf("connection received a raw data"); }); client.On.Event((name, bytes, type) => { printf("connection received a event"); }); client.On.Modify((socket) => { printf("called before try open connection."); }); ``` ```csharp // open connection if closed client.To.Open(new Host("127.0.0.1", 8080)); // close connection if opened client.To.Close(); // send raw data if connected client.To.Data(new byte[2] { 128, 255 }, RUDP.Unreliable); client.To.Data("hello world", NE.Encoding.UTF8, RUDP.Reliable); // send event if connected client.To.Event("name", new byte[2] { 128, 255 }, RUDP.Unreliable); client.To.Event("name", "hello world", NE.Encoding.UTF8, RUDP.Reliable); ```📄 Server```csharp using Netly; RUDP.Server server = new RUDP.Server(); ``` ```csharp server.On.Open(() => { printf("connection opened"); }); server.On.Close(() => { printf("connection closed"); }); server.On.Error((exception) => { printf("connection error on open"); }); server.On.Accept((client) => { client.On.Open(() => { printf("client connected"); }); client.On.Close(() => { // Only if use connection is enabled. printf("client disconnected"); }); client.On.Data((bytes, type) => { if (type == RUDP.Reliable) { ... } else if (type == RUDP.Unreliable) { ... } else { ... } /* type == RUDP.Sequenced */ printf("client received a raw data"); }); client.On.Event((name, type) => if (type == RUDP.Reliable) { ... } else if (type == RUDP.Unreliable) { ... } else { ... } /* type == RUDP.Sequenced */ printf("client received a event"); }); }); ``` ```csharp // open connection server.To.Open(new Host("127.0.0.1", 8080)); // close connection server.To.Close(); // broadcast raw data for all connected client server.To.DataBroadcast("text buffer", RUDP.Unreliable); server.To.DataBroadcast(new byte[] { 1, 2, 3 }, RUDP.Reliable); server.To.DataBroadcast(new byte[] { 3, 2, 1 }, RUDP.Sequenced); // broadcast event (netly event) for all connected client server.To.EventBroadcast("event name", "text buffer", RUDP.Unreliable); server.To.EventBroadcast("event name", new byte[] { 1, 2, 3 }, RUDP.Reliable); server.To.EventBroadcast("event name", new byte[] { 3, 2, 1 }, RUDP.Sequenced); ``` |
WebSocket |
📄 Client```csharp using Netly; HTTP.WebSocket client = new HTTP.WebSocket(); ``` ```csharp client.On.Open(() => { // websocket connection opened }); client.On.Close(() => { // websocket connection closed }); client.On.Error((exception) => { // error on open websocket connectin }); client.On.Data((bytes, type) => { if (type == HTTP.Binary) { ... } else if (type == HTTP.Text) { ... } else { /* NOTE: it's imposible */ } // raw data received from server }); client.On.Event((name, bytes, type) => { if (type == HTTP.Binary) { ... } else if (type == HTTP.Text) { ... } else { /* NOTE: it's imposible */ } // event received from server }); client.On.Modify((wsSocket) => { // modify websocket socket }); ``` ```csharp // open websocket client connection client.To.Open(new Uri("ws://127.0.0.1:8080/echo")); // close websocket client connection client.To.Close(); // send raw data for server // text message client.To.Data("my message", HTTP.Text); // binnary message client.To.Data(NE.GetBytes("my buffer"), HTTP.Binary); // send event (netly event) for server // text message client.To.Event("event name", "my message", HTTP.Text); // binnary message client.To.Data("event name", NE.GetBytes("my buffer"), HTTP.Binary); ```📄 Server```csharp using Netly; using Netly.Interfaces; HTTP.Server server = new HTTP.Server(); IHTTP.WebSocket[] Clients = server.WebSocketClients; ``` ```csharp server.Map.WebSocket("/chat/{token}", async (req, ws) => { // Accept websocket from dynamic path string token = req.Params["token"]; // validate websocket connection from params if (Foo.Bar(token) == false) { ws.To.Close(); } ws.On.Modify(...); ws.On.Open(...); ws.On.Close(...); ws.On.Data(...); ws.On.Event(...); }); server.Map.Websocket("/echo", (req, ws) => { // Handle websocket on /echo path ws.On.Modify((wsSocket) => { // modify server-side websocket ocket }); ws.On.Open(() => { // server-side websocket connection opened }); ws.On.Close(() => { // server-side websocket connection closed }); ws.On.Data((bytes, type) => { if (type == HTTP.Binary) { ... } else if (type == HTTP.Text) { ... } else { /* NOTE: it's imposible */ } // server-side websocket received raw data }); ws.On.Event((name, bytes, type) => { if (type == HTTP.Binary) { ... } else if (type == HTTP.Text) { ... } else { /* NOTE: it's imposible */ } // server-side websocket received event }); }); ``` ```csharp server.On.Open(() => { // http server opened }); server.On.Close(() => { // http server closed }); server.On.Error((exception) => { // http server open error }); server.On.Modify((httpListener) => { // HttpListener instance, called before try open connection. }); // Open http server connection server.To.Open(new Uri("http://127.0.0.1:8080/")); // Close http server connection server.To.Close(); ``` ```csharp // open websocket client connection server.To.Open(new Uri("ws://127.0.0.1:8080/echo")); // close websocket client connection server.To.Close(); // broadcast raw data for all connected websocket socket // text message server.To.WebsocketDataBroadcast("my message", HTTP.Text); // binnary message server.To.WebsocketDataBroadcast(NE.GetBytes("my buffer"), HTTP.Binary); // broadcast event (netly event) for all connected websocket socket // text message server.To.WebsocketEventBroadcast("event name", "my message", HTTP.Text); // binnary message server.To.WebsocketEventBroadcast("event name", NE.GetBytes("my buffer"), HTTP.Binary); ``` |
Byter |
###### For more information and details see [Byter's](https://github.com/alec1o/Byter) official information
> Byter documentation: [alec1o/Byter](https://github.com/alec1o/Byter)
📄 Primitive```csharp using Byter; ``` - Serialize _(have +20 types of data supported, e.g. enum, bool, array, list, class, struct,... [see official docs](https://github.com/alec1o/Byter)_ ```csharp Primitive primitive = new(); // add element primitive.Add.ULong(1024); // e.g. Id primitive.Add.DateTime(DateTime.UtcNow); // e.g. Sent Time primitive.Add.Struct(new Student() {...}); // e.g Student primitive.Add.Class(new Employee() {...}); // e.g Employee ... // get buffer byte[] buffer = primitive.GetBytes(); ``` - Deserialize ```csharp // WARNING: Need primitive buffer to deserialize Primitive primitive = new(...buffer); ulong id = primitive.Get.ULong(); DateTime sentTime = primitive.Get.DateTime(); Student student = primitive.Get.StructBut when you want to deserialize your (Class, Structure, List 📄 Extension```csharp using Byter; ``` - Global Default Encoding [(source code spec)](https://github.com/alec1o/Byter/blob/main/src/src/extension/StringExtension.cs#L8) ```csharp // update global defaut encoding. Default is UTF8 StringExtension.Default = Encoding.Unicode; // Unicode is UTF16 ``` - Convert string to byte[] ```csharp // using global encoding (*UTF8) byte[] username = "@alec1o".GetBytes(); // using UNICODE (*UTF16) encoding byte[] message = "Hello 👋 World 🌎".GetBytes(Encoding.Unicode); // using UTF32 encoding string secreatWord = "I'm not human, I'm a concept."; byte[] secreat = secreatWord.GetBytes(Encoding.UTF32); ``` - Convert byte[] to string ```csharp // using global encoding (*UTF8) string username = new byte[] { ... }.GetString(); // using UNICODE (*UTF16) encoding string message = new byte[] { ... }.GetString(Encoding.Unicode); // using UTF32 encoding byte[] secreat = new byte[] { ... }; string secreatWord = secreat.GetString(Encoding.UTF32); ``` - Capitalize string ```rb string name = "alECio furanZE".ToCapitalize(); # Alecio Furanze string title = "i'M noT humAn"; title = title.ToCapitalize(); # I'm Not Human ``` - UpperCase string ```rb string name = "alECio furanZE".ToUpperCase(); # ALECIO FURANZE string title = "i'M noT humAn"; title = title.ToUpperCase(); # I'M NOT HUMAN ``` - LowerCase string ```rb string name = "ALEciO FUraNZE".ToLowerCase(); # alecio furanze string title = "i'M Not huMAN"; title = title.ToLowerCase(); # i'm not human ``` |
Integration and interaction example codes
Standard |
📄 Console```csharp using System; using Netly; public class Program { private static void Main(string[] args) { UDP.Client client = new UDP.Client(); client.On.Open(() => { Console.WriteLine( |
---|---|
Flax Engine |
📄 Script```csharp using System; using FlaxEngine; using Netly; public class Example : Script { public string message; internal UDP.Client client; public override void Awake() { client = new UDP.Client(); client.On.Open(() => { Debug.Log( |
Unity Engine |
📄 MonoBehaviour```csharp using System; using FlaxEngine; using Netly; public class Example : MonoBehaviour { public string message; internal UDP.Client client; private void Awake() { client = new UDP.Client(); client.On.Open(() => { Debug.Log( |
WARNING: |
Initialize event handlers once, not in loops. Set up handlers with ` Handle protocol actions wisely. Use ` ```csharp // OK 100% Recommended private void Start() { var client = ...; client.On.Open(() => ...); // e.g generic handler client.On.Open(() => ...); // e.g only to send "Hi" client.On.Event((name, bytes, ?) => ...); // e.g generic event handler client.On.Event((name, bytes, ?) => ...); // e.g only to handle A event client.On.Event((name, bytes, ?) => ...); // e.g only to handle B event client.To.Open(...); } ``` ```csharp public void Update() { client.To.Open(...); // [OK? - May Not In Loop?] client.To.Data(...); // [OK? - May Not In Loop?] client.To.Event(...); // [OK? - May Not In Loop?] client.To.Close(...); // [OK? - May Not In Loop?] ws.On.Open(() => ...); // [BAD - Never In Loop] ws.On.Close(() => ... ); // [BAD - Never In Loop] ws.On.Data((bytes) => ... ); // [BAD - Never In Loop] ws.On.Error((exception) => ... ); // [BAD - Never In Loop] ws.On.Event((name, bytes) => ... ); // [BAD - Never In Loop] } ``` |