C# TCP client with backgroundWorker - c#

I have a TCP client written in C#, and I use backgroundWorker to connect the server, something like this:
void ConnectToServer() {
try {
bwConnector = new BackgroundWorker();
bwConnector.DoWork += new DoWorkEventHandler(bwConnector_DoWork);
bwConnector.RunWorkerCompleted = new RunWorkerCompletedEventHandler(bwConnector_RunWorkerCompleted);
bwConnector.RunWorkerAsync();
e.Result = true;
} catch {
e.Result = false;
}
}
void bwConnector_DoWork(object sender, DoWorkEventArgs e) {
clientSocket = new Socket(xxxxxx);
clientSocket.Connect(xxxx);
this.networkStream = new NetworkStream(this.clientSocket);
this.bwReceiver = new BackgroundWorker();
this.bwReceiver.DoWork += new DoWorkEventHandler(StartReceive);
........
}
And I have a timer to check clientSocket.Connected is true or false, if false, I will call ConnectToServer() again for another connection attempt.
The problem is everytime I closed the application and reopen it, it seems that the last socket still remain there and have 2 sockets with the same IP but different ports connecting to the server.
Even I have something like this:
void bwConnector_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
((BackgroundWorker)sender).Dipose();
if (!((bool)e.Result)) {
Debug.Log("failed");
} else {
Debug.Log("success");
}
}
For disconnection, I have such codes:
System.Net.NetworkInformation.NetworkChange.NetworkAvailablilityChanged += new System.Net.NetworkInformation.NetworkAvailabilityChangedEventHandler(NetworkAvailabilityChanged);
private void NetworkChange_NetworkAvailabilityChanged(object sender , System.Net.NetworkInformation.NetworkAvailabilityEventArgs e)
{
if ( !e.IsAvailable )
{
this.OnNetworkDead(new EventArgs());
this.OnDisconnectedFromServer(new EventArgs());
}
else
this.OnNetworkAlived(new EventArgs());
}
...............
public event DisconnectedEventHandler DisconnectedFromServer;
protected virtual void OnDisconnectedFromServer(EventArgs e)
{
if ( DisconnectedFromServer != null )
{
DisconnectedFromServer(this , e);
}
}
...........
void StartReceive(xxxxxxxxx) {
while (this.clientSocket.Connected) {
.........
}
this.Disconnect();
}
bool Disconnect() {
if (this.clientSocket != null && this.clientSocket.Connected) {
try {
this.clientSocket.Shutdown(SocketShutdown.Both);
this.clientSocket.Close();
return true;
} catch {
return false;
}
}
}
Thanks for help.

Responding here as suggested by SO.
Sadly, real world applications tend to fail and often time unexpected things happen. Being accidently closed by a user might be the most obvious "unexpected thing". I would have made sure that, if nothing else, at least everything is cleaned up on exit.
In the end, its all upto you.

Related

Background Worker RunWorkerCompleted isn't being called after DoWork finished

