StreamReader doesn't retrieve everything from NetworkStream (TCP and C#) - c#

I'm working on a project, where you have to retrieve data from a server and display it in the UI. It's a newsgroups server, that contains about 250 groups in total.
The output from the server SHOULD as far as I understand, be stored in a NetworkStream object and is read from a StreamReader which saves each line into a string.
This works, but unfortunately it seems like it doesn't finish reading everything before finishing the method call.
Next time I call another command and read it from the StreamReader it then returns the rest of the output from the previous command.
I've been struggling with this for hours now, and can't see how to fix this.
This is my code:
public ObservableCollection<Newsgroup> GetNewsGroups()
{
ObservableCollection<Newsgroup> newsgroups = new ObservableCollection<Newsgroup>();
if(connectionStatus.Equals(ConnectionStatus.CONNECTED) && loginStatus.Equals(LoginStatus.LOGGED_IN))
{
byte[] sendMessage = Encoding.UTF8.GetBytes("LIST\n");
// Write to the server
ns.Write(sendMessage, 0, sendMessage.Length);
Console.WriteLine("Sent {0} bytes to server...", sendMessage.Length);
ns.Flush();
// b) Read from the server
reader = new StreamReader(ns, Encoding.UTF8);
// We want to ignore the first line, as it just contains information about the data
string test = reader.ReadLine();
Console.WriteLine(test);
string recieveMessage = "";
if (ns.CanRead)
{
while (reader.Peek() >= 0)
{
recieveMessage = reader.ReadLine();
Console.WriteLine("Got this message {0} back from the server", recieveMessage);
// This part will simply remove the annoying numbers after the newsgroup name
int firstSpaceIndex = recieveMessage.IndexOf(" ");
string refactoredGroupName = recieveMessage.Substring(0, firstSpaceIndex);
newsgroups.Add(new Newsgroup { GroupName = refactoredGroupName });
}
}
}
return newsgroups;
}

I'd be interested to see what information about the data you are throwing away on the first line (what's in "test" variable). If it tells you how many bytes are coming your way, you should use that information to retrieve the correct amount of data instead of Peek.
If the last line contains a single period, change your while loop to look like this instead:
recieveMessage = reader.ReadLine();
while (recieveMessage != ".")
{
Console.WriteLine("Got this message {0} back from the server", recieveMessage); // This part will simply remove the annoying numbers after the newsgroup name int
firstSpaceIndex = recieveMessage.IndexOf(" ");
string refactoredGroupName = recieveMessage.Substring(0, firstSpaceIndex);
newsgroups.Add(new Newsgroup { GroupName = refactoredGroupName });
recieveMessage = reader.ReadLine();
}

Related

c# - Method Failing to Parse CSV File

