Problem with serial port data receive in C# - c#

I have a problem with a C# program.
Through the Serial port i Receive a large string about 110 characters.
This part works ok, but when i add code to split the string up i receive an error after some rows.
Here is the error I get:
**An unhandled exception of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll
Additional information: Index and length must refer to a location within the string.**
Here is the code:
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (!comport.IsOpen) return;
if (CurrentDataMode == DataMode.Text)
{
// Read all the data waiting in the buffer
string data = comport.ReadExisting();
Log(LogMsgType.Incoming, data);
string ziua = data.Substring(0, 8);
string ora = data.Substring(8, 8);
string linie = data.Substring(18, 1);
string interior = data.Substring(22, 3);
string durata1 = data.Substring(26, 4);
string durata2 = data.Substring(30, 8);
string nrtel = data.Substring(38, 10);
string tipapel = data.Substring(75, 1);
string acct = data.Substring(76, 5);
}
else
{
int bytes = comport.BytesToRead;
byte[] buffer = new byte[bytes];
comport.Read(buffer, 0, bytes);
Log(LogMsgType.Incoming, ByteArrayToHexString(buffer));
}
}
EDIT:
i've tested every substring and all of them are ok.
the string lenght is 112. it can't be to short.
this error appears after a few lines of 112... about one and a half

This is typical behavior for a serial port. They are very slow. When the DataReceived event fires, you'd typically only get one or two characters. Notably is that it works well when you debug because single-stepping through the code gives the serial port lots of time to receive additional characters. But it will go Kaboom as soon as you run without a debugger because the string isn't long enough.
You'll need to modify the code by appending the string you receive to a string variable at class scope. Only parse the string after you've received all the characters you expected. You'll need some way to know that you've received the full response. Most typically serial devices will terminate the string with a special character. Often a line-feed.
If that's the case then you can make it easy by setting the SerialPort.NewLine property to that terminator and calling ReadLine() instead of ReadExisting().

The length of "data" is probably too short for one of the calls to "Substring". Check the length of the string that you expect before accessing parts of it that may not exist.

You don't check what you have enough data before processing. SerialPort.ReadExisting Method just
Reads all immediately available bytes, based on the encoding, in both the stream and the input buffer of the SerialPort object.
Your device maybe just don't have time to pass all data. So you need rewrite your logic to concatenate incoming data and process it after receiving enough data.

The exception is telling you that, at some point, Substring is being given parameters that exceed the length of the string. Which likely means you aren't getting the data you are expecting from the serial port. Try inserting a breakpoint at the first call to Substring and check the contents of data to make sure the device you are reading from isn't sending some kind of error code or something other than what your code expects.

You should verify the length of your string before your start splitting it up. Put a conditional in there to handle the case where the string is less than what you expect, and then see if the errors persist.

Try make a length check for each variable assignment like this:
string acct = (data.length >= 81) ? data.Substring(76, 5) : string.empy;
The data.length could be shorter than the total length of your substring (76 + 5).

Related

Server's socket blocking if no newline detected but client's socket not

While setting up a TCP server-client connection, I realized that the server receive function hangs if the client does not send an '\n', but the client does not block if the sever doesn't. I tried searching for an explanation without finding a proper answer, so I came here to ask for your help.
I am using the same function to exchange data for both server and client, but I don't know why it works for one and doesn't for the other...
Here is my function in C#:
public bool sendToClient(int i, string msg)
{
try
{
(clientSockets.ElementAt(i)).mSocket.Send(Encoding.ASCII.GetBytes(msg));
}
catch(Exception e)
{
Console.WriteLine(e.Data.ToString());
return false;
}
return true;
}
private string getMessageFromConnection(Socket s)
{
byte[] buff;
string msg = "";
int k;
do
{
buff = new byte[100];
k = s.Receive(buff, 100, SocketFlags.None);
msg += Encoding.ASCII.GetString(buff, 0, k);
} while (k >= 100);
return msg;
}
The sockets are simple SOCK_STREAM ones, and the clientSockets is a list containing Client objects containing each client info including their socket.
I understand that one solution would be to detect a particular character to end the message, but I would like to know the reason behind it because I also had this issue using C.
Thanks in advance.
Your while loop continues only as long as you're reading exactly 100 bytes, and it seems that you intend to use that to detect the end of a message.
This will fail if the message is exactly 100 or any multitude of 100 bytes (in which case it will append a subsequent message to it).
But even worse - there is no guarantee that the socket will return 100 bytes, even if there is data still on its way. Receive does not wait until the underlying buffer has reached 100 bytes, it will return whatever it has available at that point.
You're going to have to either include a header that indicates the message length, or have a terminator character that indicates the end of the message.

Send PC information over TCP