I'm creating a simple program that pings all the servers on our network and returns whether the ping requests were successful.
I'm trying to utilise background workers so that the user can press the ping button and the pings run in the background while they can do other things on the UI
DoWork runs fine, there's no loop to keep it there infinitely, and it reaches the line:
r = pinger.Send(s)
and then from my understanding it ends and so the RunWorkCompleted method should be called?
I'm relearning programming after a long abscense so if I missed something obvious I apologise
...
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
}
private void Ping_Btn_Click(object sender, EventArgs e)
{
count = Convert.ToInt32(pingSeconds_TxtBox.Text);
if (backgroundWorker1.IsBusy != true)
{
// Start operation
backgroundWorker1.RunWorkerAsync();
}
}
private static void OnTimedEvent(Object source, ElapsedEventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
if(worker.CancellationPending == true)
{
e.Cancel = true;
}
else
{
for(int i = 0; i <= count; i++)
{
MessageBox.Show("something is happening");
// Create ping object
Ping pinger = new Ping();
PingReply r;
// IP to test ping
string s = "###";
try
{
r = pinger.Send(s);
}
catch (Exception b)
{
MessageBox.Show(b.ToString());
}
}
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Show me something");
if(e.Cancelled == true)
{
statusLbl1.Text = "Cancelled";
} else if(e.Error != null)
{
statusLbl1.Text = "Error: " + e.Error.Message;
} else
{
statusLbl1.Text = "YEEEEEEEET";
}
}
...
You need to attach your backgroundWorker1_RunWorkerCompleted event handler to the RunWorkerCompleted event. The C# compiler doesn't hook handlers to events based on naming conventions. You have to do it explicitly.
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
}
I strongly suggest you convert this code to use async await which is much better at representing the flow of code control, rather than using the old BackgroundWorker which is basically deprecated.
Note the following:
The main event handler should be async void but all other async functions should be async Task.
Use of SemaphoreSlim.WaitAsync(0) to check if we are busy.
Ping object needs a using or finally to dispose it, as does the CancellationTokenSource.
<= count looks like it should be < count because you begin at 0.
SemaphoreSlim sem = new SemaphoreSlim(1, 1);
CancellationToken token;
private async void Ping_Btn_Click(object sender, EventArgs e)
{
if (!await sem.WaitAsync(0))
return;
var tokenSource = new CancellationTokenSource();
try
{
var count = Convert.ToInt32(pingSeconds_TxtBox.Text);
await RunPingsAsync(count, tokenSource.Token);
statusLbl1.Text = "YEEEEEEEET";
}
catch (OperationCanceledException)
{
statusLbl1.Text = "Cancelled";
}
catch (Exception e)
{
statusLbl1.Text = "Error: " + e.Error.Message;
}
finally
{
sem.Release();
tokenSource.Dispose();
}
MessageBox.Show("Show me something");
}
private Task RunPingsAsync(int count, CancellationToken token)
{
for(int i = 0; i < count; i++)
{
token.ThrowIfCancellationRequested();
MessageBox.Show("something is happening");
// IP to test ping
string s = "###";
// Create ping object
using (Ping pinger = new Ping())
{
var r = await pinger.SendPingAsync(s);
}
}
}
If you want to keep an infinite loop, then you have to make a loop in your backgroundWorker1_DoWork Method. Something like this
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker) sender;
while (!worker.CancellationPending)
{
//Do your stuff here
for(int i = 0; i <= count; i++)
{
MessageBox.Show("something is happening");
// Create ping object
Ping pinger = new Ping();
PingReply r;
// IP to test ping
string s = "###";
try
{
r = pinger.Send(s);
}
catch (Exception b)
{
MessageBox.Show(b.ToString());
}
}
}
}
Also, it is not a good idea to display message boxes from your background thread, Log it in console or any file.

Using UdpClient, how do you retry UdpClient.Connect() until it succeeds?