I am trying to work through a school assignment that has us use a C# program to parse data from a CSV file and add it to a table in a local database. When I try to run the program though, the method I am using fails to parse any of the data into the object.
Here is the method I am using:
//Parse CSV line
public bool ParseCSVline(string aLine)
{
try
{
string[] fields = aLine.Split(',');
this.Item_ID = int.Parse(fields[0]);
this.Invent_id = int.Parse(fields[1]);
this.Itemsize = fields[2];
this.Color = fields[3];
this.Curr_price = decimal.Parse(fields[4]);
this.Qoh = int.Parse(fields[5]);
return true; //if everything parsed, return true
}
catch (Exception ex)
{
Console.Write("Failed to Parse");
return false; //if a parse failed, return false
}
When running the program the method keeps throwing the Exception instead of actually parsing the data. For clarity, here is the section in the Main program that is calling everything:
/Step 2 - Open input file
//Set where the file comes from
string filepath = #"C:\Users\Karlore\Documents\School\SAI-430\";
string filename = #"NewInventory.csv";
//Open reader
StreamReader theFile = new StreamReader(filepath + filename);
//Step 3 - Create an object to use
Item theItem = new Item();
//Step 4 - Loop through file and add to database
while (theFile.Peek() >= 0)
{
//Get one line and parse it inside the object
theItem.ParseCSVline(filename);
//Check to see if item is already there
if (theItem.IsInDatabase(connection))
{
continue;
}
else
{
//Add the new item to the database if it wasn’t already there
theItem.AddRow(connection);
}
} //end of while loop
If anyone can point out where I may have made an error, or point me in the right direction I would appreciate it.
Replace the line:
theItem.ParseCSVline(filename);
by:
theItem.ParseCSVline(theFile.ReadLine());

C# Serialport not receiving all the bytes in a single transmission

I'm using the following code to receive hex data over the serial port, it seems that some of these transmissions are broken up into 2 lines when they are parts of the same transmission. How do I make sure that each transmission is received properly?
public void Receiver(object sender, SerialDataReceivedEventArgs e)
{
string data;
do
{
data = COMPort.ReadExisting();
} while (COMPort.BytesToRead != 0);
RxARR = data.ToCharArray().ToList();
Dispatcher.Invoke(new MethodInvoker(Display)); // Start "Display" on the UI thread
}
You should never assume that data from a serial port is provided to you in one go (the same is true for network communication by the way). You need to make sure that your code accepts a message only if you have received everything.
Everything is hard to define. This can either be all characters until a defined termination sequence (for example \r\n or EOL byte) or until a fixed number of bytes is read. Generally one can say, as every message can be fragmented, reliable communcation is not possible without a defined end of message signal.
What we do is:
Create a StringBuilder
Read everything into that StringBuilder
Search for termination sequence
Remove everything from the StringBuilder up to and including the termination sequence
Process that chunk of data
Repeat from 3 until termination sequence is not found
Keep the remaining characters in StringBuilder, as this is the start of a new message
Pseudo code:
private StringBuilder serialBuffer = new StringBuilder();
private string terminationSequence = "\r\n"; // Anything that can't be part of a message
public void Receiver(object sender, SerialDataReceivedEventArgs e)
{
string data = COMPort.ReadExisting();
serialBuffer.Append(data);
string bufferString = serialBuffer.ToString();
int index = -1;
do
{
index = bufferString.IndexOf(terminationSequence);
if (index > -1)
{
string message = bufferString.Substring(0, index);
bufferString = bufferString.Remove(0, index + terminationSequence.Length);
HandleMessage(message);
}
}
while (index > -1)
serialBuffer = new StringBuilder(bufferString);
}
By the way: Looping within your DataReceived event is not desired, as this event is called by the SerialPort whenever something new is ready to be read. Reading in a loop may interfere with what SerialPort is doing by default. So: Don't read in a loop within that event! The "loop" is the event being fired in sequence by SerialPort.
I Had almost the same situation.
Like Thorsten said the data sometimes does not come at once..
Maybe because of Hardware or BaundRate..
What really works for me was:
var end = '\r';
int dataLength = _serialPort.BytesToRead;
byte[] data = new byte[dataLength];
int nbrDataRead = _serialPort.Read(data, 0, dataLength);
RxString = Encoding.ASCII.GetString(data);
txtCod.AppendText(RxString);
if (RxString.IndexOf((char)end) > -1)
{
this.Invoke(new EventHandler(Final));
}

Serial port string recognision

I'm having incoming data recieved over a serial port, with a serial port reader that sits there and reads all data. I want the following to happen:
When ENTER ("\r") is being received over the serial port, I want the line of data before the ENTER captured in a string, and its first letter compared with the first letter that comes after the ENTER. If they match, continue reading the serial, if they don't, then display the line of text.
Here's my solution in C#. It doesn't work and I suspect some logic errors:
public void SerialReceived_Uson()
{
// create a temporary storage string
string sTemp = "";
// add buffer to string (sCurrent)
sRecievedString += comPort.ReadExisting();
// when the last member in string is Carriage return copy the string to a temporary location (sTemp)
if (sRecievedString.EndsWith("\r"))
{
sTemp = sRecievedString;
// clear string
sRecievedString = "";
}
// add new buffer to the string
sRecievedString += comPort.ReadExisting();
// compare the starting characters of the two strings
if (sRecievedString[0] == sTemp[0])
{
// if they match, override sTemp with sCurrent and continue reading
sTemp = sRecievedString;
sRecievedString += comPort.ReadExisting();
}
// else display sTemp
else
{
MessageBox.Show(sTemp);
}
}

TCP Client - loop in communication

I am writing a program which goal is to communicate with the weight terminal using TCP client.
I'm sending specified messages (eg. checking status) and depending on the replies I'm making some another process.
First, some code.
Connection:
public static void PolaczZWaga(string IP, int port)
{
IP = IP.Replace(" ", "");
KlientTCP = new TcpClient();
KlientTCP.Connect(IPAddress.Parse(IP), port);
}
Sending message (eg. checking status)
public static string OdczytDanychZWagi(byte[] WysylaneZapytanie)
{
// Wysyłka komunikatu do podłączonego serwera TCP
byte[] GotoweZapytanie = KomunikatyWspolne.PoczatekKomunikacji.Concat(WysylaneZapytanie).Concat(KomunikatyWspolne.KoniecKumunikacji).ToArray();
NetworkStream stream = KlientTCP.GetStream();
stream.Write(GotoweZapytanie, 0, GotoweZapytanie.Length);
// Otrzymanie odpowiedzi
// Buffor na odpowiedz
byte[] odpowiedz = new Byte[256];
// String do przechowywania odpowiedzi w ASCII
String responseData = String.Empty;
// Odczyt danych z serwera
Int32 bytes = stream.Read(odpowiedz, 0, odpowiedz.Length);
responseData = System.Text.Encoding.ASCII.GetString(odpowiedz, 0, bytes);
return responseData;
}
After Form1 open I make an connection and checking status
string odp = KomunikacjaSieciowa.OdczytDanychZWagi(OdczytZWagi.Kom_RejestrStatusu);
char status = odp[0];
switch(status)
{
case 'B':
KomunikacjaSieciowa.WysylkaDoWyswietlaczaWagi_4linie(WysylkaDoWyswietlacza_Komunikaty.LogWitaj, WysylkaDoWyswietlacza_Komunikaty.LogZaloguj, WysylkaDoWyswietlacza_Komunikaty.PustaLinia, WysylkaDoWyswietlacza_Komunikaty.LogNrOperatora);
string NrOperatora = KomunikacjaSieciowa.OdczytDanychZWagi(OdczytZWagi.Kom_ZatwierdzoneF1);
//int NrOperatora_int = Convert.ToInt32(NrOperatora);
break;
// here goes next case etc
Here starts my problem - communication takes place only once and the operation requires data on the terminal. Before the operator enters data program ends.
How to change the code / loop / add a timer to repeated communication to achieve a certain status?
More specifically, as in this passage:
case 'B':
KomunikacjaSieciowa.WysylkaDoWyswietlaczaWagi_4linie(WysylkaDoWyswietlacza_Komunikaty.LogWitaj, WysylkaDoWyswietlacza_Komunikaty.LogZaloguj, WysylkaDoWyswietlacza_Komunikaty.PustaLinia, WysylkaDoWyswietlacza_Komunikaty.LogNrOperatora);
string NrOperatora = KomunikacjaSieciowa.OdczytDanychZWagi(OdczytZWagi.Kom_ZatwierdzoneF1);
repeat "string NrOperatora" depending on the returned data?
Where's the best place to make loop?? Maybe I should use thread??
I think using stream.BeginRead and checking the status when reads are complete is the best way so if the status is not OK you can call stream.BeginRead to the same method so it will be a loop calling here self until status is OK

Incomplete data received across network from C# to Arduino

I am trying to send a word over to an Arduino running as a server, from a WPF C# application. Every now and again the complete work is not sent.
C# Code
public void send(String message)
{
TcpClient tcpclnt = new TcpClient();
ConState.Content = "Connecting.....";
try
{
tcpclnt.Connect("192.168.0.177", 23);
ConState.Content = "Connected";
String str = message;
Stream stm = tcpclnt.GetStream();
ASCIIEncoding asen = new ASCIIEncoding();
byte[] ba = asen.GetBytes(str);
stm.Write(ba, 0, ba.Length);
tcpclnt.Close();
}
catch (Exception)
{
ConState.Content = "Not Connected";
return;
}
}
How it is sent to the method:
String mes = "back;";
send(mes);
Arduino code:
if (client.available() > 0) {
// Read the bytes incoming from the client:
char thisChar = client.read();
if (thisChar == ';')
{
//Add a space
Serial.println("");
}
else {
//Print because it's not a space
Serial.write(thisChar);
}
}
The Arduino is using the chat server example. I am sending "back;" and "forward;" across. The results on the serial monitor:
back
forwaback
forward
back
forwaforwar
The problem seems to be with this code:
if (client.available() > 0) {
// read the bytes incoming from the client:
char thisChar = client.read();
...
}
What it does is:
Check if we have received data from the client
Read a single byte from the client buffer
Exit, and go on to do other things
As the OP pointed out, this comes direct from Arduino chat server example. In that example, this working correctly in loop() depends on the alreadyConnected flag being set right after a new connection is made: if it isn't, then the buffer is flushed before any data is read. That's one possible landmine.
Nonetheless, there is no reason to change the if block to be a while loop in the OP's case so, in other words instead of
if (client.available() > 0) {
have
while (client.available() > 0) {
The only reason to have an if statement there is to make sure that you frequently do other processing in loop() if you have clients that send a lot of data: If the reading of client data is done from inside a while this loop will not exit until the there is no more data from the client. Since this doesn't seem to be an issue in the asked-about case, the if to while change makes sense.

Categories

Resources