I am trying to send various bits of PC information such as free HDD space, total RAM etc to a Windows Service over TCP. I have the following code which basically creates a string of information split by a |, ready for processing within the Windows Service TCP server to be put in to a SQL table.
Is it best to do this as I have done or is there a better way?
public static void Main(string[] args)
{
Program stc = new Program(clientType.TCP);
stc.tcpClient(serverAddress, Environment.MachineName.ToString() + "|" + FormatBytes(GetTotalFreeSpace("C:\\")).ToString());
Console.WriteLine("The TCP server is disconnected.");
}
public void tcpClient(String serverName, String whatEver)
{
try
{
//Create an instance of TcpClient.
TcpClient tcpClient = new TcpClient(serverName, tcpPort);
//Create a NetworkStream for this tcpClient instance.
//This is only required for TCP stream.
NetworkStream tcpStream = tcpClient.GetStream();
if (tcpStream.CanWrite)
{
Byte[] inputToBeSent = System.Text.Encoding.ASCII.GetBytes(whatEver.ToCharArray());
tcpStream.Write(inputToBeSent, 0, inputToBeSent.Length);
tcpStream.Flush();
}
while (tcpStream.CanRead && !DONE)
{
//We need the DONE condition here because there is possibility that
//the stream is ready to be read while there is nothing to be read.
if (tcpStream.DataAvailable)
{
Byte[] received = new Byte[512];
int nBytesReceived = tcpStream.Read(received, 0, received.Length);
String dataReceived = System.Text.Encoding.ASCII.GetString(received);
Console.WriteLine(dataReceived);
DONE = true;
}
}
}
catch (Exception e)
{
Console.WriteLine("An Exception has occurred.");
Console.WriteLine(e.ToString());
}
}
Thanks
Because TCP is stream-based, it is important to have some indicator in the message to signal the other end when it has read the complete message. There are two traditional ways of doing this. First, you could have some special byte pattern at the end of each message. When the other end reads the data, it knows that it has read a full message when that special byte pattern is seen. Using this mechanism requires a byte pattern that is not likely to be included in the actual message. The other way is to include the length of the data at the beginning of the message. This is the way I do it. All my TCP messages include a short header structured like this:
class MsgHeader
{
short syncPattern; // e.g., 0xFDFD
short msgType; // useful if you have different messages
int msgLength; // length of the message minus header
}
When the other side starts receiving data, it reads the first 8 bytes, verifies the sync pattern (for the sake of sanity), and then uses the message length to read the actual message. Once the message has been read, it processes the message based on the message type.
I'd suggest creating a class that gathers the system information you're interested in and is capable of encoding/decoding it, something like:
using System;
using System.Text;
class SystemInfo
{
private string machineName;
private int freeSpace;
private int processorCount;
// Private so no one can create it directly.
private SystemInfo()
{
}
// This is a static method now. Call SystemInfo.Encode() to use it.
public static byte[] Encode()
{
// Convert the machine name to an ASCII-based byte array.
var machineNameAsByteArray = Encoding.ASCII.GetBytes(Environment.MachineName);
// *THIS IS IMPORTANT* The easiest way to encode a string value so that it
// can be easily decoded is to prepend the length of the string. Otherwise,
// you're left guessing on the decode side about how long the string is.
// Calculate the message length. This does *NOT* include the size of
// the message length itself.
// NOTE: As new fields are added to the message, account for their
// respective size here and encode them below.
var messageLength = sizeof(int) + // length of machine name string
machineNameAsByteArray.Length + // the machine name value
sizeof(int) + // free space
sizeof(int); // processor count
// Calculate the required size of the byte array. This *DOES* include
// the size of the message length.
var byteArraySize = messageLength + // message itself
sizeof(int); // 4-byte message length field
// Allocate the byte array.
var bytes = new byte[byteArraySize];
// The offset is used to keep track of where the next field should be
// placed in the byte array.
var offset = 0;
// Encode the message length (a very simple header).
Buffer.BlockCopy(BitConverter.GetBytes(messageLength), 0, bytes, offset, sizeof(int));
// Increment offset by the number of bytes added to the byte array.
// Note that the increment is equal to the value of the last parameter
// in the preceding BlockCopy call.
offset += sizeof(int);
// Encode the length of machine name to make it easier to decode.
Buffer.BlockCopy(BitConverter.GetBytes(machineNameAsByteArray.Length), 0, bytes, offset, sizeof(int));
// Increment the offset by the number of bytes added.
offset += sizeof(int);
// Encode the machine name as an ASCII-based byte array.
Buffer.BlockCopy(machineNameAsByteArray, 0, bytes, offset, machineNameAsByteArray.Length);
// Increment the offset. See the pattern?
offset += machineNameAsByteArray.Length;
// Encode the free space.
Buffer.BlockCopy(BitConverter.GetBytes(GetTotalFreeSpace("C:\\")), 0, bytes, offset, sizeof(int));
// Increment the offset.
offset += sizeof(int);
// Encode the processor count.
Buffer.BlockCopy(BitConverter.GetBytes(Environment.ProcessorCount), 0, bytes, offset, sizeof(int));
// No reason to do this, but it completes the pattern.
offset += sizeof(int).
return bytes;
}
// Static method. Call is as SystemInfo.Decode(myReceivedByteArray);
public static SystemInfo Decode(byte[] message)
{
// When decoding, the presumption is that your socket code read the first
// four bytes from the socket to determine the length of the message. It
// then allocated a byte array of that size and read the message into that
// byte array. So the byte array passed into this function does *NOT* have
// the 4-byte message length field at the front of it. It makes no sense
// in this class anyway.
// Create the SystemInfo object to be populated and returned.
var si = new SystemInfo();
// Use the offset to navigate through the byte array.
var offset = 0;
// Extract the length of the machine name string since that is the first
// field encoded in the message.
var machineNameLength = BitConverter.ToInt32(message, offset);
// Increment the offset.
offset += sizeof(int);
// Extract the machine name now that we know its length.
si.machineName = Encoding.ASCII.GetString(message, offset, machineNameLength);
// Increment the offset.
offset += machineNameLength;
// Extract the free space.
si.freeSpace = BitConverter.ToInt32(message, offset);
// Increment the offset.
offset += sizeof(int);
// Extract the processor count.
si.processorCount = BitConverter.ToInt32(message, offset);
// No reason to do this, but it completes the pattern.
offset += sizeof(int);
return si;
}
}
To encode the data, call the Encode method like this:
byte[] msg = SystemInfo.Encode();
To decode the data once it's been read from the socket, call the Decode method like this:
SystemInfo si = SystemInfo.Decode(msg);
As to your actual code, I'm not sure why you're reading from the socket after writing to it unless you're expecting a return value.
A few things to consider. Hope this helps.
EDIT
First of all, use the MsgHeader if you feel you need it. The example above simply uses the message length as the header, i.e., it does not include a sync pattern or a message type. Whether you need to use this additional information is up to you.
For every new field you add to the SystemInfo class, the overall size of the message will increased, obviously. Thus, the messageLength value needs to be adjusted accordingly. For example, if you add an int to include the number of processors, messageLength will increase by sizeof(int). Then, to add it to the byte array, simply use the same System.Buffer.BlockCopy call. I've adjusted the example to show this with a little more detail, including making the method static.