My client/server programs work well with each other, but only when the server is up and running before my client starts. If the client fails to connect on the first try, I can't get it to try again.
Here's my Client's connect method.
public void connect()
{
IPAddress server_address = IPAddress.Parse("127.0.0.1");
IPEndPoint server_ip = new IPEndPoint(server_address, 5685);
Console.WriteLine("2");
bool connected = false;
while (!connected)
{
try
{
Console.WriteLine("IN CONNECTED");
udp_client.Connect(server_ip);
byte[] send_data = Encoding.ASCII.GetBytes("INIT");
udp_client.Send(send_data, send_data.Length);
byte[] received_bytes = udp_client.Receive(ref server_ip);
string received_data = Encoding.ASCII.GetString(received_bytes);
if (received_data == "INIT")
{
connected = true;
Console.WriteLine("RECEIVED INIT");
listen(server_ip);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
What I was hoping to see is the udp_client.Connect(server_ip) to loop until I received that "INIT" message from the server.
As it currently stands, there is no loop. It seems to get stuck on udp_client.Receive(ref server_ip).
Any help would be appreciated!
This is pseudoCode - you will have to move somethings to class scope to allow future send/receives (which you'd do using a different method). This is only designed to show you how to connect when the connection blocks:
bool isClientConnected = false;
var connector = new System.ComponentModel.BackgroundWorker();
public void connectToUDP(){
connector.DoWork+= connect;
connector.RunWorkerAsync();
}
private void connect(object sender, DoWorkEventArgs e)
{
IPAddress server_address = IPAddress.Parse("127.0.0.1");
IPEndPoint server_ip = new IPEndPoint(server_address, 5685);
Console.WriteLine("2");
try
{
Console.WriteLine("Waiting for server...");
udp_client.Connect(server_ip);
byte[] send_data = Encoding.ASCII.GetBytes("INIT");
udp_client.Send(send_data, send_data.Length);
byte[] received_bytes = udp_client.Receive(ref server_ip);
string received_data = Encoding.ASCII.GetString(received_bytes);
if (received_data == "INIT")
{
isClietConnected = true;
Console.WriteLine("now connected");
listen(server_ip);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
public bool sendReceiveUDP(string send){
if(!isClientConnected){
return false;
}
//perform send
return true;
}
You would then maintain the connected session using class scope and send/receive using a different method. This is for connect only since you only need to do it once.
How you set something like this up:
private bool isConnected = false();
private bool send(){
if(!isConnected){
connect();
}
//send
}
private bool connect(){
if(!isConnected){
//launch connection thread
}
}
private delegate void onNewReceive(string message);
public event onNewReceive onNewReceiveEvent;
public void fireEvent(string message){
onNewReceiveEvent.Invoke(message);
}
private void waitForData(object sender, DoWorkEventArgs e){
//this is the backgroundworker
while(true){
receive();
fireEvent(message);
}
}
Then, subscribe to the onNewREceivedEvent in another class and process the inbound message. onNewReceivedEvent += processInboundMEsasage();
This is all psuedocode and "brain compiled" (creit to others) so it's only meant for demonstrations. Without intellisense, I'm lost.

TCPIP connection in c# and data sending/receiving in another threads

I have a problem with my TCPIP connection Form programm.
I have a code, where I'm trying to send and receive some data from server.
The main problem of my app is how to reconcile some threads:
myListenThread - to listening data from server
myReadStreamThread - to read data from server
System.Threading.Thread - main thread eg. to write data to server
captureThread - to do another things like capturing images from camera
Part of my code:
private void buttonConnect_Click(object sender, EventArgs e)
{
try
{
Connect();
Connected = true;
this.myListenThread = new Thread(new ThreadStart(Listen));
this.myListenThread.Start();
}
catch
{
MessageBox.Show("Invalid host! Try again.");
}
}
private void Listen()
{
this.myReadStreamThread = new Thread(new ThreadStart(ReadStream));
this.myReadStreamThread.Start();
while (Connected)
{
if (!myReadClient.Connected)
{
Connect();
}
}
}
private void Connect()
{
IPAddress IP = IPAddress.Parse(textboxIP.Text);
int PORT = Convert.ToInt32(textboxPORT.Text);
this.myReadClient = new TcpClient();
this.myReadClient.Connect(IP, PORT);//SOMETIMES HERE'S AN ERROR
this.myStream = this.myReadClient.GetStream();
Properties.Settings.Default.IP = Convert.ToString(IP);
Properties.Settings.Default.PORT = Convert.ToString(PORT);
Properties.Settings.Default.Save();
}
private void ReadStream()
{
while (true)
{
try
{
this.myReadBuffer = new byte[this.myReadClient.ReceiveBufferSize];
this.myBufferSize = myStream.Read(myReadBuffer, 0, this.myReadClient.ReceiveBufferSize);
if (myBufferSize != 0)
{
this.myString = Encoding.ASCII.GetString(myReadBuffer);
//myDelegate myDel;
//myDel = new myDelegate(Print);
//richtextboxRead.Invoke(myDel);
}
}
catch
{
break;
}
}
}
All is working correct when I'm connecting to server, but when I want to send some string the problem appears because of threads.
I decided to send string, by clicking Button3 and waiting until I receive string "1" from server using while loop:
private void button3_Click(object sender, EventArgs e)
{
this.captureThread = new Thread(new ThreadStart(() => this.newGame()));
this.captureThread.Start();
}
private bool newGame()
{
string command = "12345abc";
if (Connected)
{
WriteStream(command);
}
while (myBufferSize == 0 && myString !="1") { }
Thread.Sleep(2000);
...//doing other things
}
private void WriteStream(string command)
{
Connect();
this.myWriteBuffer = Encoding.ASCII.GetBytes(command);
this.myStream.Write(this.myWriteBuffer, 0, command.Length);
}
And the problem with connection and data send/receive problem appears, when it should write my string "command" - it doesn't react. MyBufferSize is always 0 and myString is always null. Sometimes an Error about connection appears when I click Button3 (assigned in code). I think it is because in captureThread I can't see any data from another threads. How to solve it?

System.Timers.Timer Usage

For the purposes of this question I'm including a class of mine in its entirety:
public class SerialPortConnection
{
private SerialPort serialPort;
private string ping;
double failOut;
bool isReceiving;
public SerialPortConnection(string comPort = "Com1", int baud = 9600, System.IO.Ports.Parity parity = System.IO.Ports.Parity.None, int dataBits = 8, System.IO.Ports.StopBits stopBits = System.IO.Ports.StopBits.One, string ping = "*IDN?", double failOut = 2)
{
this.ping = ping;
this.failOut = failOut * 1000;
try
{
serialPort = new SerialPort(comPort, baud, parity, dataBits, stopBits);
}
catch (Exception e)
{
throw e;
}
}
//Open Serial Connection. Returns False If Unable To Open.
public bool OpenSerialConnection()
{
//Opens Initial Connection:
try
{
serialPort.Open();
serialPort.Write("REMOTE\r");
}
catch (Exception e)
{
throw e;
}
serialPort.Write(ping + "\r");
var testReceived = "";
isReceiving = true;
Timer StopWatch = new Timer(failOut);
StopWatch.Elapsed += new ElapsedEventHandler(OnTimedEvent);
StopWatch.Interval = failOut;
StopWatch.Enabled = true;
while (isReceiving == true)
{
try
{
testReceived += serialPort.ReadExisting();
}
catch (Exception e)
{
throw e;
}
}
StopWatch.Dispose();
if (testReceived.Contains('>'))
{
return true;
}
else
{
return false;
}
}
public string WriteSerialConnection(string SerialCommand)
{
try
{
serialPort.Write(String.Format(SerialCommand + "\r"));
var received = "";
bool isReceiving = true;
Timer StopWatch = new Timer(failOut);
StopWatch.Elapsed += new ElapsedEventHandler(OnTimedEvent);
StopWatch.Interval = failOut;
StopWatch.Enabled = true;
while (isReceiving == true)
{
try
{
received += serialPort.ReadExisting();
}
catch (Exception e)
{
throw e;
}
}
if (received.Contains('>'))
{
return received;
}
else
{
received = "Error: No Data Received From Device";
return received;
}
StopWatch.Dispose();
}
catch (Exception e)
{
throw e;
}
}
//Closes Serial Connection. Returns False If Unable To Close.
public bool CloseSerialConnection()
{
try
{
serialPort.Write("LOCAL\r");
serialPort.Close();
return true;
}
catch (Exception e)
{
throw e;
}
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
isReceiving = false;
}
}
What I'm attempting to do here is keep a loop running for a set amount of time (two seconds in this case) because the device connected to the serial port I'm working with is unpredictable. I don't know what data I will receive from it and I don't know how long it will take. That can't be fixed and is something I have to work with. My best option, currently, is to wait a set amount of time and check the data I've received for an end token (">"). I've tried wiring up a timer even in the class like so:
Timer StopWatch = new Timer(failOut * 1000);
StopWatch.Elapsed += new ElapsedEventHandler(OnTimedEvent);
StopWatch.Interval = failOut;
StopWatch.Enabled = true;
But it doesn't appear to work. The event itself looks like so:
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
isReceiving = false;
}
My objective is to cut the loop isReceiving is tied to:
(while isReceiving == true)
{
//Do Something
}
But it doesn't appear to work. I assume I've completely misunderstood the function of the timer but I've had suggestions before to implement it. What am I doing wrong? If I'm just completely misusing it, what can I use instead of a timer? As I've said, I've no choice but to wait a set amount of time and check what I've received. That can't be avoided or handled in any way other than waiting and hoping I get something.
EDIT:
Maybe it's best I clarify this. The OnTimedEvent event is firing and the variable is set to false but it doesn't cut the loop as isReceiving isn't getting set to false.
EDIT 2:
Mr. Passant's answer works beautifully barring a strange error I'm encountering. As I don't believe it's a problem within his answer, it's more likely that it's a hardware flaw, or something else strange and obscure along those lines, I'm leaving his answer marked as accepted. I recommend anyone that chooses to implement his answer also view the question I have submitted here:
Apparent IO.Ports.SerialPort Flaw in C# or Possible Hardware Flaw
You are making it too difficult on yourself. Simply change the SerialPort.NewLine property to ">". And use SerialPort.ReadLine() to read the response. You can still use a timeout if you need it, assign the SerialPort.ReadTimeout property and be prepared to catch the TimeoutException.

Cancelling a background worker which is continuously reading from a network stream

I have a TCP server running which spits out messages of 2 bytes at regular intervals.
I'm trying to create a client side form which connects to the server and continuously reads from the stream until I click a disconnect button on the form.
So far the client works fine except that I cannot disconnect. I set the CancellationPending to true but it seems to reset back to false before the dowork method gets a chance to set e.Cancel.
I'm also sure there must be a more acceptable way of continuously reading the stream and writing to the form - at the moment I am calling RunWorkerAsync within the Worker Completed method to achieve the loop!
private void Disconnect()
{
commsWorker1.CancelAsync();
}
private void ReadFromStream()
{
try
{
commsWorker1.RunWorkerAsync();
}
catch (Exception ex)
{
writeToBox("Error: " + ex.Message);
}
}
//background worker dowork method
private void BackGroundGetServerData(object sender, DoWorkEventArgs e)
{
if (true == commsWorker1.CancellationPending)
{
e.Cancel = true;
}
else
{
Byte[] dataArray = new Byte[2];
try
{
_DataStream.Read(dataArray, 0, 2);
String reply = System.Text.Encoding.ASCII.GetString(dataArray);
e.Result = reply;
}
catch (Exception ex)
{
}
}
}
//background worker workercompleted method
private void BackGroundDisplayMessages(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
//close connection here
}
else
{
writeToBox((String)e.Result);
commsWorker1.RunWorkerAsync();
}
}
}
}
Can't you just loop inside the background worker method?
private void BackGroundGetServerData(object sender, DoWorkEventArgs e)
{
while(true)
{
Byte[] dataArray = new Byte[2];
try
{
_DataStream.Read(dataArray, 0, 2);
String reply = System.Text.Encoding.ASCII.GetString(dataArray);
e.Result = reply;
}
catch (Exception ex)
{
return;
}
}
}
Then upon disconnect simply close the socket. This will cause the Exception to be thrown in the while loop and you can exit gracefully through the catch block.
Edit: Then you can update the GUI from the loop after each message is read. Make sure the handle to the control you are updating is available (assuming it's called box):
delegate void updateDelegate(String p);
private void BackGroundGetServerData(object sender, DoWorkEventArgs e)
{
while(true)
{
Byte[] dataArray = new Byte[2];
try
{
_DataStream.Read(dataArray, 0, 2);
String reply = System.Text.Encoding.ASCII.GetString(dataArray);
box.BeginInvoke(new updateDelegate(writeToBox), reply);
}
catch (Exception ex)
{
return;
}
}
}
BeginInvoke is required in this case because you are trying to update the GUI from another thread, which is not allowed. This method forwards the update to the GUI thread.
It seems that you are invoking RunWorkerAsync() in the worker complete method and that resets your CancellationPending prop. I think you can try to fix this by adding to Disconnect() method some disconnectFlag = true; and in WorkerComplete method you should add:
if (e.Cancelled || disconnectFlag)
{
disconnectFlag = false;
//close connection here
} else ...

Categories