I have a while loop that begins when I press a button. I'm using it to maintain communication with a service on another computer. The problem is that when I get the "Completed" message back from the other computer I need it to break out of the while loop and stop listening till the button is pressed again. Nothing I do seems to break it out of the loop.
Please note the entire process is executed in its own thread.
I tried putting break; just before the end of the first case in the switch, no such luck and I'm not sure if it's because it's a switch statement that expects a break; between cases or what the reason is. I also tried putting return; in there but it still won't break out. I end up having to close the application and restart it to use the button again.
TcpClient client = new TcpClient(serverIP, 11000);
NetworkStream stream = client.GetStream();
Byte[] bytes = new Byte[256];
String data = null;
int i;
stream.Write(copy, 0, copy.Length);
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
//MessageBox.Show(data);
switch (data)
{
case "Completed":
this.Invoke((MethodInvoker)delegate
{
progressBar1.Value = 0;
progressBar1.Visible = false;
progressBar1.Update();
if (prod)
{
sqlLink.setProdFile(imageName, destFileName);
} else
{
sqlLink.setTestFile(imageName, destFileName);
if (sqlLink.getTestVM(imageName) != "")
{
if (message.Text("Test VM", "Power on specified Virtual Machine in private mode?", MessageBoxButtons.OKCancel) == DialogResult.OK)
{
PS ps = new PS();
ps.powerOnVM(sqlLink.getTestVM(imageName));
}
}
}
//Tried putting break; here.
});
break;
case "FIU":
{
progressBar1.Value = 0;
progressBar1.Visible = false;
progressBar1.Update();
message.Text("Error", "The image is in use. Try shutting down machines or unassigning devices.", MessageBoxButtons.OK);
}
break;
case "DSF":
{
progressBar1.Value = 0;
progressBar1.Visible = false;
progressBar1.Update();
message.Text("Error", "Drive space is full on production volume. Try deleting some older images.", MessageBoxButtons.OK);
}
break;
default:
this.Invoke((MethodInvoker)delegate
{
progressBar1.Value = Int16.Parse(data);
progressBar1.Update();
});
break;
}
}
stream.Close(); //This never happens.
client.Close();
}
catch (Exception ex)
{
message.Text("Error", "Copy Method Error: " + ex.Message, MessageBoxButtons.OK);
}
Define a boolean variable that will hold the fact that you received the "Completed" message.
When you'll be entering the next iteration, if this value is true, then you break and you'll be getting out of your loop.
Example :
// Abbreviated
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
bool breakTheWhile = false;
switch (data)
{
case "Completed":
// Abbreviated
breakTheWhile = true;
break;
case "FIU":
// Abbreviated
break;
case "DSF":
// Abbreviated
break;
default:
// Abbreviated
break;
}
if (breakTheWhile)
break;
}
Related
I have a system in which I read the serial port from a X,Y,Z motion stage, meaning that I send a signal (via usb) to a function which reads the signal, moves a stepper motor accordingly, then reads the next stepper motor signal and so on. At the moment, this function looks like this:
public void SCPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
string sCurString = "";
//Loop to receive the data from serial port
System.Threading.Thread.Sleep(150);
sCurString = SCPort.ReadExisting();
if (sCurString != "")
StrReceiver = sCurString;
if (BlnSet == true)
{
if (StrReceiver.Length == 3)
{
if (StrReceiver.Substring(StrReceiver.Length - 3) == "OK\n")
BlnReadCom = true;
}
else if (StrReceiver.Length == 4)
{
if (StrReceiver.Substring(StrReceiver.Length - 3) == "OK\n" || StrReceiver.Substring(StrReceiver.Length - 4) == "OK\nS")
BlnReadCom = true;
}
else
{
if (StrReceiver.Substring(StrReceiver.Length - 3) == "OK\n" || StrReceiver.Substring(StrReceiver.Length - 4) == "OK\nS" ||
StrReceiver.Substring(StrReceiver.Length - 5) == "ERR1\n" || StrReceiver.Substring(StrReceiver.Length - 5) == "ERR3\n" ||
StrReceiver.Substring(StrReceiver.Length - 5) == "ERR4\n" || StrReceiver.Substring(StrReceiver.Length - 5) == "ERR5\n")
BlnReadCom = true;
}
}
else
{
if (StrReceiver.Substring(StrReceiver.Length - 1, 1) == "\n")
BlnReadCom = true;
}
}
catch (Exception ex)
{
MessageBox.Show("Failed to receive data", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
This function reads the serialport SCPort, every 150ms indicated by the Thread.Sleep. If I try to read the data any faster, I get the exception (which is likely an indication of the limitation of my system). Fine. However, this exception is not thrown immediately, but every once in a while. What I would like to do instead of waiting a fixed time between reading the signal, is to each time wait until the serialport is ready and then read it. This should speed up my system, as instead of waiting 150ms between every movement, I could wait exactly the amount of time the system requires.
The question is: how do I implement this behavior in the function?
I have not tried to solve it on my own, because I really have no idea about how to do this. Will be happy to implement this into my function, but at a bare minimum I need to be pointed in the right direction.
This function handles connection to the port SCPort.
public void ConnectPort(short sPort)
{
if (SCPort.IsOpen == true)
{
ClosePort();
buttonConnectStage.Text = "Connect stage";
}
else if (SCPort.IsOpen == false)
{
SCPort.PortName = comStage.SelectedItem.ToString(); //Set the serial port number
SCPort.BaudRate = 9600; //Set the bit rate
SCPort.DataBits = 8; //Set the data bits
SCPort.StopBits = StopBits.One; //Set the stop bit
SCPort.Parity = Parity.None; //Set the Parity
SCPort.ReadBufferSize = 2048;
SCPort.WriteBufferSize = 1024;
SCPort.DtrEnable = true;
SCPort.Handshake = Handshake.None;
SCPort.ReceivedBytesThreshold = 1;
SCPort.RtsEnable = false;
//This delegate should be a trigger event for fetching data asynchronously, it will be triggered when there is data passed from serial port.
SCPort.DataReceived += new SerialDataReceivedEventHandler(SCPort_DataReceived); //DataReceivedEvent delegate
try
{
SCPort.Open(); //Open serial port
if (SCPort.IsOpen)
{
StrReceiver = "";
BlnBusy = true;
BlnSet = false;
SendCommand("?R\r"); //Connect to the controller
Delay(250);
BlnBusy = false;
if (StrReceiver == "?R\rOK\n")
{
displayValues();
BlnConnect = true; //Connected successfully
ShrPort = sPort; //Setial port number
buttonConnectStage.Text = "Disconnect stage";
SendCommand("V" + sSpeed.ToString() + "\r"); //Set speed
}
else
{
BlnBusy = false;
BlnConnect = false;
buttonConnectStage.Text = "Failed to connect";
MessageBox.Show("Failed to connect", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
}
}
catch (Exception ex)
{
}
}
}
I read data from the serial port and parse it in a separate class. However data is incorrectly parsed and some samples are repeated while others are missing.
Here is an example of the parsed packet. It starts with the packetIndex (shoudl start from 1 and incrementing). You can see how the packetIdx repeats and some of the other values repeat as well. I think that's due to multithreading but I'm not sure how to fix it.
2 -124558.985180734 -67934.4168823262 -164223.049786454 -163322.386243628
2 -124619.580759952 -67962.535376851 -164191.757344217 -163305.68949052
3 -124685.719571795 -67995.8394760894 -164191.042088394 -163303.119039907
5 -124801.747477263 -68045.7062179692 -164195.288919841 -163299.140429394
6 -124801.747477263 -68045.7062179692 -164221.105184687 -163297.46404856
6 -124832.8387538 -68041.9287731563 -164214.936103217 -163294.983004926
This is what I should receive:
1 -124558.985180734 -67934.4168823262 -164223.049786454 -163322.386243628
2 -124619.580759952 -67962.535376851 -164191.757344217 -163305.68949052
3 -124685.719571795 -67995.8394760894 -164191.042088394 -163303.119039907
4 -124801.747477263 -68045.7062179692 -164195.288919841 -163299.140429394
...
This is the SerialPort_DataReceived
public void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
lock (_lock)
{
byte[] buffer = new byte[_serialPort1.BytesToRead];
_serialPort1.Read(buffer, 0, buffer.Length);
for (int i = 0; i < buffer.Length; i++)
{
//Parse data
double[] samplesAtTimeT = DataParserObj.interpretBinaryStream(buffer[i]);
//Add data to BlockingCollection when parsed
if (samplesAtTimeT != null)
_bqBufferTimerSeriesData.Add(samplesAtTimeT);
}
}
}
And the class that parses the data:
public class DataParser
{
private int packetSampleCounter = 0;
private int localByteCounter = 0;
private int packetState = 0;
private byte[] tmpBuffer = new byte[3];
private double[] ParsedData = new double[5]; //[0] packetIdx (0-255), [1-4] signal
public double[] interpretBinaryStream(byte actbyte)
{
bool returnDataFlag = false;
switch (packetState)
{
case 0: // end packet indicator
if (actbyte == 0xC0)
packetState++;
break;
case 1: // start packet indicator
if (actbyte == 0xA0)
packetState++;
else
packetState = 0;
break;
case 2: // packet Index
packetSampleCounter = 0;
ParsedData[packetSampleCounter] = actbyte;
packetSampleCounter++;
localByteCounter = 0;
packetState++;
break;
case 3: //channel data (4 channels x 3byte/channel)
// 3 bytes
tmpBuffer[localByteCounter] = actbyte;
localByteCounter++;
if (localByteCounter == 3)
{
ParsedData[packetSampleCounter] = Bit24ToInt32(tmpBuffer);
if (packetSampleCounter == 5)
packetState++; //move to next state, end of packet
else
localByteCounter = 0;
}
break;
case 4: // end packet
if (actbyte == 0xC0)
{
returnDataFlag = true;
packetState = 1;
}
else
packetState = 0;
break;
default:
packetState = 0;
break;
}
if (returnDataFlag)
return ParsedData;
else
return null;
}
}
Get rid of the DataReceived event and instead use await serialPort.BaseStream.ReadAsync(....) to get notified when data comes in. async/await is much cleaner and doesn't force you into multithreaded data processing. For high speed networking, parallel processing is great. But serial ports are slow, so extra threads have no benefit.
Also, BytesToRead is buggy (it does return the number of queued bytes, but it destroys other state) and you should never call it.
Finally, do NOT ignore the return value from Read (or BaseStream.ReadAsync). You need to know how bytes were actually placed into your buffer, because it is not guaranteed to be the same number you asked for.
private async void ReadTheSerialData()
{
var buffer = new byte[200];
while (serialPort.IsOpen) {
var valid = await serialPort.BaseStream.ReadAsync(buffer, 0, buffer.Length);
for (int i = 0; i < valid; ++i)
{
//Parse data
double[] samplesAtTimeT = DataParserObj.interpretBinaryStream(buffer[i]);
//Add data to BlockingCollection when parsed
if (samplesAtTimeT != null)
_bqBufferTimerSeriesData.Add(samplesAtTimeT);
}
}
}
Just call this function after opening the port and setting your flow control, timeouts, etc. You may find that you no longer need the blocking queue, but can just handle the contents of samplesAtTimeT directly.
Data supposed to be received should be something like"-31.12345 167.12345", but sometimes I received data like "2.378999E11 3.593719E"? I have checked my port setting they are using same parameter. My code as following:
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
byte rxChar=0;
byte[] rxDataBuffer = new byte[240];
int rxdata = 0;
rxNumberDataBytes = (byte)serialPort.BytesToRead;
//SerialPort sp = (SerialPort)sender;
//rxdata = sp.Read(rxDataBuffer, 0, 8);
while (serialPort.BytesToRead > 0)
{
rxChar = (byte)serialPort.ReadByte(); // read data
// to display bytes received in hex,check was all data received
Invoke(new Action(() =>
{
textBox3.Text += " " + rxChar.ToString("X") + "\r\n";
}));
switch (rxState)
{
case RxIdleState:
if (rxChar == 0x5A)
{
rxState = RxInstructionState;
}
break;
case RxInstructionState:
rxInstruction = rxChar;
rxState = RxNumberofbytesState;
break;
case RxNumberofbytesState:
rxChar = rxNumberDataBytes;
rxState = RxDataState;
rxdata = 0;
break;
case RxDataState:
// count number of rxdata until 8 bytes real data
rxDataBuffer[rxdata] = rxChar;
rxdata++;
if (rxdata == 8)
{
float f11 = BitConverter.ToSingle(rxDataBuffer, 0);
float f22 = BitConverter.ToSingle(rxDataBuffer, 4);
Invoke(new Action(() =>
{
textBox3.Text += f11.ToString() + " " + f22.ToString() + "\r\n";
}));
rxState = RxStopState;
}
break;
case RxStopState:
if (rxChar == 0x2C)
{
rxState = RxIdleState;
}
break;
default:
rxState = RxIdleState;
break;
}
}
}
}
I think you're grabbing wrong data due to a wrong set operation.
In the following, you are setting the number of bytes you want and then setting the rxChar to the first available byte in the stream. That seems fine.
rxNumberDataBytes = (byte)serialPort.BytesToRead;
while (serialPort.BytesToRead > 0)
{
rxChar = (byte)serialPort.ReadByte(); // read data
....
}
However, in the following, you are then ignoring that rxChar value and setting it to the number of bytes that were available for reading.
case RxNumberofbytesState:
rxChar = rxNumberDataBytes; // <--- This is correct?
rxState = RxDataState;
rxdata = 0;
break;
You are then using the new rxChar value to add to your rxDataBuffer instead of the first byte that was available in the serial port.
Looking at the flow of the code, the rest of the 7 bytes look like they will be correct, but the first one will always be wrong.
May also just be floating point precision issue ;)
this is going to sound silly but for some reason this code seems to skip over the read. Or maybe it is going too fast? I'm trying to get a telnet response from the server and I get the first line response but nothing more. It doesn't matter how many readline() I put or if I try to sleep the thread. What can I do to get the whole response from the server to be printed? I think I narrowed the problem down to exiting. In the sample program if you hard code the exit, it immediately ends the loop and displays nothing. I tried sleeping the thread but that just seemed to stop everything.
Output:
220 server-12.tower-558.messagelabs.com ESMTP
Expected Output:
250-server-11.tower-555.messagelabs.com says EHLO to iphere
250-PIPELINING
250-8BITMIME
250-STARTTLS
My Code:
//Telnet Start
IPHostEntry hostInfo = Dns.Resolve(list[j]);
TelnetConnection tc = new TelnetConnection(hostInfo.AddressList[0].ToString(), 25);
string prompt = "a";
string consoleout = "";
// while connected
while (tc.IsConnected && prompt != "exit")
{
// display server output
prompt = "ehlo a.com";
tc.WriteLine(prompt);
//I've tried adding a 2 or 5 second thread sleep here and I still get the same result.
Console.Write(tc.Read());
prompt = "exit";
}
//Telnet End
TelnetConnection Class:
// minimalistic telnet implementation
// conceived by Tom Janssens on 2007/06/06 for codeproject
//
// http://www.corebvba.be
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
namespace MinimalisticTelnet
{
enum Verbs {
WILL = 251,
WONT = 252,
DO = 253,
DONT = 254,
IAC = 255
}
enum Options
{
SGA = 3
}
class TelnetConnection
{
TcpClient tcpSocket;
int TimeOutMs = 100;
public TelnetConnection(string Hostname, int Port)
{
try
{
tcpSocket = new TcpClient(Hostname, Port);
}
catch (SocketException e)
{
Console.Write(e);
}
}
public string Login(string Username,string Password,int LoginTimeOutMs)
{
int oldTimeOutMs = TimeOutMs;
TimeOutMs = LoginTimeOutMs;
string s = Read();
if (!s.TrimEnd().EndsWith(":"))
throw new Exception("Failed to connect : no login prompt");
WriteLine(Username);
s += Read();
if (!s.TrimEnd().EndsWith(":"))
throw new Exception("Failed to connect : no password prompt");
WriteLine(Password);
s += Read();
TimeOutMs = oldTimeOutMs;
return s;
}
public void WriteLine(string cmd)
{
Write(cmd + "\n");
}
public void Write(string cmd)
{
if (!tcpSocket.Connected) return;
byte[] buf = System.Text.ASCIIEncoding.ASCII.GetBytes(cmd.Replace("\0xFF","\0xFF\0xFF"));
tcpSocket.GetStream().Write(buf, 0, buf.Length);
}
public string Read()
{
if (!tcpSocket.Connected) return null;
StringBuilder sb=new StringBuilder();
do
{
ParseTelnet(sb);
System.Threading.Thread.Sleep(TimeOutMs);
} while (tcpSocket.Available > 0);
return sb.ToString();
}
public bool IsConnected
{
get { return tcpSocket.Connected; }
}
void ParseTelnet(StringBuilder sb)
{
while (tcpSocket.Available > 0)
{
int input = tcpSocket.GetStream().ReadByte();
switch (input)
{
case -1 :
break;
case (int)Verbs.IAC:
// interpret as command
int inputverb = tcpSocket.GetStream().ReadByte();
if (inputverb == -1) break;
switch (inputverb)
{
case (int)Verbs.IAC:
//literal IAC = 255 escaped, so append char 255 to string
sb.Append(inputverb);
break;
case (int)Verbs.DO:
case (int)Verbs.DONT:
case (int)Verbs.WILL:
case (int)Verbs.WONT:
// reply to all commands with "WONT", unless it is SGA (suppres go ahead)
int inputoption = tcpSocket.GetStream().ReadByte();
if (inputoption == -1) break;
tcpSocket.GetStream().WriteByte((byte)Verbs.IAC);
if (inputoption == (int)Options.SGA )
tcpSocket.GetStream().WriteByte(inputverb == (int)Verbs.DO ? (byte)Verbs.WILL:(byte)Verbs.DO);
else
tcpSocket.GetStream().WriteByte(inputverb == (int)Verbs.DO ? (byte)Verbs.WONT : (byte)Verbs.DONT);
tcpSocket.GetStream().WriteByte((byte)inputoption);
break;
default:
break;
}
break;
default:
sb.Append( (char)input );
break;
}
}
}
}
}
UPDATE:
Using the following loop I was able to get it to work once, but only once it would not iterate through the count of j when I verified that j was 2. My output, expected output and function are below.
Function:
for (int j = 0; j < list.Count; j++)
{
//Telnet Start
Console.WriteLine("On round #" + j);
IPHostEntry hostInfo = Dns.Resolve(list[j]);
TelnetConnection tc = new TelnetConnection(hostInfo.AddressList[0].ToString(), 25);
string prompt = "a";
string consoleout = "";
// while connected
while (tc.IsConnected && prompt != "exit")
{
// display server output
Console.Write(tc.Read());
// send client input to server
prompt = "ehlo a.com";
tc.WriteLine(prompt);
// display server output
consoleout = tc.Read();
Console.Write(consoleout);
//send exit input to server
prompt = "exit";
tc.WriteLine(prompt);
Console.Write(tc.Read());
}
Console.WriteLine("**DISCONNECTED**");
//Telnet End
if (consoleout.IndexOf("STARTTLS")>-1)
{
if (j == 0)
{
if (list[j].Contains(domains[i]))
dataGridView1.Rows.Add(domains[i], list[j], "Y", numberemployees, "Y");
else
dataGridView1.Rows.Add(domains[i], list[j], "Y", numberemployees, "N");
}
else
{
if (list[j].Contains(domains[i]))
dataGridView1.Rows.Add(null, list[j], "Y", null, "Y");
else
dataGridView1.Rows.Add(null, list[j], "Y", null, "N");
}
}
else
{
if (j == 0)
{
if (list[j].Contains(domains[i]))
dataGridView1.Rows.Add(domains[i], list[j], "N", numberemployees, "Y");
else
dataGridView1.Rows.Add(domains[i], list[j], "N", numberemployees, "N");
}
else
{
if (list[j].Contains(domains[i]))
dataGridView1.Rows.Add(null, list[j], "N", null, "Y");
else
dataGridView1.Rows.Add(null, list[j], "N", null, "N");
}
}
}
Output:
On round #0
True220 server-6.tower-95.messagelabs.com ESMTP
250-server-6.tower-95.messagelabs.com
250-STARTTLS
250-PIPELINING
250 8BITMIME
502 unimplemented (#5.5.1)
**DISCONNECTED**
On round #1
True220 server-14.tower-558.messagelabs.com ESMTP
**DISCONNECTED**
Expected Output:
On round #0
True220 server-6.tower-95.messagelabs.com ESMTP
250-server-6.tower-95.messagelabs.com
250-STARTTLS
250-PIPELINING
250 8BITMIME
502 unimplemented (#5.5.1)
**DISCONNECTED**
On round #1
True220 server-14.tower-558.messagelabs.com ESMTP
250-STARTTLS
250-PIPELINING
250 8BITMIME
**DISCONNECTED**
So for whatever reason, it simply won't do the second loop. But it also is starting to depend on the server. Some servers respond with a little information and some I get something like this, which makes it look like nothing happened at all.
On round #0
True220 SMTP Proxy Server Ready
**DISCONNECTED**
On round #1
True220 SMTP Proxy Server Ready
**DISCONNECTED**
On round #2
True220 SMTP Server Ready
**DISCONNECTED**
On round #3
True220 SMTP Server Ready
**DISCONNECTED**
On round #4
True220 SMTP Proxy Server Ready
**DISCONNECTED**
On round #5
True220 SMTP Proxy Server Ready
**DISCONNECTED**
Kyle,
It looks like you need to open the socket:
public string Read()
{
if (!tcpSocket.Connected)
{
throw new Exception("Socket is Closed.");
}
StringBuilder sb=new StringBuilder();
do
{
ParseTelnet(sb);
System.Threading.Thread.Sleep(TimeOutMs);
} while (tcpSocket.Available > 0);
return sb.ToString();
}
It isn't open, so it is returning.
[UPDATE]
Try looping through IPHostEntry hostInfo = Dns.Resolve(list[j]); before your //Telnet Start comment:
private const int PORT25 = 25; // I hate magic numbers
foreach (var item in list) {
var hostInfo = Dns.Resolve(item);
Console.WriteLine(hostInfo);
foreach (var address in hostInfo.AddressList) {
var tc = new TelnetConnection(address, PORT25);
Console.WriteLine("{0} TelnetConnection Connected: {1}", address, tc.IsConnected);
}
}
[Update 2]
This is really hard to debug, not knowing what all you are trying to connect to and what else is going on.
That said, let's try the following:
In your class MinimalisticTelnet, add this method:
public void Close() {
if (tcpSocket != null) {
tcpSocket.Close();
}
}
I don't see it anywhere else, and that could be causing some issues the next time you attempt your second connection.
Now, in your test code, add the new one line of code after the while loop:
while (tc.IsConnected && prompt != "exit") {
// display server output
Console.Write(tc.Read());
// send client input to server
prompt = "ehlo a.com";
tc.WriteLine(prompt);
// display server output
consoleout = tc.Read();
Console.Write(consoleout);
//send exit input to server
prompt = "exit";
tc.WriteLine(prompt);
Console.Write(tc.Read());
}
tc.Close();
Console.WriteLine("**DISCONNECTED**");
With any luck, the reason your 2nd connection was failing was because you still had an open connection.
I solve a so similar problem adding 3 reads, then 8 seconds timeout and finally reading again AND SHOWING IN MY RICHTEXBOX:
string r1 = conexion.Read();
string r2 = conexion.Read();
string r3 = conexion.Read();
System.Threading.Thread.Sleep(8000);
richtextbox.AppendText(r1 + r2 + r3);
I hope it can help you!
Tell me if it works to you.
ALL,
Have the following problem.
First, here is my code:
class InternetConnector
{
private static ManualResetEvent receiveDone = new ManualResetEvent( false );
public static ManualResetEvent processingDone = new ManualResetEvent( false );
public static ConcurrentQueue<string> messages = new ConcurrentQueue<string>();
public static bool Connected;
public bool ReceiveMessage(Action<Socket> successHandler, Action<Exception> errorHandler)
{
bool receive = false;
ClientStateObject obj = new ClientStateObject();
obj.server = client;
var connectionData = new ConnectionData
{
ErrorHandler = errorHandler,
SuccessHandler = successHandler,
Socket = client,
clientObj = obj
};
if (Connected)
{
client.BeginReceive(connectionData.clientObj.buffer, 0, ClientStateObject.bufSize, 0, new AsyncCallback(ReceiveCallback), connectionData);
receive = true;
receiveDone.WaitOne();
}
return receive;
}
private static void ReceiveCallback(IAsyncResult ar)
{
ConnectionData connectionData = new ConnectionData();
bool complete = false;
try
{
connectionData = (ConnectionData)ar.AsyncState;
Socket client = connectionData.Socket;
int num = client.EndReceive(ar);
{
connectionData.clientObj.stringBuffer.Append(Encoding.ASCII.GetString(connectionData.clientObj.buffer, 0, num));
string response = connectionData.clientObj.stringBuffer.ToString();
if (response.EndsWith("&"))
complete = true;
string[] msgs = response.Split('&');
int j = 0;
if (!complete)
j++;
for (int i = 0; i < msgs.Count() - j; i++)
{
string sts = msgs[i];
if (i == 0 && receivingMessage != String.Empty)
{
sts = receivingMessage + sts;
messages.Enqueue(sts + "&" );
receivingMessage = String.Empty;
}
else
messages.Enqueue(sts + "&");
}
if (!complete)
receivingMessage += msgs[msgs.Count() - 1];
else
receivingMessage = String.Empty;
receiveDone.Set();
if (connectionData.SuccessHandler != null)
{
connectionData.SuccessHandler(client);
processingDone.WaitOne();
client.BeginReceive(connectionData.clientObj.buffer, 0, ClientStateObject.bufSize, 0, new AsyncCallback(ReceiveCallback), connectionData);
}
}
}
catch (Exception e)
{
if (connectionData.ErrorHandler != null)
connectionData.ErrorHandler(e);
}
}
}
And here is the success handler:
public partial class Form1 : Form
{
private void AsyncSuccessHandler(Socket socket)
{
if (InvokeRequired)
{
BeginInvoke(new Action( () => AsyncSuccessHandler( socket ) ));
return;
}
if (InternetConnector.messages.Count() == 0)
{
status.Text = "Signals Receiver: Connected";
status.ForeColor = Color.Green;
startStop.Text = "Stop";
isRunning = true;
create.Enabled = true;
SetGUIColorsAndValues();
client.SendMessageToConnector("First Signal Pass4&");
client.ReceiveMessage(AsyncSuccessHandler, AsyncErrorHandler);
}
else
{
SetGUIColorsAndValues();
GUIChangeOnConnection();
InternetConnector.processingDone.Set();
}
}
private void GUIChangeOnConnection()
{
for( int i = 0; i < InternetConnector.messages.Count; i++ )
{
string message;
InternetConnector.messages.TryDequeu( out message );
// process message
}
}
}
Problem:
I need to continuosly read the data from the server and display them in the GUI. I also need the GUI to be responsive to i.e. button clicks.
I also need to start n read iteration when n-1 read iteration has been finished processing and the concurrentqueue object in my code is empty.
However going thru the code in debugger I can see that BeginReceive() call in the reading callback does not wait till the end of success handler and the queue is not empty.
What am I missing? Is there a better way to achieve this?
I am also aware of SignalR library but at this pont I don't want to use any 3rd party library.
Any help in this matter appreciated.
Thank you.
[EDIT]
Do I understand Amit's reply correctly:
class InternetConnector
{
private static ManualResetEvent processingDone = new ManualResetEvent( false );
private static void ReceiveCallback(IAsyncResult ar)
{
ConnectionData connectionData = new ConnectionData();
bool complete = false;
try
{
connectionData = (ConnectionData)ar.AsyncState;
Socket client = connectionData.Socket;
int num = client.EndReceive(ar);
{
connectionData.clientObj.stringBuffer.Append(Encoding.ASCII.GetString(connectionData.clientObj.buffer, 0, num));
string response = connectionData.clientObj.stringBuffer.ToString();
if (response.EndsWith("&"))
complete = true;
string[] msgs = response.Split('&');
int j = 0;
if (!complete)
j++;
for (int i = 0; i < msgs.Count() - j; i++)
{
string sts = msgs[i];
if (i == 0 && receivingMessage != String.Empty)
{
sts = receivingMessage + sts;
messages.Enqueue(sts + "&" );
receivingMessage = String.Empty;
}
else
messages.Enqueue(sts + "&");
}
if (!complete)
receivingMessage += msgs[msgs.Count() - 1];
else
receivingMessage = String.Empty;
receiveDone.Set();
if (connectionData.SuccessHandler != null)
{
processingDone.WaitOne();
connectionData.SuccessHandler(client);
processingDone.Set();
client.BeginReceive(connectionData.clientObj.buffer, 0, ClientStateObject.bufSize, 0, new AsyncCallback(ReceiveCallback), connectionData);
}
}
}
catch (Exception e)
{
if (connectionData.ErrorHandler != null)
connectionData.ErrorHandler(e);
}
}
}
?
[/EDIT]
[EDIT 2]
Please see updated code.
When I set a breakpoint at the very end of the GUIChangeOnConnection() - line with the "}" - I see that the queue does have some items.
I will try to change ManualResetEvent, in the meantime.
[/EDIT 2]
If I have understood the problem correctly, you are referring to a problem at the following point of code:
if (connectionData.SuccessHandler != null)
{
connectionData.SuccessHandler(client);
client.BeginReceive(connectionData.clientObj.buffer, 0, ClientStateObject.bufSize, 0, new AsyncCallback(ReceiveCallback), connectionData);
}
If your intention is that you hit server only when you have finished processing previous data then the SuccessHandler must be blocking (as is obvious from your problem statement as well).
Now take a look at the following lines in your SuccessHandler
if (InvokeRequired)
{
BeginInvoke(new Action( () => AsyncSuccessHandler( socket ) ));
return;
}
The above code defers the execution of your SuccessHandler so that the UI remains responsive. Or in other words, your SuccessHandler is not blocking.
Since you are updating GUI from the SuccessHandler, it is also important to run it from UI thread (which it is correctly doing right now).
So in order to make the SuccessHandler blocking, you can make the following code itself run on UI thread using InvokeRequired mechanism
if (connectionData.SuccessHandler != null)
{
connectionData.SuccessHandler(client);
client.BeginReceive(connectionData.clientObj.buffer, 0, ClientStateObject.bufSize, 0, new AsyncCallback(ReceiveCallback), connectionData);
}
Since client.BeginReceive is async it will not block the UI thread and you anyway wanted to run SuccessHandler on UI thread..
If you don't have access to the Form in ReceiveCallback, you can use a waithandle to block call to client.BeginReceive till SuccessHandler has finished.
private static AutoResetEvent _processingDone = new AutoResetEvent( false );
//Introduce an internal property ProcessingDone to access _processingDone
//This should be in a class which you can access both from 'Form1' and the class containing ReceiveCallBack
//In ReceiveCallBack
if (connectionData.SuccessHandler != null)
{
connectionData.SuccessHandler(client);
ProcessingDone.WaitOne();
client.BeginReceive(connectionData.clientObj.buffer, 0, ClientStateObject.bufSize, 0, new AsyncCallback(ReceiveCallback), connectionData);
}
ProcessingDone.Set() must be done from within SuccessHandler at a point where you can determine that SuccessHandler has finished and client.BeginReceive needs to be invoked.
This is internal to your logic. From the code you have shared, it is difficult to pin point the place where you need to put ProcessingDone.Set().