C# Separate TCP messages - c#

I'm working on a TCP Client/Server system for practice purposes and I want to send specific data between the two.
I'm already able to send bytes and let them display as a string. Also I am able to send a specific string ("mb") and let a MessageBox pop-up on the server-side.
The content of the Box is the text sent (in this case "mb", though).
This is the Server-Side:
byte[] msg = new byte[4096];
var count = stream.Read(msg, 0, msg.Length);
string returnData = Encoding.ASCII.GetString(msg, 0, count);
switch(returnData)
{
case "mb":
MessageBox((IntPtr)0, returnData, "HACKERZ", 0);
break;
case "":
client.Client.Disconnect(true);
Console.WriteLine("User disconnected");
break;
default:
Console.WriteLine(returnData);
break;
}
And this is Client-Side:
private void btnSend_Click(object sender, EventArgs e)
{
NetworkStream stream = client.GetStream();
byte[] msg = new byte[4096];
msg = Encoding.ASCII.GetBytes(txtMsg.Text);
stream.Write(msg, 0, msg.Length);
}
So if I write "mb" in the Textfield, it shows a MessageBox saying "mb".
I would like to know, how can I separate the message that was sent by the NetworkStream, so I can set Capture and Content of the MessageBox separately.

As you noticed, you are sending binary data over socket. In client you get bytes from message msg = Encoding.ASCII.GetBytes(txtMsg.Text); and in server you're casting it back to string with string returnData = Encoding.ASCII.GetString(msg, 0, count);
In this approach, you can send for instance comma-separated string where first field would be Capture and second Content.
However, as it is byte stream, you can send anything what is serializable into bytes. You can use BinaryWriter\BinaryReader over Network stream to send any structure.

Related

How to set a correct buffer size for TCP message responses

This is how I currently send data to an external TCP server
byte[] data = new byte[0] /* the data to send */;
TcpClient client = new TcpClient("127.0.0.1", 3000); // connect to the tcp server
NetworkStream stream = client.GetStream();
await stream.WriteAsync(data, 0, data.Length);
data = new byte[256]; // set the buffer size
int responseBytes = await stream.ReadAsync(data, 0, data.Length); // store the response to the buffer
string responseData = System.Text.Encoding.ASCII.GetString(data, 0, responseBytes);
stream.Close();
client.Close();
For the response I have to setup the buffer size here new byte[256]. But what if the response is greater than this size? I can't determine the correct size because I'm just connecting to his external server, send a message to it and expect a response. Is there a way I can make this dynamic?
As a sidenote: I'm sending various HL7 messages to clinic servers and they will send back HL7 ACK messages as a response. This gives some information about HL7 ACK messages
https://healthstandards.com/blog/2007/02/01/ack-message-original-mode-acknowledgement/
An example ACK could be
MSH|^~&|CATH|StJohn|AcmeHIS|StJohn|20061019172719||ACK^O01|MSGID12349876|P|2.3
MSA|AA|MSGID12349876
For the response I have to setup the buffer size here new byte[256]. But what if the response is greater than this size?
Then you call stream.ReadAsync() and append your buffer (or the decoded string) to a larger buffer until you know you have received the entire message, which you need to do anyway: the Write() from one end of the socket does not need to correspond to one Read() on the other end. Multiple writes can be read in a single read, or the other way around.
So something like this:
data = new byte[256]; // set the buffer size
var builder = new StringBuilder();
do
{
int responseBytes = await stream.ReadAsync(data, 0, data.Length); // store the response to the buffer
string responseData = System.Text.Encoding.ASCII.GetString(data, 0, responseBytes);
builder.Append(responseData);
} while (responseBytes > 0)
Do note that this happens to work with ASCII, as it doesn't have multibyte characters. Were it UTF-8 or a similar encoding, the 256th byte could be the start of a character which continues into the next read, i.e. byte 1 (and perhaps 2) of the next read.
This code also assumes you want to keep reading until the connection is closed (then responseBytes = 0). If this protocol has a length prefix or message terminator, you have to handle those.
Usually you don't want to implement this low-level stuff yourself, aren't there libraries available that handle the HL7 protocol?

Connecting to Serial->Ethernet converter box via Telnet, getting strange response (no matter what I send)

