Netly version 4 is now available! Experience the new way of interacting with Netly. See more
⭐ Your star on Netly brightens our journey and makes a real impact! |
powered by ALEC1O
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 | KeZero sponsor Netly with hosting infrastructure and website domain. Netly receives JetBrains' IDE for free, Thanks to their generous sponsorship. |
| Supporter |
|
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 | Latest |
| 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 4.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 |
|
|---|---|
| Dependencies | |
| Build | # 1. clone project $ gitclone"https://github.com/alec1o/Netly"netly# 2. build project $ dotnetbuild"netly/" -cRelease -o"netly/bin/"# NOTE:# Netly.dll require Byter.dll because is Netly dependency# Netly.dll and Byter.dll have on build folder <netly-path>/bin/ |
| Features |
|
Code highlights
| TCP | 📄 ClientusingNetly;TCP.Clientclient=newTCP.Client(framing:true);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 enabledprintf("validate ssl/tls certificate");// return true if certificate is validreturntrue;});// open connection if closedclient.To.Open(newHost("127.0.0.1",8080));// close connection if openedclient.To.Close();// send raw data if connectedclient.To.Data(newbyte[2]{128,255});client.To.Data("hello world",NE.Encoding.UTF8);// send event if connectedclient.To.Event("name",newbyte[2]{128,255});client.To.Event("name","hello world",NE.Encoding.UTF8);// enable encryption (must call before client.To.Open)client.To.Encryption(true);📄 ServerusingNetly;TCP.Serverserver=newTCP.Server(framing:true);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.");});// open connectionserver.To.Open(newHost("1.1.1.1",1111));// close connectionserver.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 clientserver.To.DataBroadcast("text buffer");server.To.DataBroadcast(newbyte[]{1,2,3});// broadcast event (netly event) for all connected clientserver.To.EventBroadcast("event name","text buffer");server.To.EventBroadcast("event name",newbyte[]{1,2,3}); |
|---|---|
| UDP | 📄 ClientusingNetly;UDP.Clientclient=newUDP.Client();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.");});// open connection if closedclient.To.Open(newHost("127.0.0.1",8080));// close connection if openedclient.To.Close();// send raw data if connectedclient.To.Data(newbyte[2]{128,255});client.To.Data("hello world",NE.Encoding.UTF8);// send event if connectedclient.To.Event("name",newbyte[2]{128,255});client.To.Event("name","hello world",NE.Encoding.UTF8);📄 ServerusingNetly;UDP.Serverserver=newUDP.Server();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");});});// open connectionserver.To.Open(newHost("127.0.0.1",8080));// close connectionserver.To.Close();// broadcast raw data for all connected clientserver.To.DataBroadcast("text buffer");server.To.DataBroadcast(newbyte[]{1,2,3});// broadcast event (netly event) for all connected clientserver.To.EventBroadcast("event name","text buffer");server.To.EventBroadcast("event name",newbyte[]{1,2,3}); |
| HTTP | 📄 ClientusingNetly;HTTP.Clientclient=newHTTP.Client();// add http header for requestclient.Headers.Add("Content-Type","json");client.Headers.Add("Token","ImGui.h");// add http url queries e.g: https://www.alec1o.com/?page=about&version=4client.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 requestingboolisFetching=client.IsOpened;HttpClienthttp=null;// called before try connect to server// modify the HttpClient objectclient.On.Modify((HttpClientinstance)=>{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.<foo>==<bar>){ ...}});// erro on fetch, it can be timeout or whatever error// but if you receives error it mean the operation is called or doneclient.On.Error((Exceptionexception)=>{Ny.Logger.PushError(exception);});// connection is closed with fetch server.client.On.Close(()=>{if(http.<bar>==<foo>){ ...}});// used to fetch a serverclient.To.Open("method e.g GET","url","body, allow null");// used for cancel opened requestclient.To.Close();📄 ServerusingNetly;HTTP.Serverserver=newHTTP.Server();// return true if server is serve http contextboolisServe=server.IsOpened;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 connectionserver.To.Open(newUri("http://127.0.0.1:8080/"));// Close http server connectionserver.To.Close();// Map pathserver.Map.Get("/",async(req,res)=>{// Handle async: GET}) server.Map.Post("/user",(req,res)=>{// Handle sync: POST});// map using dynamic URLserver.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*//* Note: Middlewares is executed in added order. BUT, LOCAL MIDDLEWARE HAVE PRIORITY THAN GLOBAL MIDDLEWARE*/// Global Middleware (*don't have workflow path)server.Middleware.Add(async(req,res,next)=>{// verify request timerStopwatchwatch=newStopwatch();// init timernext();// call another middleware.watch.Stop();// stop timerres.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 middlewarenext();// 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");awaitres.Send(404,"{'error': 'invalid request.' }");// skip other middlewares:// next();}});// Register parse middlewareserver.Middleware.Add((request,response,next)=>{if(request.Enctype==HTTP.Enctype.Json){request.Body.RegisterParse(true,(Typetype)=>{// e.g. using dotnet >= 6 @System.Text.JsonreturnJsonSerializer.Deserialize(request.Body.Text,type);});} ...next();});// Usage of body parser.server.Map.Post("/register",(request,response)=>{vardata=request.Body.Parse<MyLoginInput>(); ...}); |
| RUDP | 📄 ClientusingNetly;RUDP.Clientclient=newRUDP.Client();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.");});// open connection if closedclient.To.Open(newHost("127.0.0.1",8080));// close connection if openedclient.To.Close();// send raw data if connectedclient.To.Data(newbyte[2]{128,255},RUDP.Unreliable);client.To.Data("hello world",NE.Encoding.UTF8,RUDP.Reliable);// send event if connectedclient.To.Event("name",newbyte[2]{128,255},RUDP.Unreliable);client.To.Event("name","hello world",NE.Encoding.UTF8,RUDP.Reliable);📄 ServerusingNetly;RUDP.Serverserver=newRUDP.Server();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){ ...}elseif(type==RUDP.Unreliable){ ...}else{ ...}/* type == RUDP.Sequenced */printf("client received a raw data");});client.On.Event((name,type)=>if(type==RUDP.Reliable){ ...}elseif(type==RUDP.Unreliable){ ...}else{ ...}/* type == RUDP.Sequenced */printf("client received a event");});});// open connectionserver.To.Open(newHost("127.0.0.1",8080));// close connectionserver.To.Close();// broadcast raw data for all connected clientserver.To.DataBroadcast("text buffer",RUDP.Unreliable);server.To.DataBroadcast(newbyte[]{1,2,3},RUDP.Reliable);server.To.DataBroadcast(newbyte[]{3,2,1},RUDP.Sequenced);// broadcast event (netly event) for all connected clientserver.To.EventBroadcast("event name","text buffer",RUDP.Unreliable);server.To.EventBroadcast("event name",newbyte[]{1,2,3},RUDP.Reliable);server.To.EventBroadcast("event name",newbyte[]{3,2,1},RUDP.Sequenced); |
| WebSocket | 📄 ClientusingNetly;HTTP.WebSocketclient=newHTTP.WebSocket();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){ ...}elseif(type==HTTP.Text){ ...}else{/* NOTE: it's imposible */}// raw data received from server});client.On.Event((name,bytes,type)=>{if(type==HTTP.Binary){ ...}elseif(type==HTTP.Text){ ...}else{/* NOTE: it's imposible */}// event received from server});client.On.Modify((wsSocket)=>{// modify websocket socket});// open websocket client connectionclient.To.Open(newUri("ws://127.0.0.1:8080/echo"));// close websocket client connectionclient.To.Close();// send raw data for server// text messageclient.To.Data("my message",HTTP.Text);// binnary messageclient.To.Data(NE.GetBytes("my buffer"),HTTP.Binary);// send event (netly event) for server// text messageclient.To.Event("event name","my message",HTTP.Text);// binnary messageclient.To.Data("event name",NE.GetBytes("my buffer"),HTTP.Binary);📄 ServerusingNetly;usingNetly.Interfaces;HTTP.Serverserver=newHTTP.Server();IHTTP.WebSocket[]Clients=server.WebSocketClients;server.Map.WebSocket("/chat/{token}",async(req,ws)=>{// Accept websocket from dynamic pathstringtoken=req.Params["token"];// validate websocket connection from paramsif(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 pathws.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){ ...}elseif(type==HTTP.Text){ ...}else{/* NOTE: it's imposible */}// server-side websocket received raw data});ws.On.Event((name,bytes,type)=>{if(type==HTTP.Binary){ ...}elseif(type==HTTP.Text){ ...}else{/* NOTE: it's imposible */}// server-side websocket received event});});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 connectionserver.To.Open(newUri("http://127.0.0.1:8080/"));// Close http server connectionserver.To.Close();// open websocket client connectionclient.To.Open(newUri("ws://127.0.0.1:8080/echo"));// close websocket client connectionclient.To.Close();// broadcast raw data for all connected websocket socket// text messageserver.To.WebsocketDataBroadcast("my message",HTTP.Text);// binnary messageserver.To.WebsocketDataBroadcast(NE.GetBytes("my buffer"),HTTP.Binary);// broadcast event (netly event) for all connected websocket socket// text messageserver.To.WebsocketEventBroadcast("event name","my message",HTTP.Text);// binnary messageserver.To.WebsocketEventBroadcast("event name",NE.GetBytes("my buffer"),HTTP.Binary); |
| Byter | For more information and details see Byter's official information
📄 PrimitiveusingByter;
Primitive can serialize/deserialize complex data, e.g. (T[], List, Class, Struct, Enum).
📄 ExtensionusingByter;
|
Integration and interaction example codes
| Standard | 📄 ConsoleusingSystem;usingNetly;publicclassProgram{privatestaticvoidMain(string[]args){UDP.Clientclient=newUDP.Client();client.On.Open(()=>{Console.WriteLine(<some-text-here>);};client.On.Close(()=>{Console.WriteLine(<some-text-here>);};client.On.Error((exception)=>{Console.WriteLine(<some-text-here>);};while(true){if(!client.IsOpened){client.To.Open(newHost("1.1.1.1",1111));}else{Console.WriteLine("Message: ");stringmessage=Console.ReadLine();client.To.Data(message??"No message.",NE.Encoding.UTF8);}}}} |
|---|---|
| Flax Engine | 📄 ScriptusingSystem;usingFlaxEngine;usingNetly;publicclassExample:Script{publicstringmessage;internalUDP.Clientclient;publicoverridevoidAwake(){client=newUDP.Client();client.On.Open(()=>{Debug.Log(<some-text-here>);};client.On.Close(()=>{Debug.Log(<some-text-here>);};client.On.Error((exception)=>{Debug.Log(<some-text-here>);};}publicoverridevoidStart(){client.To.Open(newHost("1.1.1.1",1111));}publicoverridevoidUpdate(){if(!client.IsOpened){client.To.Open(newHost("1.1.1.1",1111));}else{if(Input.GetKeyDown(KeyCode.Space)){client.To.Data(message??"No message.",NE.Encoding.UTF8);}}}} |
| Unity Engine | 📄 MonoBehaviourusingSystem;usingFlaxEngine;usingNetly;publicclassExample:MonoBehaviour{publicstringmessage;internalUDP.Clientclient;privatevoidAwake(){client=newUDP.Client();client.On.Open(()=>{Debug.Log(<some-text-here>);};client.On.Close(()=>{Debug.Log(<some-text-here>);};client.On.Error((exception)=>{Debug.Log(<some-text-here>);};}privatevoidStart(){client.To.Open(newHost("1.1.1.1",1111));}privatevoidUpdate(){if(!client.IsOpened){client.To.Open(newHost("1.1.1.1",1111));}else{if(Input.GetKeyDown(KeyCode.Space)){client.To.Data(message??"No message.",NE.Encoding.UTF8);}}}} |
| WARNING: | Initialize event handlers once, not in loops. Set up handlers with `..On.` methods in initialization methods like `Awake()` or `Start()`. Avoid repeatedly setting these up in update loops to maintain performance. Handle protocol actions wisely. Use `..To.` methods, such as `..To.Open()`, `..To.Data()`, and `..To.Close()`, with careful management. Ensure you only open a connection when it's not already open and send data only when the connection is confirmed as active. Avoid calling these methods in tight loops. // OK 100% RecommendedprivatevoidStart(){varclient= ...;client.On.Open(()=> ...);// e.g generic handlerclient.On.Open(()=> ...);// e.g only to send "Hi"client.On.Event((name,bytes,?)=> ...);// e.g generic event handlerclient.On.Event((name,bytes,?)=> ...);// e.g only to handle A eventclient.On.Event((name,bytes,?)=> ...);// e.g only to handle B eventclient.To.Open(...);}publicvoidUpdate(){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]} |

KeZero sponsor Netly with hosting infrastructure and website domain.
Netly receives JetBrains' IDE for free, Thanks to their generous sponsorship.