Benj Benj - 1 month ago 15
C# Question

Is there an automatic endianness detection in BinaryReader

I am developing a client that generates and transmits bitmaps over a TCP connection using a very simple protocol:


  • [Step 1] Send file-size n in bytes as Int64

  • [Step 2] Send n bytes that represent the Bitmap-Data



To assist me during development I implemented a simple server that waits for a connection, accepts files while the client sends and stores them on the HDD using a randomly generated filename

using System;
using System.Text;
using System.IO;
using System.Net.Sockets;

namespace SimplePictureReceiverServer
{
class Program
{
private static Int32 PORT = -1;
private static String DirSaveTo = null;

static void Main(string[] args)
{
// Ensuring that required parameters have been provided
if (args.Length < 2) { ShowUsage("Parameter-Count"); return; }
if (!Int32.TryParse(args[0], out PORT)) { ShowUsage("<port>"); return; }
if (!Directory.Exists(args[1])) { ShowUsage("<dir>"); return; }

PORT = Int32.Parse(args[0]);
DirSaveTo = args[1];

// Setting up the Server on <127.0.0.1>:<PORT>
TcpClient c = null; NetworkStream s;
TcpListener l = new TcpListener(System.Net.IPAddress.Loopback, PORT);
l.Start();

while(true)
{
// Waiting for a Client to connect
Console.Write("Waiting ... ");
c = l.AcceptTcpClient();
s = c.GetStream();
Console.WriteLine("Ok");

// Handle the client
try
{
using (BinaryReader r = new BinaryReader(s, Encoding.Unicode))
{
HandleClient(r);
}
}
catch(Exception e)
{
Console.WriteLine("Client disconnected (" + e.Message + ")");
Console.WriteLine();
}
}
}

private static void ShowUsage(String s)
{
Console.WriteLine("Problem with " + s);
Console.WriteLine();
Console.WriteLine("USAGE \t program.exe <port> <dir>");
Console.WriteLine("<port> : Port to listen to when waiting for connections");
Console.WriteLine("<dir> : Directory to store received pictures at. The directory must exist");
Console.WriteLine();
Console.WriteLine("Press [ENTER]");
Console.ReadLine();
}
private static void HandleClient(BinaryReader r)
{
const Int32 BUF_SIZE = 10 * 1024;
Int32 cntPicutresReceived = 0; StringBuilder b = new StringBuilder();

Random rnd = new Random();
Byte[] buf;

Int32 bytesLeftToRead;

while (true)
{
using (MemoryStream m = new MemoryStream())
{
// Reading picture size and picture from stream
bytesLeftToRead = Convert.ToInt32(r.ReadInt64()); // Reading size as Int32
// Little/ Big Endian - Consider !

Console.WriteLine("Expected filesize is " + bytesLeftToRead.ToString("#,##0") + " Bytes");

while (bytesLeftToRead > 0) // Reading the picture
{
buf = r.ReadBytes(Math.Min(BUF_SIZE, bytesLeftToRead));
m.Write(buf, 0, buf.Length);

bytesLeftToRead = bytesLeftToRead - buf.Length;
}

// Storing picture on HDD with a fancy filename created in the StringBuilder
m.Seek(0L, SeekOrigin.Begin);

b.Clear();
b.Append((++cntPicutresReceived).ToString("0000"));
b.Append("__");
b.Append(rnd.Next(Int32.MaxValue).ToString("X"));
b.Append(".bmp");

using (FileStream f = new FileStream(Path.Combine(DirSaveTo, b.ToString()), FileMode.Create))
{
m.CopyTo(f);
}

Console.WriteLine("Image saved at '" + Path.Combine(DirSaveTo, b.ToString()) + "'");
}
}
}
}
}


To test the server I then implemented a Test-Client that generates 5 random Bitmaps and sends them to the server

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net.Sockets;

namespace SimplePictureSender
{
class Program
{
static Random Random = new Random();

static void Main(string[] args)
{
Bitmap b;
TcpClient c = new TcpClient("127.0.0.1", 12345);
Stream s = c.GetStream();

using (BinaryWriter w = new BinaryWriter(s))
{
for (Int32 i = 0; i < 5; i++)
{
using (MemoryStream m = new MemoryStream())
{
b = CreateRandomBitmap(640, 480, 1000, 1000); b.Save(m, ImageFormat.Bmp);
Console.WriteLine("Picture size is " + m.Length + " Bytes"); Console.WriteLine();

m.Seek(0L, SeekOrigin.Begin);

w.Write(m.Length);
m.WriteTo(w.BaseStream);
}
}
}

Console.WriteLine("[ENTER] to exit");
Console.ReadLine();
}

private static Bitmap CreateRandomBitmap(Int32 minX = 100, Int32 minY = 100, Int32 maxDeltaX = 250, Int32 maxDeltaY = 250)
{
Console.Write("Generating Picture ... ");

Bitmap b = new Bitmap(Random.Next(minX, minX + maxDeltaX), Random.Next(minY, minY + maxDeltaY));
Color c;

for(Int32 x = 0; x < b.Width; x++)
for(Int32 y = 0; y < b.Height; y++)
{
c = Color.FromArgb(Random.Next(0, 256), Random.Next(0, 256), Random.Next(0, 256));
b.SetPixel(x, y, c);
}

Console.WriteLine("Done");

return b;
}
}
}


This all works good and well ... surprisingly too well.

If I replace the line
using (BinaryReader r = new BinaryReader(s, Encoding.Unicode))
on the server by one of the following


  • using (BinaryReader r = new BinaryReader(s, Encoding.BigEndianUnicode))

  • using (BinaryReader r = new BinaryReader(s))



the setup always succeeds transmitting and storing the bitmaps. However, I would expect at least one of these codes to fail, since the server should then not be able to correctly extract the size of the following data blob.

Why is it working? Is there an implicit Endianness-Detection? If now I would implement a client in C - would I need to worry about endianness? If not - what is my server expecting?

Answer

See BinaryReader Constructor.

The encoding parameter is called "character encoding" and apparently has no effect on how integers are written to the stream.

If your client and server have the same endian-ness, then the code will work no matter what the character encoding is since you aren't writing any characters to the stream, just bytes.

Comments