I'm trying to communicate with a device that has a Serial COM port that goes out (using RS232 Protocol), which I've hooked up to a converter box that converts the connection to Ethernet.
I've successfully connected to the converter box (which has its own IP) and I've successfully communicated with the device by sending it commands over telnet by using PuTTY, to which it has responded with various data that I've been able to parse.
I've established a connection by creating a new TcpClient, and I'm able to send strings to the device, but every time I get a response back, it's always "??\u0003" which I've researched and found that \uhhhh is a Unicode escape protocol. This confuses me because I have used ASCII encoding for everything.
public string TcpConnect(string cmd)
{
var client = new TcpClient();
//cmd = "ping";
Console.WriteLine("Connecting to server");
client.Connect("169.254.20.40", 23); //22 = ssh, 23 = telnet, 80 = http
Console.WriteLine("CONNECTED SUCCESSFULLY");
Stream tcpStream = client.GetStream();
ASCIIEncoding A = new ASCIIEncoding();
byte[] enable = A.GetBytes("enable" + Environment.NewLine);
byte[] connect = A.GetBytes("connect line 1" + Environment.NewLine);
byte[] ba = A.GetBytes(cmd);
Console.WriteLine("Transmitting.....");
tcpStream.Write(enable, 0, enable.Length);
tcpStream.Write(connect, 0, connect.Length);
tcpStream.Write(ba, 0, ba.Length);
byte[] responseBytes = new byte[4096];
int numBytesRead = tcpStream.Read(responseBytes, 0, responseBytes.Length);
var message = A.GetString(responseBytes, 0, numBytesRead);
Console.WriteLine("\nServer Closed.");
return message;
}
If I were to pass in "$TEAA4B9\r\n" as the message, I would expect something along the lines of "$TEA,086,040,031,000,3798" which is nowhere close to what I'm getting (which is ??\u0003)
Nevermind figured it out
Just kidding! Here's what I did: I added a "System.Threading.Thread.Sleep("1000") before the "tcpStream.Read" line at the bottom, and now it outputs the data I need. The device was outputting garbage on the first line (perhaps a handshake, not sure) and that's all that was being read before it was being stored into "message" and returned (not enough time was spent reading before it moved on to the next line of code)

After receiving string as memory stream and encoding, more chars are added

I built an app which receiving and sending data to server by the code below, and I noticed It's adding some chars to the string I send as MemoryStream when I'm getting the string back. Here's the code and the debugging information:
Client:
while (true)
{
if (stream.DataAvailable)
{
while ((i = stream.Read(ByteBuffer, 0, ByteBuffer.Length)) != 0)
{
ms.Write(ByteBuffer, 0, ByteBuffer.Length);
if (stream.DataAvailable)
continue;
else
break;
}
ToReturn = Encoding.ASCII.GetString(ms.ToArray());
return ToReturn;
}
}
}
Server:
MemoryStream response = new MemoryStream();
response = Protocol.ProcessRequest(dataRecieved, ClientAddr);
#endregion
Console.WriteLine("Trying to send back response." + Encoding.ASCII.GetString(response.ToArray()));
stream.Flush();
response.WriteTo(stream);
I've checked with the debugger and what printed with the console:
the sent information is just fine, for example:
response.Id^Name^Type^SubType^Description^AddedBy^AddedDT^IsSpecial^Amount#1^VGA cable^cable^display^Very old and common display cable.^Aviv^14/01/2019 22:04:34^False^3345#2^HDMI cable^cable^display^newer and better display cable. can pass network, audio and info.^Aviv^14/01/2019 22:05:30^False^4793
but the info received on the other side of the socket (the client) was:
Id^Name^Type^SubType^Description^AddedBy^AddedDT^IsSpecial^Amount#1^VGA cable^cable^display^Very old and common display cable.^Aviv^14/01/2019 22:04:34^False^3345#2^HDMI cable^cable^display^newer and better display cable. can pass network, audio and info.^Aviv^14/01/2019 22:05:30^False^4793alse^4
-with these (alse^4) few chars at the end. can anyone tell me what's the encoding problem? Thanks.
AGAIN: the output from the server is fine
//ms.Write(ByteBuffer, 0, ByteBuffer.Length);
ms.Write(ByteBuffer, 0, i);

Read bytes from NetworkStream (Hangs)

