{"id":26155469,"url":"https://github.com/ntdls/ntdls.streamframing","last_synced_at":"2026-02-11T14:32:21.186Z","repository":{"id":205799816,"uuid":"715250407","full_name":"NTDLS/NTDLS.StreamFraming","owner":"NTDLS","description":"Stream wrapper (typically TCPIP/NetworkStream) that enables reliable framing, compression, optional encryption and two way communication.","archived":false,"fork":false,"pushed_at":"2025-11-14T02:56:16.000Z","size":168,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-14T03:10:45.103Z","etag":null,"topics":["io","nuget","streams"],"latest_commit_sha":null,"homepage":"https://networkdls.com/Entity/ntdls-streamframing","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/NTDLS.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,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-11-06T19:08:25.000Z","updated_at":"2025-11-14T02:56:12.000Z","dependencies_parsed_at":null,"dependency_job_id":"97de2ea8-cb21-400d-805d-0d3e16e9008d","html_url":"https://github.com/NTDLS/NTDLS.StreamFraming","commit_stats":null,"previous_names":["ntdls/ntdls.streamframing"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/NTDLS/NTDLS.StreamFraming","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NTDLS%2FNTDLS.StreamFraming","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NTDLS%2FNTDLS.StreamFraming/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NTDLS%2FNTDLS.StreamFraming/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NTDLS%2FNTDLS.StreamFraming/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NTDLS","download_url":"https://codeload.github.com/NTDLS/NTDLS.StreamFraming/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NTDLS%2FNTDLS.StreamFraming/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29335219,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-11T14:07:45.431Z","status":"ssl_error","status_checked_at":"2026-02-11T14:07:45.080Z","response_time":97,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["io","nuget","streams"],"created_at":"2025-03-11T08:56:24.954Z","updated_at":"2026-02-11T14:32:21.171Z","avatar_url":"https://github.com/NTDLS.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NTDLS.StreamFraming\r\n\r\n📦 Be sure to check out the NuGet pacakge: https://www.nuget.org/packages/NTDLS.StreamFraming\r\n\r\nNTDLS.StreamFraming is a set of extension methods for a Stream (typically TCPIP/NetworkStream) that\r\nenables reliable framing, compression, optional encryption, two-way communication and support for\r\nasynchronous query/reply. Messages are guaranteed to be received in their entirety and in the order\r\nwhich they were sent.\r\n\r\n\r\n## Sending a notification frame:\r\n\u003e Here we are using an established TcpClient connection, getting its stream and then calling\r\n\u003e WriteNotification() to pacakge the payload \"MyMessage\" and write it to the stream.\r\n\u003e As per TCP/IP protocol, this can and will be received fragmented and/or concatenated with\r\n\u003e other bytes that are sent in the stream... but don't worry thats what NTDLS.StreamFraming\r\n\u003e is here to solve.\r\n```csharp\r\nusing (var tcpStream = tcpClient.GetStream())\r\n{\r\n    while (tcpClient.Connected)\r\n    {\r\n        string text = $\"This is a message that was sent at {DateTime.Now.ToLongTimeString()}.\";\r\n\r\n        //Assemble a message frame and write it to the stream:\r\n        tcpStream.WriteNotification(new MyMessage(text));\r\n\r\n        Thread.Sleep(1000);\r\n    }\r\n    tcpStream.Close();\r\n}\r\n```\r\n\r\n## Receiving a notification frame:\r\n\u003e Here we are using an established TcpClient connection, getting its stream and then calling\r\n\u003e ReadAndProcessFrames(). ReadAndProcessFrames will read from the stream and determine if the\r\n\u003e bytes that are received (if any) are a full frame, only a fragment or a concatenation of\r\n\u003e multiple packets and fragments. Any full frames will be split, validated, decompressed,\r\n\u003e deserialized and then the callback \"ProcessFrameNotificationCallback\" will be called for each valid frame.\r\n```csharp\r\nvar frameBuffer = new FrameBuffer();\r\n\r\nusing (var tcpStream = _tcpClient.GetStream())\r\n{\r\n    while (_tcpClient.Connected)\r\n    {\r\n        //Read from the stream, assemble the bytes into the original messages and call\r\n        //  the handler for each message that was received.\r\n        if (tcpStream.ReadAndProcessFrames(frameBuffer, ProcessFrameNotificationCallback) == false)\r\n        {\r\n            //If ReadAndProcessFrames() returns false then we have been disconnected.\r\n            break;\r\n        }\r\n    }\r\n    tcpStream.Close();\r\n}\r\n\r\n//This is the handler for a frame that was written to the stream with a call to WriteNotification().\r\n//Note that the origianl class is received and we use patter matching to determine what we are receiving.\r\nprivate void ProcessFrameNotificationCallback(IFrameNotification payload)\r\n{\r\n    //We recevied a message, see if it is of the type \"MyMessage\".\r\n    if (payload is MyMessage myMessage)\r\n    {\r\n        Console.WriteLine($\"Received from server: '{myMessage.Text}'\");\r\n    }\r\n}\r\n```\r\n\r\n\r\n## Sending a query frame:\r\n\u003e Here we are using an established TcpClient connection, getting its stream and then calling\r\n\u003e WriteQuery() to pacakge the payload \"MyQuery\" and write it to the stream. We will then \"wait\"\r\n\u003e asynchronously or the client to receive and reply to the query. \r\n```csharp\r\nusing (var tcpStream = tcpClient.GetStream())\r\n{\r\n    while (tcpClient.Connected)\r\n    {\r\n        tcpStream.WriteQuery\u003cMyQueryReply\u003e(new MyQuery(\"Hello client!\")).ContinueWith((o) =\u003e\r\n            {\r\n                if (o.IsCompletedSuccessfully \u0026\u0026 o.Result != null)\r\n                {\r\n                    Console.WriteLine($\"Received [QueryReply] from client: '{o.Result.Text}')\");\r\n                }\r\n            });\r\n\r\n        //In this example, we have to call ReadAndProcessFrames() even though we are not\r\n        //  supplying it with any callbacks because it receives the replies from the query\r\n        //  that is sent above and routes them to the correct query task handler.\r\n        if (tcpStream.ReadAndProcessFrames(frameBuffer) == false)\r\n        {\r\n            break; //The client disconnected.\r\n        }\r\n\r\n        Thread.Sleep(1000);\r\n    }\r\n    tcpStream.Close();\r\n}\r\n```\r\n\r\n## Receiving a query frame and replying:\r\n\u003e Here we are using an established TcpClient connection, getting its stream and then calling\r\n\u003e ReadAndProcessFrames(). ReadAndProcessFrames will read from the stream and determine if the\r\n\u003e bytes that are received are a full frame. Any full frames will be deserialized and routed\r\n\u003e to the callbacks \"ProcessFrameNotificationCallback\" or \"ProcessFrameQueryCallback\" based\r\n\u003e on their types. \"ProcessFrameNotificationCallback\" expects a return value to be of the type\r\n\u003e specified on the original call to WriteQuery(). The value returned from ProcessFrameQueryCallback\r\n\u003e will be framed and sent back to the originator and then routed to the appropriate waiting\r\n\u003e asynchronous task.\r\n```csharp\r\nvar frameBuffer = new FrameBuffer();\r\n\r\nusing (var tcpStream = _tcpClient.GetStream())\r\n{\r\n    while (_tcpClient.Connected)\r\n    {\r\n        //Read from the stream, assemble the bytes into the original messages and call\r\n        //  the handler for each message that was received.\r\n        if (tcpStream.ReadAndProcessFrames(frameBuffer, ProcessFrameNotificationCallback, ProcessFrameQueryCallback) == false)\r\n        {\r\n            //If ReadAndProcessFrames() returns false then we have been disconnected.\r\n            break;\r\n        }\r\n    }\r\n    tcpStream.Close();\r\n}\r\n\r\n//This is the handler for a frame that was written to the stream with a call to WriteNotification().\r\n//Note that the origianl class is received and we use patter matching to determine what we are receiving.\r\nprivate void ProcessFrameNotificationCallback(IFrameNotification payload)\r\n{\r\n    //We recevied a message, see if it is of the type \"MyMessage\".\r\n    if (payload is MyMessage myMessage)\r\n    {\r\n        Console.WriteLine($\"Received from server: '{myMessage.Text}'\");\r\n    }\r\n}\r\n\r\nprivate IFrameQueryReply ProcessFrameQueryCallback(IFrameQuery payload)\r\n{\r\n    //We recevied a message, see if it is of the type \"MyQuery\", if so reply with the type of MyQueryReply.\r\n    if (payload is MyQuery myQuery)\r\n    {\r\n        Console.WriteLine($\"Received [Query] from server: '{myQuery.Text}'\");\r\n\r\n        return new MyQueryReply(\"Hello Server!\");\r\n    }\r\n\r\n    throw new Exception(\"The query type was unhandled.\");\r\n}\r\n```\r\n\r\n## Supporting Classes:\r\n\u003e These are the payload classes that are used in the examples:\r\n\r\n**Used in the examples to send a one-way notification**\r\n```csharp\r\n    internal class MyMessage : IFrameNotification\r\n    {\r\n        public string Text { get; set; }\r\n\r\n        public MyMessage(string text)\r\n        {\r\n            Text = text;\r\n        }\r\n    }\r\n```\r\n\r\n**Used in the examples to send a query that expects a reply:**\r\n```csharp\r\ninternal class MyQuery : IFrameQuery\r\n{\r\n    public string Text { get; set; }\r\n\r\n    public MyQuery(string text)\r\n    {\r\n        Text = text;\r\n    }\r\n}\r\n```\r\n\r\n**Used in the examples to reply to a received query:**\r\n```csharp\r\ninternal class MyQueryReply : IFrameQueryReply\r\n{\r\n    public string Text { get; set; }\r\n\r\n    public MyQueryReply(string text)\r\n    {\r\n        Text = text;\r\n    }\r\n}\r\n```\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fntdls%2Fntdls.streamframing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fntdls%2Fntdls.streamframing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fntdls%2Fntdls.streamframing/lists"}