Read characters from serial port in c#

Hello I am using Read() method to read 10 characters say 0123456789 from serial port. Actually the characters are sent by a PIC Micro-controller.
Here is my code:
serialPort1.PortName = "com4";
serialPort1.BaudRate = 9600;
serialPort1.Open();
char[] result = new char[10];
serialPort1.Read(result, 0, result.Length);
string s = new string(result);
MessageBox.Show(s);
serialPort1.Close();
When I run the code, a message box shows up and displays only the first character. "0" alone is displayed in the message box.
Where have i gone wrong ??
What you are doing wrong is not paying attention to the return value of Read(). Which tells you how many bytes were read.
Serial ports are very slow devices, at a typical baudrate setting of 9600 it takes a millisecond to get one byte transferred. That's an enormous amount of time for a modern processor, it can easily execute several million instructions in a millisecond. The Read() method returns as soon as some bytes are available, you only get all 10 of them if you make your program artificially slow so the driver gets enough time to receive all of them.
A simple fix is to keep calling Read() until you got them all:
char[] result = new char[10];
for (int len = 0; len < result.Length; ) {
len += serialPort1.Read(result, len, result.Length - len);
}
Another common solution is to send a unique character to indicate the end of the data. A line feed ('\n') is a very good choice for that. Now it becomes much simpler:
string result = serialPort.ReadLine();
Which now also supports arbitrary response lengths. Just make sure that the data doesn't also contain a line feed.

Sending Array of Bytes from Client to server?

