NTDLS.StreamFraming
1.2.7
dotnet add package NTDLS.StreamFraming --version 1.2.7
NuGet\Install-Package NTDLS.StreamFraming -Version 1.2.7
<PackageReference Include="NTDLS.StreamFraming" Version="1.2.7" />
paket add NTDLS.StreamFraming --version 1.2.7
#r "nuget: NTDLS.StreamFraming, 1.2.7"
// Install NTDLS.StreamFraming as a Cake Addin #addin nuget:?package=NTDLS.StreamFraming&version=1.2.7 // Install NTDLS.StreamFraming as a Cake Tool #tool nuget:?package=NTDLS.StreamFraming&version=1.2.7
NTDLS.StreamFraming
📦 Be sure to check out the NuGet pacakge: https://www.nuget.org/packages/NTDLS.StreamFraming
NTDLS.StreamFraming is a set of extension methods for a Stream (typically TCPIP/NetworkStream) that enables reliable framing, compression, optional encryption, two-way communication and support for asynchronous query/reply. Messages are guaranteed to be received in their entirety and in the order which they were sent.
Sending a notification frame:
Here we are using an established TcpClient connection, getting its stream and then calling WriteNotification() to pacakge the payload "MyMessage" and write it to the stream. As per TCP/IP protocol, this can and will be received fragmented and/or concatenated with other bytes that are sent in the stream... but don't worry thats what NTDLS.StreamFraming is here to solve.
using (var tcpStream = tcpClient.GetStream())
{
while (tcpClient.Connected)
{
string text = $"This is a message that was sent at {DateTime.Now.ToLongTimeString()}.";
//Assemble a message frame and write it to the stream:
tcpStream.WriteNotification(new MyMessage(text));
Thread.Sleep(1000);
}
tcpStream.Close();
}
Receiving a notification frame:
Here we are using an established TcpClient connection, getting its stream and then calling ReadAndProcessFrames(). ReadAndProcessFrames will read from the stream and determine if the bytes that are received (if any) are a full frame, only a fragment or a concatenation of multiple packets and fragments. Any full frames will be split, validated, decompressed, deserialized and then the callback "ProcessFrameNotificationCallback" will be called for each valid frame.
var frameBuffer = new FrameBuffer();
using (var tcpStream = _tcpClient.GetStream())
{
while (_tcpClient.Connected)
{
//Read from the stream, assemble the bytes into the original messages and call
// the handler for each message that was received.
if (tcpStream.ReadAndProcessFrames(frameBuffer, ProcessFrameNotificationCallback) == false)
{
//If ReadAndProcessFrames() returns false then we have been disconnected.
break;
}
}
tcpStream.Close();
}
//This is the handler for a frame that was written to the stream with a call to WriteNotification().
//Note that the origianl class is received and we use patter matching to determine what we are receiving.
private void ProcessFrameNotificationCallback(IFrameNotification payload)
{
//We recevied a message, see if it is of the type "MyMessage".
if (payload is MyMessage myMessage)
{
Console.WriteLine($"Received from server: '{myMessage.Text}'");
}
}
Sending a query frame:
Here we are using an established TcpClient connection, getting its stream and then calling WriteQuery() to pacakge the payload "MyQuery" and write it to the stream. We will then "wait" asynchronously or the client to receive and reply to the query.
using (var tcpStream = tcpClient.GetStream())
{
while (tcpClient.Connected)
{
tcpStream.WriteQuery<MyQueryReply>(new MyQuery("Hello client!")).ContinueWith((o) =>
{
if (o.IsCompletedSuccessfully && o.Result != null)
{
Console.WriteLine($"Received [QueryReply] from client: '{o.Result.Text}')");
}
});
//In this example, we have to call ReadAndProcessFrames() even though we are not
// supplying it with any callbacks because it receives the replies from the query
// that is sent above and routes them to the correct query task handler.
if (tcpStream.ReadAndProcessFrames(frameBuffer) == false)
{
break; //The client disconnected.
}
Thread.Sleep(1000);
}
tcpStream.Close();
}
Receiving a query frame and replying:
Here we are using an established TcpClient connection, getting its stream and then calling ReadAndProcessFrames(). ReadAndProcessFrames will read from the stream and determine if the bytes that are received are a full frame. Any full frames will be deserialized and routed to the callbacks "ProcessFrameNotificationCallback" or "ProcessFrameQueryCallback" based on their types. "ProcessFrameNotificationCallback" expects a return value to be of the type specified on the original call to WriteQuery(). The value returned from ProcessFrameQueryCallback will be framed and sent back to the originator and then routed to the appropriate waiting asynchronous task.
var frameBuffer = new FrameBuffer();
using (var tcpStream = _tcpClient.GetStream())
{
while (_tcpClient.Connected)
{
//Read from the stream, assemble the bytes into the original messages and call
// the handler for each message that was received.
if (tcpStream.ReadAndProcessFrames(frameBuffer, ProcessFrameNotificationCallback, ProcessFrameQueryCallback) == false)
{
//If ReadAndProcessFrames() returns false then we have been disconnected.
break;
}
}
tcpStream.Close();
}
//This is the handler for a frame that was written to the stream with a call to WriteNotification().
//Note that the origianl class is received and we use patter matching to determine what we are receiving.
private void ProcessFrameNotificationCallback(IFrameNotification payload)
{
//We recevied a message, see if it is of the type "MyMessage".
if (payload is MyMessage myMessage)
{
Console.WriteLine($"Received from server: '{myMessage.Text}'");
}
}
private IFrameQueryReply ProcessFrameQueryCallback(IFrameQuery payload)
{
//We recevied a message, see if it is of the type "MyQuery", if so reply with the type of MyQueryReply.
if (payload is MyQuery myQuery)
{
Console.WriteLine($"Received [Query] from server: '{myQuery.Text}'");
return new MyQueryReply("Hello Server!");
}
throw new Exception("The query type was unhandled.");
}
Supporting Classes:
These are the payload classes that are used in the examples:
Used in the examples to send a one-way notification
internal class MyMessage : IFrameNotification
{
public string Text { get; set; }
public MyMessage(string text)
{
Text = text;
}
}
Used in the examples to send a query that expects a reply:
internal class MyQuery : IFrameQuery
{
public string Text { get; set; }
public MyQuery(string text)
{
Text = text;
}
}
Used in the examples to reply to a received query:
internal class MyQueryReply : IFrameQueryReply
{
public string Text { get; set; }
public MyQueryReply(string text)
{
Text = text;
}
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net6.0
- Newtonsoft.Json (>= 13.0.3)
- NTDLS.Semaphore (>= 3.3.0)
- protobuf-net (>= 3.2.30)
-
net7.0
- Newtonsoft.Json (>= 13.0.3)
- NTDLS.Semaphore (>= 3.3.0)
- protobuf-net (>= 3.2.30)
-
net8.0
- Newtonsoft.Json (>= 13.0.3)
- NTDLS.Semaphore (>= 3.3.0)
- protobuf-net (>= 3.2.30)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on NTDLS.StreamFraming:
Package | Downloads |
---|---|
NTDLS.MemoryQueue
In memory non-persistent message queue with notifications/messages, query/reply support and several message boradcast schemes. Intended for inter-process-communication, queuing, load-balancing and buffering over TCP/IP. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.2.7 | 108 | 6/20/2024 |
1.2.6 | 737 | 1/22/2024 |
1.2.5 | 199 | 1/4/2024 |
1.2.4 | 146 | 1/3/2024 |
1.2.3 | 192 | 12/27/2023 |
1.2.2 | 138 | 12/22/2023 |
1.2.1 | 286 | 11/15/2023 |
1.2.0 | 130 | 11/12/2023 |
1.1.0 | 168 | 11/10/2023 |
1.0.6 | 114 | 11/10/2023 |
1.0.5 | 166 | 11/8/2023 |
1.0.4 | 178 | 11/7/2023 |
1.0.3 | 110 | 11/6/2023 |
1.0.2 | 144 | 11/6/2023 |
1.0.1 | 124 | 11/6/2023 |
1.0.0 | 116 | 11/6/2023 |
License fix.