I'm trying to learn the basics of networking and I've built an echo server from this tutorial. I checked the server with telnet and it works perfect.
Now when I'm using some of the many client samples on the Internet:
// Create a TcpClient.
// Note, for this client to work you need to have a TcpServer
// connected to the same address as specified by the server, port
// combination.
TcpClient client = new TcpClient(server, port);
// Translate the passed message into ASCII and store it as a Byte array.
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
// Get a client stream for reading and writing.
NetworkStream stream = client.GetStream();
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
Console.WriteLine("Sent: {0}", message);
// Receive the TcpServer.response.
// Buffer to store the response bytes.
data = new Byte[256];
// String to store the response ASCII representation.
String responseData = String.Empty;
// Read the first batch of the TcpServer response bytes.
Int32 bytes = stream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Console.WriteLine("Received: {0}", responseData);
// Close everything.
stream.Close();
client.Close();
It doesn't work very well. If I will comment the stream.Read line, everything works perfect (expect I can't read). I was also trying to accomplish that in a similar way using asynchronous callback method for the read. and then it only works after I terminate the program (the server handles the request)
I suspect that the way I'm reading from the stream cause this block, but I'm too clueless to understand what I'm doing wrong.
The implementation will block until at least one byte of data can be
read, in the event that no data is available.
From MSDN
Your server propably isn't sending you any data.
Edit:
I tested your client and it works perfectly fine. Try it yourself and set the following parameters:
string server = "google.com";
int port = 80;
string message = "GET /\n";
It's definitely your server which has the problem.

Send server multiple messages? C#

I have a quick and dirty question. So as it stands, i have two clients and a server running. I can communicate messages from the clients to the server without any problem. my problem appears when i want to read two messages from the client - rather than just one message.
The error which i receive is: IOException was unhandled. Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
Here is my code on the server side:
private static void HandleClientComm(object client)
{
/** creating a list which contains DatabaseFile objects **/
List theDatabase = new List();
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
do
{
bytesRead = 0;
try
{
// Blocks until a client sends a message
bytesRead = clientStream.Read(message, 0, 4096);
}
catch (Exception)
{
// A socket error has occured
break;
}
if (bytesRead == 0)
{
// The client has disconnected from the server
break;
}
// Message has successfully been received
ASCIIEncoding encoder = new ASCIIEncoding();
Console.WriteLine("To: " + tcpClient.Client.LocalEndPoint);
Console.WriteLine("From: " + tcpClient.Client.RemoteEndPoint);
Console.WriteLine(encoder.GetString(message, 0, bytesRead));
if (encoder.GetString(message, 0, bytesRead) == "OptionOneInsert")
{
byte[] message2 = new byte[4096];
int bytesRead2 = 0;
**bytesRead2 = clientStream.Read(message, 0, 4096);** //ERROR occurs here!
Console.WriteLine("Attempting to go inside insert)");
Menu.Insert(theDatabase, bytesRead2);
}
Here is my client code:
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes("OptionOneInsert");
Console.ReadLine();
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
NetworkStream clientStream2 = client.GetStream();
String text = System.IO.File.ReadAllText("FirstNames.txt");
clientStream2.Write(buffer, 0, buffer.Length);
clientStream2.Flush();
ASCIIEncoding encoder2 = new ASCIIEncoding();
byte[] buffer2 = encoder2.GetBytes(text);
Console.WriteLine("buffer is filled with content");
Console.ReadLine();
When the client sends the message "optionOne" it is received by the server just fine. It's only when i attempt to send the string called "text" that the issues appears!
Any help would be greatly appreciated - I'm not all that familiar with Sockets, hence i've been struggling with trying to understand this for sometime now
You've got a big problem here - there's nothing to specify the end of one message and the start of another. It's quite possible that the server will receive two messages in one go, or half a message and then the other half.
The simplest way of avoiding that is to prefix each message with the number of bytes in it, e.g. as a fixed four-byte format. So to send a message you would:
Encoding it from a string to bytes (ideally using UTF-8 instead of ASCII unless you're sure you'll never need any non-ASCII text)
Write out the length of the byte array as a four-byte value
Write out the content
On the server:
Read four bytes (looping if necessary - there's no guarantee you'd even read those four bytes together, although you almost certainly will)
Convert the four bytes into an integer
Allocate a byte array of that size
Loop round, reading from "the current position" to the end of the buffer until you've filled the buffer
Convert the buffer into a string
Another alternative is simply to use BinaryReader and BinaryWriter - the ReadString and WriteString use length-prefixing, admittedly in a slightly different form.
Another alternative you could use is to have a delimiter (e.g. carriage-return) but that means you'll need to add escaping in if you ever need to include the delimiter in the text to transmit.

Categories