I've a tcp based client-server application. I'm able to send and receive strings, but don't know how to send an array of bytes instead.
I'm using the following function to send a string from the client to the server:
static void Send(string msg)
{
try
{
StreamWriter writer = new StreamWriter(client.GetStream());
writer.WriteLine(msg);
writer.Flush();
}
catch
{
}
}
Communication example
Client sends a string:
Send("CONNECTED| 84.56.32.14")
Server receives a string:
void clientConnection_ReceivedEvent(Connection client, String Message)
{
string[] cut = Message.Split('|');
switch (cut[0])
{
case "CONNECTED":
Invoke(new _AddClient(AddClient), client, null);
break;
case "STATUS":
Invoke(new _Status(Status), client, cut[1]);
break;
}
}
I need some help to modify the functions above in order to send and receive an array of bytes in addition to strings. I want to make a call like this:
Send("CONNECTED | 15.21.21.32", myByteArray);
Just use Stream - no need for a writer here. Basic sending is simple:
stream.Write(data, 0, data.Length);
However you probably need to think about "framing", i.e. how it knows where each sun-message starts and ends. With strings this is often special characters (maybe new line) - but this is rarely possible in raw binary. A common appoach is to proceed the message with the number f bytes to follow, in a pre-defined way (maybe network-byte-order fixed 4 byte unsigned integer, for example).
Reading: again, use the Stream Read method, but understand that you always need t check the return value; just because you say "read at most 20 bytes" doesn't mean you get that many, even if more is coming - you could read 3,3,3,11 bytes for example (unlikely, but you see what I mean). For example, to read exactly 20 bytes:
var buffer = new byte[...];
int count = 20, read, offset = 0;
while(count > 0 && ((read = source.Read(buffer, offset, count)) > 0) {
offset += read;
count -= read;
}
if(count != 0) throw new EndOfStreamException();
Since you seem new to networking you might want to use WCF or another framework. I've just written an article about my own framework: http://blog.gauffin.org/2012/05/griffin-networking-a-somewhat-performant-networking-library-for-net
You need to use a header for your packets as Mark suggested, since TCP uses streams and not packets. i.e. there is not 1-1 relation between send and receive operations.
This is the same problems I'm having. I only code the client and the server accepts byte arrays as proper data. The messages start with an ASCII STX character preceded by a bunch of bytes of any values except the STX and ETX characters. The message ends with a ETX ASCII CHARACTER. In C I could do this in my sleep, but I'm learning C# on the job. I don't understand why you would send bunches of double byte unicodes when single byte ASCII codes work just as well. Wasting double the bandwidth.

Async Sockets, Receive multiple files on same connection

I have a server that is going to transfer multiple files to a client over a single connection. The packet from server is in the following format:
unique_packet_id | file_content
I have onDataReceived function which I need to work like this:
public class TRACK_ID {
public string id;
public string unknown_identifier;
}
List<TRACK_ID> TRACKER = new List<TRACK_ID>();
public void OnDataReceived(IAsyncResult asyn)
{
try
{
log("OnDataReceived");
SocketPacket theSockId = (SocketPacket)asyn.AsyncState;
int iRx = theSockId.thisSocket.EndReceive(asyn);
// .. read the data into data_chunk
// if seperator found, that means we got an first chunk with the id
if (data_chunk.Contains("|") == true)
{
// extract unique_packet_id from the data
// bind unique_packet_id to an some kind of identifier? how!!??
TRACK_ID new_track = new TRACK_ID();
new_track.id = unique_packet_id;
new_track.unknown_identifier = X;
TRACKER.add(new_track);
} else {
// no seperator found - we're getting the rest of the data
// determinate the unique_packet_id of the incoming data so we can distinguish data/files
string current_packet_id = "";
for(int i=0; i<TRACKER.count; i++){
if(TRACKER[i].unknown_identifier == X){
current_packet_id = TRACKER[i].id; // we found our packet id!
break;
}
}
// now we got our packet_id and know where to store the buffer
}
WaitForData..
}
}
I need a variable X that will allow me to track where to store each incoming buffer
If I closed connection for each file, I could bind unique_packet_id to socket_id (socket_id would be X), but since I'm using the same connection, socket_id always stays the same so I have to use something else for this X variable.
Only other solution I can think of is sending the unique_packet_id in each chunk of data. But that seems like not the best way to do it. Then I would have to split the file buffer into chunks and append the id to each chunk. Any other ways how to accomplish this? Thanks!
You didn't say if you're using a stream socket or a datagram socket.
If you're using a datagram socket (e.g. you're using UDP/IP) then you will always receive a whole packet all at once, so you can identify the data because it goes with the unique_packet_id that was found before the | at the beginning of the current packet.
If you're using a stream socket (e.g. you're using TCP/IP) then I think you have a problem. Your packet format isn't delimited or escaped, so how will you know where one packet ends and the next one begins?
If you are using a stream socket, you need to use, for example, a packet format like this:
unique packet ID (say, in ASCII, terminated with CRNL —­ or whatever you choose)
content length (same format)
packet payload
The receiver can find the end of the packet because it knows how many bytes will be part of the payload.
You will also need to be prepared for the case where you get one of your packets in small pieces. For example, your callback function might be called once with part of the unique packet ID, called again with the rest of the header and part of the payload, and again with the rest of the payload and the complete following packet tacked on to the end. Or you may get three whole packets and part of a fourth in a single call to your callback function.
The other possible solution you mention, that of sending the unique_packet_id in each chunk of data, is not possible, because the sender doesn't know how the data will be chunked up when it is delivered to the receiver.

Categories