Basics of working with pipes in C# .NET part 6: message compression

In the previous part we looked at a possible solution to transmit objects through a pipe from the client to a server. We saw that it was not much different compared to transmitting a string message as the object had to be serialised first.

In this post we’ll consider a way to reduce the message size by compressing it. In messaging you should always aim for short and concise messages for good speed and scaling. However, sometimes the messages are simply large and you cannot do much about it. E.g. consider a service that scans the contents of a web page and returns the HTML as a string along with some measurements for every element on the page, such as CSS and JS files etc: time to first byte, header receive time, header size etc. That can be a large piece of string.

We’ll reuse a technique outlined in this post dedicated to BZip2 string compression. If you don’t know how that works then read through that article, it’s very short.

You’ll need to import the following NuGet package to use BZip2 in the pipe server in client console applications:

sharpziplib nuget

Here’s the pipe client code to send a BZip2 compressed message to the pipe server:

private static void SendCompressedMessageToServer()
{
	using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream("test"))
	{
		string longMessage = "This is a large piece of text";
		string compressed = string.Empty;
		using (MemoryStream source = new MemoryStream(Encoding.UTF8.GetBytes(longMessage)))
		{
			using (MemoryStream target = new MemoryStream())
			{
				BZip2.Compress(source, target, true, 4096);
				byte[] targetByteArray = target.ToArray();
				compressed = Convert.ToBase64String(targetByteArray);
			}
		}
		namedPipeClient.Connect();
		byte[] messageBytes = Encoding.UTF8.GetBytes(compressed);
		namedPipeClient.Write(messageBytes, 0, messageBytes.Length);
	}
}

…and here’s how the server can handle the message:

private static void ReceiveCompressedMessageFromClient()
{
	using (NamedPipeServerStream namedPipeServer = new NamedPipeServerStream("test", PipeDirection.InOut,
		1, PipeTransmissionMode.Message))
	{
		namedPipeServer.WaitForConnection();
		StringBuilder messageBuilder = new StringBuilder();
		string messageChunk = string.Empty;
		byte[] messageBuffer = new byte[5];
		do
		{
			namedPipeServer.Read(messageBuffer, 0, messageBuffer.Length);
			messageChunk = Encoding.UTF8.GetString(messageBuffer);
			messageBuilder.Append(messageChunk);
			messageBuffer = new byte[messageBuffer.Length];
		}
		while (!namedPipeServer.IsMessageComplete);
		string fullMessage = messageBuilder.ToString().Trim('\0');
		byte[] largeCompressedTextAsBytes = Convert.FromBase64String(fullMessage);
		using (MemoryStream source = new MemoryStream(largeCompressedTextAsBytes))
		{
			using (MemoryStream target = new MemoryStream())
			{
				BZip2.Decompress(source, target, true);
				string uncompressedString = Encoding.UTF8.GetString(target.ToArray());
				Console.WriteLine(uncompressedString);
			}
		}
	}
}

We compress the string using the BZip2 algorithm and then convert that to a base 64 string in the client. The server performs the exact opposite. You may be wondering about this bit:

string fullMessage = messageBuilder.ToString().Trim('\0');

The character described in the Trim argument is the string termination or null-termination character that marks the end of the message. That’s how the pipe server and client can determine whether there’s anything remaining of an incoming message.

View the list of posts on Messaging here.

Advertisement

Basics of working with pipes in C# .NET part 5: transmitting objects

In the previous part we saw a very basic chat application between a client and a server through a pipe. In this post we’ll see a way of how to transmit objects from the client to the server.

You’ll see that it really is not much different from transmitting a message. We cannot simply transmit an object using e.g. a WriteObject method. Instead the object must be serialised at the client side and deserialised at the server side. We’ll serialise our object using JSON and transmit the object properties using the message transmission technique we’ve seen earlier. The server will then deserialise the JSON string into the object.

We’ll pretend that the client wants to transmit an order:

public class Order
{
	public string ProductName { get; set; }
	public int Quantity { get; set; }
	public string CustomerName { get; set; }
	public string Address { get; set; }
}

Read more of this post

Basics of working with pipes in C# .NET part 4: basic conversation with messages

In this post we saw how a pipe stream client and server can send each other single bytes. In this post we managed to send a single message from the client to the server. It’s time to marry the two concepts. We’ll let the client and server start a conversation.

The server side code has nothing new compared to the posts referred to above:

Read more of this post

Basics of working with pipes in C# .NET part 3: message transmission

So far in the posts on pipe streams in .NET we’ve been considering the transmission of single bytes. That’s not really enough for a real-world messaging application. In this post we’ll extend our discussion to complete messages.

We have to explicitly indicate that we want to process messages in the NamedPipeServerStream constructor. Also, we’ll need to read the messages in chunks. A message is represented by a byte array and we don’t know in advance how long the message will be. Fortunately the NamedPipeServerStream object has an IsMessageComplete property which we’ll be able to use in the do-while loop:

Read more of this post

Basics of working with pipes in C# .NET part 2: continuous byte communication

In this post we briefly introduced how interprocess communication pipes are represented in .NET. The server sent a single byte to the client and the client sent a single byte in response. We’ll add some more flesh to the code but still keep it very simple. We’ll let the client and server send each other individual bytes as messages. Obviously that is still very childish but it will do for demo purposes. We’ll continue with proper messages in the next post.

Here’s the extended server code with a lot more output. We read a line of input from the console but only transmit the very first byte. We wait for the client’s response. The byte value of ‘x’, i.e. 120 will indicate that the client wants to end the communication:

Read more of this post

Basics of working with pipes in C# .NET part 1: send and receive a single byte

Pipes are used for interprocess communication. Typically there’s a single pipe server that one or more clients can connect to and exchange messages.

There are named and anonymous pipes. Anonymous pipes come with a couple of limitations compared to named pipes:

  • They are one-way only i.e. the server and client cannot exchange messages. Communication where both the server and client are allowed to send messages is called duplex
  • Anonymous pipes only allow communication with a single client. Named pipes provide a multi-client scenario
  • Anonymous pipes cannot function over the network. They are limited to the same machine. Named pipes have no such limitation

Read more of this post

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: