BinaryTextEncoding Class |
Namespace: Demo3D.IO
public abstract class BinaryTextEncoding
The BinaryTextEncoding type exposes the following members.
| Name | Description | |
|---|---|---|
| BinaryTextEncoding | Initializes a new instance of the BinaryTextEncoding class |
| Name | Description | |
|---|---|---|
| Default |
The default encoding is 2-byte big-endian length-encoded ASCII.
| |
| FixedLengthASCII |
An encoder that will encode and decode fixed length ASCII string.
| |
| FixedLengthUnicode |
An encoder that will encode and decode fixed length Unicode string.
| |
| LengthEncodedASCII1 |
Returns an encoder that will encode and decode 1-byte length encoded ASCII string. The string is formatted
in the data buffer with the first byte containing the length of the string that follows. The characters
in the string are ASCII.
| |
| LengthEncodedASCII2BE |
Returns an encoder that will encode and decode 2-byte big-endian length encoded ASCII string. The string is formatted
in the data buffer with the first two bytes containing the length of the string that follows. The characters
in the string are ASCII.
| |
| LengthEncodedASCII2LE |
Returns an encoder that will encode and decode 2-byte little-endian length encoded ASCII string. The string is
formatted in the data buffer with the first two bytes containing the length of the string that follows. The
characters in the string are ASCII.
| |
| LengthEncodedASCII4LE |
Returns an encoder that will encode and decode 4-byte little-endian length encoded ASCII string. The string is
formatted in the data buffer with the first two bytes containing the length of the string that follows. The
characters in the string are ASCII.
| |
| LengthEncodedUnicode2LE |
Returns an encoder that will encode and decode 2-byte little-endian length encoded Unicode string. The string is
formatted in the data buffer with the first two bytes containing the length of the string that follows. The
characters in the string are Unicode.
| |
| NullTerminatedASCII |
An encoder that will encode and decode null terminated ASCII strings.
| |
| NullTerminatedUnicode |
An encoder that will encode and decode null terminated Unicode strings.
| |
| StxEtx |
An encoder that will encode and decode STX/ETX delimted ASCII strings.
|
| Name | Description | |
|---|---|---|
| FixedLength |
Returns an encoder that will encode and decode fixed length string given a character encoding.
| |
| GetByteCount(BinaryString) |
Calculates the number of bytes produced by encoding the characters in the specified string.
| |
| GetByteCount(String) |
Calculates the number of bytes produced by encoding the characters in the specified string.
| |
| GetBytes(BinaryString) |
Encodes a string into a byte array.
| |
| GetBytes(BinaryString, IDataWriter) |
Encodes a string into a byte array.
| |
| GetBytes(BinaryString, ArraySegmentByte) |
Encodes a string into a byte array.
| |
| GetBytes(BinaryString, Byte, Int32, Int32) |
Encodes a string into a byte array.
| |
| GetBytesInternal |
Encodes a string into a byte array.
| |
| GetString(ArraySegmentByte) |
Decodes a string from a byte array.
| |
| GetString(IDataReader) |
Decodes a string from a byte array.
| |
| GetString(Byte, Int32, Int32) |
Decodes a string from a byte array.
| |
| GetStringAsync |
Decodes a string from a byte array.
| |
| GetStringInternal |
Decodes a string from a byte array.
| |
| GetStringInternalAsync |
Decodes a string from a byte array.
| |
| LengthEncoded |
Returns an encoder that will encode and decode length encoded string.
| |
| NullTerminated |
Returns an encoder that will encode and decode null terminated string given a character encoding.
| |
| StringDelimited |
Returns an encoder that will encode and decode string-delimited strings given a character encoding.
|
Like System.Text.Encoding except that this class takes into account not just the encoding of the characters in the text, but also the marashalling of the characters into the byte stream. It doesn't just assume it's "length encoded" as BinaryWriter does. It also copes with binary data containing nulls and control characters, and removes them from the string.
Use of this class is essential for passing string data between peers. All the IO ReadString and WriteString methods use an instance of this class to determine how to encode strings into network/IO packets. (See ReadString(IDataReader, BinaryTextEncoding) and WriteString(IDataWriter, BinaryString, BinaryTextEncoding) for example).
The following example shows simple examples for reading and writing strings.
// These first two examples show how to read/write variable-length strings to/from a packet. public void VariableLengthStrings(IDataReader receivedPacket, IDataWriter packetToSend) { // Reads all the bytes in 'receivedPacket' and converts it all into an ASCII string. var str = receivedPacket.ReadString(BinaryTextEncoding.FixedLengthASCII); // Writes the string "hello" into a packet ready to be sent to the peer. packetToSend.WriteString("hello", BinaryTextEncoding.FixedLengthASCII); } // Sometimes there's more data in a packet than just one string. In which case, it may be // necessary to specify exactly how long the string is that we're expecting to read. public void FixedLengthStrings(IDataReader receivedPacket, ushort lengthOfString) { // Reads 'lengthOfString' bytes of data from 'receivedPacket' and then interprets them as Unicode (UTF-16). var str = receivedPacket.ReadString(BinaryTextEncoding.FixedLength(System.Text.Encoding.Unicode, lengthOfString)); }
Very often, especially when communicating over TCP, strings are encapsulated in some way that describes how much of the incoming data belongs to the string. Common techniques include null-terminated (NullTerminated(Encoding)), length-encoded (LengthEncoded(Int32, Boolean, Encoding)), or string-delimited (StringDelimited(String, String, Encoding)) strings.
Null-terminated is common with ASCII strings.
public void NullTerminatedStrings(IDataReader receivedPacket, IDataWriter packetToSend) { // Reads bytes of data from 'receivedPacket' as an ASCII string until it reads the 'null' character. var str = receivedPacket.ReadString(BinaryTextEncoding.NullTerminatedASCII); // Writes the string "hello" into a packet as Unicode characters ready to be sent to the peer, // finishing it with a 'null' character. packetToSend.WriteString("hello", BinaryTextEncoding.NullTerminatedUnicode); }
Length-encoding is common when communicating with low-level systems; systems that typically use a binary protocol.
public void LengthEncodedStrings(IDataReader receivedPacket, IDataWriter packetToSend) { // Length encoded strings are prefixed with a binary number that states exactly how many // bytes belong to the string. Most commonly, the number is itself 2-bytes (a 16-bit number), // and that number is itself encoded using the Big Endian format. var str = receivedPacket.ReadString(BinaryTextEncoding.LengthEncodedASCII2BE); // This example uses a 4-byte (32-bits) Little Endian number to describe the number of bytes // in the string. The string is then interpretted as Unicode. str = receivedPacket.ReadString(BinaryTextEncoding.LengthEncoded(4, true, System.Text.Encoding.Unicode)); // Writes the string "hello" into a packet ready to be sent to the peer. The string is prepended with // two bytes 0x00, 0x05 (the length of the string). packetToSend.WriteString("hello", BinaryTextEncoding.LengthEncodedASCII2BE); }
String-delimited is common when communicating with high-level systems; systems that typically use a string-based protocol. These protocols usually encode the packet entirely in ASCII.
public string StringDelimitedStrings(IDataReader receivedPacket) { // The string to be read will start with '<<' and end with '>>'. The string returned will have the // quotes ('<<' and '>>') stripped off. Just the original string between the quotes will be returned. return receivedPacket.ReadString(BinaryTextEncoding.StringDelimited("<<", ">>")); } public string PipeDelimitedStrings(IDataReader receivedPacket) { // The string is terminated with a '|' character. Effectively the protocol uses strings separated with '|'. return receivedPacket.ReadString(BinaryTextEncoding.StringDelimited(null, "|")); } public void StxEtxDelimitedStrings(IDataReader receivedPacket, IDataWriter packetToSend) { // The standard STX ETX encapsulated format. var str = receivedPacket.ReadString(BinaryTextEncoding.StxEtx); // Writes the string "hello" prepended with the STX character and appended with the ETX character. packetToSend.WriteString("hello", BinaryTextEncoding.StxEtx); }
The following examples shows complete client and server implementations using a simple string-based protocol. The high-level system connects to this server, and then the Demo3D model (the server) and the high-level system (the client) exchange string commands. The protocol dictates that each string is encapsulated with the STX and ETX characters.
Either side can initiate a command. The other side needs to respond with "ACK" once the command has been received and executed.
using System; using System.Threading.Tasks; using Demo3D.IO; using Demo3D.Native; using Demo3D.Net; using Demo3D.Net.Protocols; using Demo3D.Visuals; namespace Examples.Net.StringServerExample { [Auto] public class Server { [Auto] PrintDelegate print; [Auto] SimplePropertyValue<int> tcpPort; // The port to run the server on. [Auto] TextVisual StatusDisplay; // Requires corresponding TextVisual child visual [Auto] TextVisual MessageDisplay; // Requires corresponding TextVisual child visual ProtocolSocket serverSocket; ServerClient<IPacketIOService> clientSocket; BinaryTextEncoding stxEtx = BinaryTextEncoding.StringDelimited("\u0002", "\u0003"); void HandleMessage(ServerClient<IPacketIOService> clientSocket, string message) { print("Server: Message received \"" + message + "\""); if (message == "ACK") { // One of our requests was acknowledged. return; } if (MessageDisplay != null) { MessageDisplay.Text = "Received: \"" + message + "\""; } if (message.StartsWith("Hello")) { // Raise the lift, and then return "ACK". // ... Raise the lift here... print("Server: Sending message \"ACK\""); var buffer = stxEtx.GetBytes("ACK"); // Encode with STX/ETX. clientSocket.IO.Write(buffer); } } void ReceiveMessage(ProtocolSocket socket, object service, NotifyDataChangedEventArgs e) { try { var args = (PacketChangedEventArgs)e; // A TCP server uses PacketChangedEventArgs. var message = args.GetData(); // Read from the packet. string str = stxEtx.GetString(message); // Decode STX/ETX string. // Handle the message. HandleMessage(clientSocket, str); } catch (Exception x) { socket.Close(x); } } // Disconnect the client. void CloseClient() { if (clientSocket != null) { clientSocket.Close(); clientSocket.IO.DataChanged -= ReceiveMessage; clientSocket = null; } } // Close the server. void CloseServer() { if (serverSocket != null) { serverSocket.Close(); serverSocket = null; } } // Accecpt a new client connection. Task ServiceClient(ServerClient<IPacketIOService> connection) { CloseClient(); // Close any existing client connection (this example only allows one client at a time). clientSocket = connection; clientSocket.IO.DataChanged += ReceiveMessage; return Task.CompletedTask; } [Auto] void OnReset(Visual sender) { if (clientSocket != null || serverSocket != null) { print("Server: Closing connection and stopping server"); CloseServer(); CloseClient(); } if (StatusDisplay != null) { StatusDisplay.Text = "Status: Closed"; StatusDisplay.Material.Color = System.Drawing.Color.Gray; } if (MessageDisplay != null) { MessageDisplay.Text = "Received: "; } } [Auto] void OnInitialize(Visual sender) { CloseServer(); CloseClient(); // Open a server on the configured tcpPort. serverSocket = TcpServer.Open(tcpPort, ServiceClient); if (StatusDisplay != null) { StatusDisplay.Text = "Status: Connected Port " + tcpPort.ToString(); StatusDisplay.Material.Color = System.Drawing.Color.Lime; } } } }
using System; using System.Collections; using System.Threading.Tasks; using Demo3D.IO; using Demo3D.Native; using Demo3D.Net; using Demo3D.Net.Protocols; using Demo3D.Visuals; namespace Examples.Net.StringClientExample { [Auto] public class Client { [Auto] IBuilder app; [Auto] PrintDelegate print; [Auto] SimplePropertyValue<string> tcpAddress; // The hostname to connect to. [Auto] SimplePropertyValue<int> tcpPort; // The port to connect to. [Auto] TextVisual StatusDisplay; // Requires corresponding TextVisual child visual Client<IPacketIOService> tcpClient; BinaryTextEncoding stxEtx = BinaryTextEncoding.StringDelimited("\u0002", "\u0003"); async Task SendMessages() { // Send a message to the server once a second while the model is running. for (int msg = 0; app.Running; msg++) { var str = "Hello " + msg.ToString(); print("Client: Sending message \"" + str + "\""); var bytes = stxEtx.GetBytes(str); // Encode with STX/ETX await tcpClient.IO.WriteAsync(bytes); // Wait one second. await Task.Delay(1000); } } void HandleMessage(Client<IPacketIOService> clientSocket, string message) { print("Client: Message received \"" + message + "\""); if (message == "ACK") { // One of our requests was acknowledged. return; } } void ReceiveMessage(ProtocolSocket socket, object service, NotifyDataChangedEventArgs e) { try { var args = (PacketChangedEventArgs)e; // A TCP server uses PacketChangedEventArgs. var message = args.GetData(); // Read from the packet. string str = stxEtx.GetString(message); // Decode STX/ETX string. // Handle the message. HandleMessage(tcpClient, str); } catch (Exception x) { socket.Close(x); } } void CloseConnection(Exception error = null) { if (tcpClient != null) { tcpClient.Close(error); tcpClient.IO.DataChanged -= ReceiveMessage; tcpClient = null; } } async Task Connect() { try { // Connect to the server using the configured tcpAddress and tcpPort. tcpClient = await TcpClient.OpenAsync(sync: false, tcpAddress, tcpPort); tcpClient.IO.DataChanged += ReceiveMessage; if (StatusDisplay != null) { StatusDisplay.Text = "Status: Connected " + tcpAddress.Value + ":" + tcpPort.ToString(); StatusDisplay.Material.Color = System.Drawing.Color.Lime; } // Start sending messages to the server. await SendMessages(); } catch (Exception e) { // If there's an error, close the connection and report the error. CloseConnection(e); } } [Auto] void OnReset(Visual sender) { CloseConnection(); if (StatusDisplay != null) { StatusDisplay.Text = "Status: Closed"; StatusDisplay.Material.Color = System.Drawing.Color.Gray; } } [Auto] IEnumerable OnInitialize(Visual sender) { yield return Connect(); } } }