socket with thread closing - c#

I'm creating a client/server application managed by Sockets and Thread.
I want to know how to close properly the socket and the related threads.
This is my code:
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
try
{
if (UserList.Count > 0)
{
foreach (User user in UserList)
{
Socket socketUser = user.getSocket();
socketUser.Send(Encoding.ASCII.GetBytes("!close"));
socketUser.Close();
}
}
thrAccept.Abort();
thrReceive.Abort();
socket.Close();
incoming.Close();
Application.Exit();
}
catch
{
MessageBox.Show("aasa");
}
}
private void connectBtn_Click(object sender, EventArgs e)
{
string port=portNum.Text;
string welcome = "Server up and running - " + System.DateTime.Now.GetDateTimeFormats()[0] + " " + DateTime.Now.ToString("HH:mm") + "\n" + "Listening Port: " + port + "\n";
socket.Bind(new IPEndPoint(IPAddress.Any, Convert.ToInt32(portNum.Text)));
socket.Listen(3);
thrAccept = new Thread(new ThreadStart(Accept));
serverLog.AppendText(welcome + "\n");
thrAccept.Start();
connectBtn.Enabled = false;
}
private void Accept()
{
while (true)
{
incoming = socket.Accept();
thrReceive = new Thread(new ThreadStart(Receive));
thrReceive.Start();
}
}
private void Receive()
{
User user = new User();//create an istance for the class User
while (true)
{
byte[] buffer = new byte[64];
incoming.Receive(buffer);
// the program continue....
How can I close correctly the socket?
Class User
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
namespace HW2_SERVER
{
class User
{
public string username;
public Socket S;
public string role;
public bool managed;
public User()
{
username = "";
S = null;
role = "";
managed = false;
}
public void setManage(bool i)
{
managed = i;
}
public void setUsername(string u)
{
username = u;
}
public void setSocket(Socket sock)
{
S = sock;
}
public void setRole(string r)
{
role = r;
}
public bool getManaged()
{
return managed;
}
public string getUsername()
{
return username;
}
public Socket getSocket()
{
return S;
}
public string getRole()
{
return role;
}
}
}

You seem to be overwriting your reference to socket and incoming. I assume these are fields on the class. I think you need to keep a collection of these objects, and close all of them when you're shutting down.
Also, aborting threads is a bad idea as you don't know what operation the thread was doing at the time it was being aborted. It's considered a better practice to make a graceful shutdown by setting a flag and checking it in the loop. This may be the cause of your exception. I suggest you don't abort the threads, and instead find a proper way to shut everything down. You're more likely to find a bug-free path this way.
I would replace both of your while (true) loops with while (_isRunning). Set that as a field on the class with a public value. When you're closing down, set it to false and the threads will exit gracefully.

Related

How to correctly use NetMq Poller for Receiving data

I have been given the task to create a interface where I receive data through socket from the sender, for this purpose I am using NetMQ PushSocket for the sender side and then I receive the data at client side sung PullSocket and I have to update the UI (WPF app) when data is received so I receive data using poller in ReceiveReady event of the PullSocket when I do this in a seperate service class and call that class in UI ViewModel the UI thread hangs, so I use Poller.Run in a task, now the problem is that when I stop the poller and then restart it again it doesn't call the ReceiveReady event
Here is the ReceiverService for receiving the data.
public class ReceiverService
{
string msg;
string _address;
int _port;
PullSocket receiver;
NetMQPoller poller;
private MapViewModel ViewModel { get; set; }
public ReceiverService(MapViewModel mapViewModel, int port = 5555)
{
_address = GetComputerLanIP();
_port = port;
receiver = new PullSocket($"tcp://{_address}:{_port}");
receiver.Options.Linger = TimeSpan.Zero;
this.ViewModel = mapViewModel;
poller = new NetMQPoller { receiver };
receiver.ReceiveReady += receiver_ReceiveReady;
}
public void Start()
{
receiver.Connect($"tcp://{_address}:{_port}");
poller.Run();
}
public void Stop()
{
receiver.Disconnect($"tcp://{_address}:{_port}");
poller.Stop();
}
private void receiver_ReceiveReady(object sender, NetMQSocketEventArgs e)
{
// receive won't block as a message is ready
msg = e.Socket.ReceiveFrameString();
// send a response
if (!string.IsNullOrEmpty(msg))
{
try
{
//Updaing the ViewModel here
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
}
private string GetComputerLanIP()
{
string strHostName = Dns.GetHostName();
IPHostEntry ipEntry = Dns.GetHostEntry(strHostName);
foreach (var ipAddress in ipEntry.AddressList)
{
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{
return ipAddress.ToString();
}
}
return "";
}
private string GetValueFromMessage(string identifier)
{
msg.Replace("{", "");
msg.Replace("}", "");
identifier = /*" " + */identifier + " = ";
try
{
int index = msg.IndexOf(identifier) + identifier.Length;
if (index != -1)
{
int index2 = msg.IndexOf(";", index);
if (index2 == -1)
{
index2 = msg.Length;
}
return msg.Substring(index, index2 - index);
}
}
catch (IndexOutOfRangeException ex)
{
return null;
}
return null;
}
}
and in my ViewModel I have set commands for these
private void StartReceiver()
{
Task.Run(() => ReceiverService.Start());
}
private void StopReceiver()
{
Task.Run(() => ReceiverService.Stop());
}
What am I doing wrong? I am new to NetMQ and WPF. TIA
at first it would be good to make a task inside ReceiverService, kind of an ActorModel, because in the end if You would like to reuse it anywhere You need to remember that You should creat a Task first.
always it would be good to have socket in using statement, because You should always close socket if You are not using it
public async Task StartAsync() {
await Task.Run(() => ThreadBody())
}
public void Stop()
{
_poller.Stop();
}
private void ThreadBody()
{
using (PullSocket receiverSocket = new PullSocket())
using (_poller = new NetMQPoller())
{
receiverSocket.Connect($"tcp://{_address}:{_port}");
receiverSocket.ReceiveReady += receiver_ReceiveReady;
_poller.Add(receiverSocket);
_poller.Run();
}
}

C# async await with dataflow BlockBuffer<T>

I have a Winform application that needs to manage several state machines concurrently. Each state machine may have a socket, serial port or other external connection.
Here is my state machine shell implemented using async/await and dataflow BlockBuffer as a message queue.
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
public class StateMachineMessage
{
private static int id = 1;
public int Id { get; } = 0;
public string Name { get; }
public string Text { get; }
public BufferBlock<StateMachineMessage> Queue { get; set; }
public StateMachineMessage(string name, string text)
{
Id = Interlocked.Increment(ref id);
Name = name;
Text = text;
Queue = null;
}
public StateMachineMessage(string name, string text, BufferBlock<StateMachineMessage> queue)
{
Id = Interlocked.Increment(ref id);
Name = name;
Text = text;
Queue = queue;
}
public override string ToString()
{
return "(" + Id + ":" + Name + ":" + Text + ")";
}
}
public class StateMachine
{
private int State { get; }
public string Name { get; }
RichTextBox TextBox { get; }
BufferBlock<StateMachineMessage> Queue { get; }
public StateMachine(string name, BufferBlock<StateMachineMessage> queue, RichTextBox textBox)
{
Name = name;
Queue = queue;
TextBox = textBox;
}
public async Task StartAsync()
{
while (await Queue.OutputAvailableAsync())
{
var request = await Queue.ReceiveAsync();
TextBox.AppendText(string.Format("{0}: {1}: {2}\n",
Thread.CurrentThread.ManagedThreadId,
Name, request));
if (request.Queue != null)
{
var response = new StateMachineMessage("RESPONSE", "Good Morning!");
TextBox.AppendText(string.Format("{0}: {1}: {2}\n",
Thread.CurrentThread.ManagedThreadId,
Name, response));
await request.Queue.SendAsync(response);
}
}
}
}
The main form creates the dataflow block buffers and state machines on initialization.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
namespace DataflowThread
{
public partial class Form1 : Form
{
// Enables the user interface to signal cancellation.
CancellationTokenSource cancellationSource;
// The worker state machine and dataflow node
StateMachine workerMachine;
BufferBlock<StateMachineMessage> workQueue;
// The main thread state machine
StateMachine mainMachine;
BufferBlock<StateMachineMessage> mainQueue;
// Enables progress bar actions to run on the UI thread.
TaskScheduler uiTaskScheduler;
public Form1()
{
InitializeComponent();
InitializeDataflow();
StartStateMachines();
}
public void InitializeDataflow()
{
// Create the cancellation source.
cancellationSource = new CancellationTokenSource();
// Create the UI task scheduler from the current sychronization context.
uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
// Create dataflow options
var options = new ExecutionDataflowBlockOptions();
options.CancellationToken = cancellationSource.Token;
options.TaskScheduler = uiTaskScheduler;
// Create dataflow nodes for the main thread and worker state machines
mainQueue = new BufferBlock<StateMachineMessage>(options);
workQueue = new BufferBlock<StateMachineMessage>(options);
// Create the state machines for the worker and main thread
mainMachine = new StateMachine("MainMach", mainQueue, richTextBox1);
workerMachine = new StateMachine("WorkerMach", workQueue, richTextBox2);
}
public void StartStateMachines()
{
var mainTask = mainMachine.StartAsync();
var workerTask = workerMachine.StartAsync();
}
~Form1()
{
cancellationSource.Dispose();
}
private void sendButton_Click(object sender, EventArgs e)
{
var request = new StateMachineMessage("REQUEST", "Hello World!", mainQueue);
richTextBox1.AppendText(string.Format("{0}: {1}: {2}\n",
Thread.CurrentThread.ManagedThreadId,
mainMachine.Name, request));
workQueue.Post(request);
}
private async void cancelButton_Click(object sender, EventArgs e)
{
try
{
workQueue.Complete();
mainQueue.Complete();
await Task.WhenAll(workQueue.Completion, mainQueue.Completion);
richTextBox1.AppendText("All queues Completed\n");
resetButton.Enabled = true;
}
catch (OperationCanceledException ex)
{
richTextBox1.AppendText(ex.ToString());
}
}
private void sendTenButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i ++)
{
var request = new StateMachineMessage("REQUEST", "Hello World!", mainQueue);
richTextBox1.AppendText(string.Format("{0}: {1}: {2}\n",
Thread.CurrentThread.ManagedThreadId,
mainMachine.Name, request));
workQueue.Post(request);
}
}
private void resetButton_Click(object sender, EventArgs e)
{
resetButton.Enabled = false;
InitializeDataflow();
StartStateMachines();
richTextBox1.Clear();
richTextBox2.Clear();
}
}
}
One started each state machine loops on the BlockBuffer until cancelled. My question:
What is the correct async/await usage for the outermost StartStateMachines() method?
Silently ignoring the Tasks returned from each state machine start feels funny. I am relatively new to C# and am trying to use the async / await patterns where I would normally use a message queue and thread. I am also trying to be a bit modern and not use Invoke/BeginInvoke everywhere. Any help is appreciated.
Thanks in advance.
Sean
EDIT - I added the full state machine source and Program.cs here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DataflowThread
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}

Windows Phone 8.1 TCP sockets

I am developing a Windows Phone 8.1 chat app and discovered multiple difficulties during programming a socket interoperability.
The main issue are 'events' on a socket, and main question is: How to subscribe to OnData event, e.g. how to detect the event, when data came from the server?
I tried to solve it by direct way: infinite reading from InputStream, and that works with 50/50 luck: sometimes that method freeze the UI stream (even the reading method is async).
I googled for that issue, because it's a very strange behavior for that important component, and didn't found any solutions for this even on MSDN. At SO I found only same solutions like mine, but they also freeze UI flow.
For reference, my app infrastructure is the main app that uses facade class from shared library, the facade class get a generic, that specify concrete behavior (this was done for reason of multiple data transports). For TCP transport, I wrote that:
public class TCPTransport : ITransportProvider
{
private bool _connected { get; set; }
private StreamSocket _socket { get; set; }
private DataReader _input { get; set; }
public TCPTransport()
{
_socket = new StreamSocket();
}
public async Task<bool> Connect(string host, int port)
{
try
{
await _socket.ConnectAsync(new HostName(host), port.ToString());
if (OnConnect != null)
OnConnect();
_connected = true;
_input = new DataReader(_socket.InputStream)
{
InputStreamOptions = InputStreamOptions.Partial
};
_read();
return _connected;
}
catch(Exception e)
{
Debug.WriteLine("[warn] cant conect to " + host + ":" + port + ". Additional: " + e.Message);
return false;
}
}
async private void _read()
{
while (true)
{
if (!_connected || _socket == null) break;
uint buffer = await _input.LoadAsync(2048);
string data = _input.ReadString(buffer);
if (OnData != null && data.Length != 0)
OnData(data);
}
}
public async Task<bool> Send(string data)
{
if (!_connected) return false;
try
{
await _socket.OutputStream.WriteAsync(Encoding.UTF8.GetBytes(data).AsBuffer());
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("[warn] Exception send: " + e.Message);
}
return true;
}
public bool Close()
{
if (!_connected) return false;
_socket.Dispose();
return true;
}
public Action OnConnect { get; set; }
public Action<string> OnData { get; set; }
public Action OnClose { get; set; }
}
I suppose that I am wrong about using that solution, but how can I listen to data from server?
For better or worse, the StreamSocket API is not designed as an event-driven API; it is designed as an asynchronous API. If you want to raise an event when data is available, use an event, not a delegate:
Here is a trivial example (hard-coded to www.microsoft.com and with no error handling) showing how to simulate the event:
Note that if you run with the debugger attached, it will temporarily freeze the UI thread. This doesn't occur when not debugging
// Create your own class to hold the data
public class DataReceivedEventArgs : EventArgs
{
public uint DataCount { get; private set; }
public DataReceivedEventArgs(uint dataCount)
{
DataCount = dataCount;
}
}
// Now inside your MainPage...
bool m_keepReading = false;
bool m_eventHandled = false;
public event TypedEventHandler<MainPage, DataReceivedEventArgs> DataReceived;
// Hook this up to (eg) a Button
private async void StartSockets(object sender, RoutedEventArgs e)
{
if (!m_eventHandled)
DataReceived += (s, args) => Debug.WriteLine(args.DataCount);
m_eventHandled = true;
m_keepReading = true;
DoSocketTestAsync();
}
// Hook this up to (eg) a Button
private void StopSockets(object sender, RoutedEventArgs e)
{
m_keepReading = false;
}
private async Task DoSocketTestAsync()
{
var socket = new StreamSocket();
await socket.ConnectAsync(new HostName("www.microsoft.com"), "http");
var writer = new DataWriter(socket.OutputStream);
writer.WriteString("GET /en-us HTTP/1.1\r\nHOST: www.microsoft.com\r\n\r\n");
await writer.StoreAsync();
ReadSocketAsync(socket);
}
private async void ReadSocketAsync(StreamSocket socket)
{
while (m_keepReading)
{
const uint size = 2048;
IBuffer buffer = new Windows.Storage.Streams.Buffer(size);
buffer = await socket.InputStream.ReadAsync(buffer, size,
InputStreamOptions.Partial);
var handler = DataReceived;
if (handler != null && buffer.Length > 0)
handler(this, new DataReceivedEventArgs(buffer.Length));
}
}
Now when you click the "start" button, assuming you have a network connection you will see some numbers start appearing in the Debug Output window.
(Also note that using private auto-implemented properties doesn't really buy you anything; just make them fields).

Hot to call a method from another thread or fire a event from another thread which gets handled on the main thread

I know, the title might be confusing, but I can't really express it better in short.
Basically, I'm writing a TCP Server. I don't have a Windows Form to show, the only thing the user sees is a TrayIcon.
My TCP Server class creates a thread for listening for clients, and then additional thread for every client handling communication. When all communication is done, I want to call a method on the main thread.
I've done it by firing a event from the client communication thread which gets handled on the main thread, and all worked fine until I wanted to add desktop notifications to my application. I've build a notification using WPF (iControlNotification) and wanted to show it in the event handler I mentioned before, but I'm getting a error message saying something like "The calling thread has to be a STA Thread".
Here's some code (I removed unnecessary party):
static class Program {
[...]
[STAThread]
static void Main() {
Log("iControlServerApplication started.");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
trayIcon = new TrayIcon();
trayIcon.Display();
NotificationManager = new iControlNotificationManager();
server = new TCPServer();
server.CommandReceived += new TCPServer.CommandReceivedEventHandler(tcpServer_CommandReceived);
if (server.Start()) {
NotificationManager.ShowNotfication("iControl Server Application", "Server started. " + plugins.Count + " plugins loaded.");
Application.Run();
} else {
MessageBox.Show("Port " + server.Port + " is already in use. Server could not be started.", ApplicationName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
[...]
static void tcpServer_CommandReceived(object source, TCPServer.CommandReceivedEventArgs e) {
string toolTipText = "[" + e.Client.IPAddress + "] >> " + e.Command;
NotificationManager.ShowNotfication("iControl Server Application", toolTipText);
foreach (IiControlPlugin plugin in plugins) {
plugin.Handle(e.SplittedCommands, e.Client);
}
}
[...]
}
-
class TCPServer {
public delegate void CommandReceivedEventHandler(object source, CommandReceivedEventArgs e);
public event CommandReceivedEventHandler CommandReceived;
public class CommandReceivedEventArgs : EventArgs {
private string _command;
private string[] _splittedCommands;
private iControlClient _client;
public CommandReceivedEventArgs(string command, iControlClient client) {
_command = command;
_splittedCommands = command.Split(new Char[]{' '});
_client = client;
}
public string Command { get { return _command; } }
public string[] SplittedCommands { get { return _splittedCommands; } }
public iControlClient Client { get { return _client; } }
}
public TCPServer() {
this.tcpListener = new TcpListener(IPAddress.Any, Port);
this.icClients = new Dictionary<String, iControlClient>();
}
public Boolean Start() {
if (PortIsAvailable(Port)) {
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
Program.Log("ListeningThread started.");
return true;
} else {
return false;
}
}
private void ListenForClients() {
this.tcpListener.Start();
TcpClient client;
while (this.keepListening) {
try {
client = this.tcpListener.AcceptTcpClient();
} catch {
break;
}
iControlClient icClient = new iControlClient(client);
icClient.Thread = new Thread(new ParameterizedThreadStart(HandleClientCommunication));
icClient.Thread.Start(icClient);
}
Program.Log("Stop listening.");
}
private void HandleClientCommunication(object client) {
iControlClient icClient = (iControlClient)client;
NetworkStream clientStream = icClient.TCP.GetStream();
clientStream.ReadTimeout = 10;
int bufflen = 4096;
byte[] message = new byte[bufflen];
int bytesRead;
while (this.keepReceiving && icClient.keepConnected) {
bytesRead = 0;
try {
bytesRead = clientStream.Read(message, 0, bufflen);
} catch {
break;
}
if (bytesRead == 0) {
break;
}
ProcessReceivedData(icClient, ParseData(message, bytesRead));
}
Program.Log("[" + icClient.IPAddress + "] Connection closed.");
icClient.TCP.Close();
this.icClients.Remove(icClient.IPAddress);
}
private void ProcessReceivedData(iControlClient icClient, String[] commands) {
Program.Log("[" + icClient.IPAddress + "] >> " + String.Join(" ", commands));
if (this.CommandReceived != null) {
CommandReceived(this, new CommandReceivedEventArgs(String.Join(" ", commands), icClient));
}
NetworkStream clientStream = icClient.TCP.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes("::ok");
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
icClient.keepConnected = false;
}
}
-
public class iControlNotificationManager {
private iControlNotifications _notifications;
public void ShowNotfication(string caption, string message) {
if ((Boolean)Program.GetSetting("notifications", true) == false) return;
Dispatcher.CurrentDispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(
() => {
iControlNotification noti = new iControlNotification(caption, message);
noti.Show();
}));
}
}
-
public class iControlNotification : Window {
private iControlNotificationModel _notification;
public iControlNotification(string caption, string message) { // Here's the error
InitializeComponent();
_notification = new iControlNotificationModel() {
Caption = caption,
Message = message
};
this.DataContext = _notification;
}
}
So how should I call tcpServer_CommandReceived so that the Notification Window can be shown the right way?
I'm really stuck here, I really appreciate any help on this!
//How to call a method from another thread :
a) You can invoke it in other thread by passing SynchronizationContext object to it:
void Method(object s)
{
SynchronizationContext sync = s as SynchronizationContext;
sync.Post(delegate { // what to do in other thread}, null);
}
Then in code you run this method in new task, passing your sync context as object (for example):
Task t = Task.Factory.StartNew(Method, SynchronizationContext.Current);
b) You can create extension method for this purpose (here is example that i used in win forms application to update UI):
public static class ControlExtensions
{
/// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread.
public static void UIThread(this Control #this, Action code)
{
if (#this.InvokeRequired)
{
#this.BeginInvoke(code);
}
else
{
code.Invoke();
}
}
}

Serial Communication gets back wrong answer when something is running in the background (like browsing the hard drive)

I have some big trouble with serial requests.
Description from what i want:
establish a serial connection, send serial requests to 6 temperature
sensors one by one (this is done every 0,5 second in a loop)
the question and answer-destination is stored in a List array
every request is started in a separate thread so the gui does not bug
while the programme waits for the sensor-hardware to answer
My problem:
The connection and the request is working fine, but if I am browsing data at the local hard drive the answer from the sensor-unit gets destroyed (negative algebraic sign or value from other sensor or simply wrong value).
How does this happen or how can I solve this?
Where I guess the problem might be:
In the private void ReceiveThread() of class SerialCommunication
Here is my code:
Class CommunicationArray:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hardwarecommunication
{
public class CommunicationArray
{
public string request { get; set; }
public object myObject { get; set; }
public string objectType { get; set; }
}
}
Class SerialCommunication
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.IO.Ports;
using System.Windows.Forms;
namespace Hardwarecommunication
{
class SerialCommunication
{
Thread t2;
Thread t;
private SerialPort serialPort = new SerialPort("COM2", 115200, Parity.Even, 8, StopBits.One);
string serialAnswer = "";
private volatile bool _shouldStop;
private int counter;
List<CommunicationArray> ar = new List<CommunicationArray>();
object[] o = new object[3];
public void addListener(string request, object myObject, string objectType)
{
CommunicationArray sa = new CommunicationArray();
sa.request = request;
sa.myObject = myObject;
sa.objectType = objectType;
ar.Add(sa);
}
public void startListen()
{
t2 = new Thread(() => writeSerialPortThread());
t2.Start();
}
public void startSerialPort2()
{
try
{
serialPort.Open();
//MessageBox.Show("Connection opend!");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
public void stopSerialPort2()
{
try
{
if (serialPort.IsOpen == true)
// Connection closed
serialPort.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void writeSerialPortThread()
{
string request = "";
for (int i = 0; i < ar.Count(); i++)
{
request = ar[i].request;
//request = ((object[])ar[0])[0].ToString();
//if (!t.IsAlive)
//{
try
{
t = new Thread(ReceiveThread);
_shouldStop = false;
//MessageBox.Show("start thread");
t.Start();
serialPort.Write(request);
Thread.Sleep(50);
_shouldStop = true;
t.Join();
}
catch
{
}
Label tmpLabelObject = (Label)ar[i].myObject;
serialAnswer = serialAnswer.Replace("=", "");
if (tmpLabelObject.InvokeRequired)
{
MethodInvoker UpdateLabel = delegate
{
tmpLabelObject.Text = serialAnswer;
};
try
{
tmpLabelObject.Invoke(UpdateLabel);
}
catch
{
}
}
}
}
private void ReceiveThread()
{
//MessageBox.Show("in thread");
while (!_shouldStop)
{
serialAnswer = "";
try
{
//MessageBox.Show("in thread");
serialAnswer = serialPort.ReadTo("\r");
if (serialAnswer != "")
{
}
return;
}
catch (TimeoutException) { }
}
}
}
}
Class Form1 //to establish the connection and to start the Sensor request
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Hardwarecommunication
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private SerialCommunication serialCommunication1 = new SerialCommunication();
private void Form1_Load(object sender, EventArgs e)
{
//start up serial connection
serialCommunication1.startSerialPort2();
}
private void buttonStart_Click(object sender, EventArgs e)
{
timerRecord.Enabled = true;
if (this.buttonStart.Text == "Start")
this.buttonStart.Text = "Stop";
else
this.buttonStart.Text = "Start";
}
private void timerRecord_Tick(object sender, EventArgs e)
{
if (this.buttonStart.Text == "Stop")
{
this.serialCommunication1.startListen();
}
}
private void buttonFillRequestArray_Click(object sender, EventArgs e)
{
this.serialCommunication1.addListener("$0BR00\r" + "\r", this.labelResult0, "label0"); //request to the hardware
this.serialCommunication1.addListener("$0BR01\r" + "\r", this.labelResult1, "label1");
this.serialCommunication1.addListener("$01R00\r" + "\r", this.labelResult2, "label2");
this.serialCommunication1.addListener("$01R01\r" + "\r", this.labelResult3, "label3");
this.serialCommunication1.addListener("$01R02\r" + "\r", this.labelResult4, "label4");
}
}
}
I woud be happy about any try to fix the problem.
I coud also upload the solution as .zip but you can't test it at all because you do not have the sensor hardware.
Note: serialPort.Write(string) is a non-blocking store into the output buffer.
That means the following won't guarantee you've even finished writing your request before you stop listening for a response:
serialPort.Write(request);
Thread.Sleep(50);
_shouldStop = true;
You could add:
while( serialPort.BytesToWrite > 0 ) Thread.Sleep(1); // force blocking
but it's ill advised.
One thing I'm wondering. There is only a single serial port here. Why do you want many different threads to work with it when you could manage the entire serial port interaction with a single thread? (Or at worse, 1 thread for input 1 thread for output)
To me it makes a lot more sense to store up requests into a queue of some kind and then peel them off one at a time for processing in a single thread. Responses could be similarly queued up or fired as events back to the caller.
EDIT: If you don't mind one read/write cycle at a time you could try:
string response;
lock(serialPort) {
// serialPort.DiscardInBuffer(); // only if garbage in buffer.
serialPort.Write(request);
response = serialPort.ReadTo("\r"); // this call will block till \r is read.
// be sure \r ends response (only 1)
}

Categories

Resources