I got the code below from a website,and this way of serial port reading is my only option because DataReceived event often doesn't work.but this code has a problem,if I close the application while transfering the application hangs forever,but I can't see why?neither freezing nor aborting the thread work.actually aborting the thread causes the program to crash.
public class CommPort
{
SerialPort _serialPort;
Thread _readThread;
bool _keepReading;
//begin Singleton pattern
static readonly CommPort instance = new CommPort();
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static CommPort()
{
}
CommPort()
{
_serialPort = new SerialPort();
_readThread = null;
_keepReading = false;
}
public static CommPort Instance
{
get
{
return instance;
}
}
//end Singleton pattern
//begin Observer pattern
public delegate void EventHandler(string param);
public EventHandler StatusChanged;
public EventHandler DataReceived;
private void StartReading()
{
if (!_keepReading)
{
_keepReading = true;
_readThread = new Thread(new ThreadStart(ReadPort));
_readThread.Start();
}
}
private void StopReading()
{
if (_keepReading)
{
_keepReading = false;
_serialPort.Close();
//_readThread.Join(); //block until exits
_readThread.Abort();
//_readThread = null;
}
}
private void ReadPort()
{
while (_keepReading)
{
if (_serialPort.IsOpen)
{
byte[] readBuffer = new byte[_serialPort.ReadBufferSize + 1];
try
{
// If there are bytes available on the serial port,
// Read returns up to "count" bytes, but will not block (wait)
// for the remaining bytes. If there are no bytes available
// on the serial port, Read will block until at least one byte
// is available on the port, up until the ReadTimeout milliseconds
// have elapsed, at which time a TimeoutException will be thrown.
int count = _serialPort.Read(readBuffer, 0, _serialPort.ReadBufferSize);
String SerialIn = System.Text.Encoding.ASCII.GetString(readBuffer, 0, count);
DataReceived(SerialIn);
}
catch (TimeoutException)
{
}
}
else
{
TimeSpan waitTime = new TimeSpan(0, 0, 0, 0, 50);
Thread.Sleep(waitTime);
}
}
}
/// <summary> Open the serial port with current settings. </summary>
public void Open()
{
Close();
try
{
_serialPort.PortName = Properties.Settings.Default.COMPort;
_serialPort.BaudRate = Properties.Settings.Default.BPS;
_serialPort.Parity = Properties.Settings.Default.Parity;
_serialPort.DataBits = Properties.Settings.Default.DataBit;
_serialPort.StopBits = Properties.Settings.Default.StopBit;
_serialPort.Handshake = Properties.Settings.Default.HandShake;
// Set the read/write timeouts
_serialPort.ReadTimeout = 50;
_serialPort.WriteTimeout = 50;
_serialPort.Open();
StartReading();
}
catch (IOException)
{
StatusChanged(String.Format("{0} does not exist", Properties.Settings.Default.COMPort));
}
catch (UnauthorizedAccessException)
{
StatusChanged(String.Format("{0} already in use", Properties.Settings.Default.COMPort));
}
catch (Exception ex)
{
StatusChanged(String.Format("{0}", ex.ToString()));
}
// Update the status
if (_serialPort.IsOpen)
{
string p = _serialPort.Parity.ToString().Substring(0, 1); //First char
string h = _serialPort.Handshake.ToString();
if (_serialPort.Handshake == Handshake.None)
h = "no handshake"; // more descriptive than "None"
StatusChanged(String.Format("{0}: {1} bps, {2}{3}{4}, {5}",
_serialPort.PortName, _serialPort.BaudRate,
_serialPort.DataBits, p, (int)_serialPort.StopBits, h));
}
else
{
StatusChanged(String.Format("{0} already in use", Properties.Settings.Default.COMPort));
}
}
/// <summary> Close the serial port. </summary>
public void Close()
{
StopReading();
StatusChanged("connection closed");
}
public bool IsOpen
{
get
{
return _serialPort.IsOpen;
}
}
}
When you close the Port in StopReading() it will cause an Exception in _serialPort.Read(...).
Not sure which one exactly but it's not a TimeOut. Your current code lets that escape and that's when your thread and your App are killed.
So add a catch(Exceptiopn ex) around the while loop in ReadPort().
Two issues at first glance.
First off, you have created a singleton class that exposes an event subscription. That's dangerous, because you should keep track of any of the subscribers. Otherwise they may be never released. That's probably what's going on in your case.
Secondly, when you manage something "IDisposable", you should take care about it. So, the SerialPort should be disposed inside a IDisposable implementation within your class. However, that won't make any sense in a singleton class.
The singleton pattern should be used only for centralized passive resources, and never to host "active" objects, events, etc.
Hope this helps.
Related
I have an issue with my application,
I have a TCPListener which listen let's say on port 14000
After the application is being closed I can see on the CMD that the listener is still listening.
At the second run of the application as expected I cant start the listener on the same port (14000) because it is already taken, I am changing the application port to 15000 on the second running, work wonderful and the listener is being CLOSED after the application is being shut down,
I assume that on the first run, the first listener on port 14000 stays open after the app is dead, on the second run the application closed/open the listener on port 15000 very well, why is this happen? I thought maybe it is about the port 14000 I've switched the orders of the opening ports (first opened 15000) and saw that the 15000 stays open and the 14000 (on the second run) closed and open correctly, Why at the first run the listener not being closed??
The code to my server:
class Server : IDisposable
{
private const int TIMER_PERIOD = 60 * 1000; // ms
private string servePort;
private string serverIP;
byte[] DataReceived = new byte[1024];
Action<string> MssageReceiveCallback;
private bool isListening = false;
static Timer serverTimer = null;
private TcpListener _Server;
private Dictionary<int, TcpClient> clientsList = new Dictionary<int, TcpClient>();
private bool serverListening = true;
private static int ClientInstance = 0;
public Server(string _serverIP, string _serverPORT, Action<string> messageReceiveCallback)
{
serverIP = _serverIP;
servePort = _serverPORT;
MssageReceiveCallback = messageReceiveCallback;
// InitilizeServer();
}
private void InitilizeServer()
{
_Server = new TcpListener(IPAddress.Parse(serverIP), int.Parse(servePort));
// if (serverTimer == null)
// serverTimer = new Timer(new TimerCallback(OnTimerCallback), null, TIMER_PERIOD, TIMER_PERIOD);
Task.Run(() =>
{
try
{
_Server.Start();
while (_Server != null)
{
TcpClient tcpClient;
try
{
tcpClient = _Server.AcceptTcpClient();
}
catch
{
continue;
}
Task.Run(() =>
{
ClientInstance++;
int currentinstance = ClientInstance;
clientsList.Add(currentinstance, tcpClient);
try
{
while (tcpClient.Connected && serverListening)
{
if (tcpClient.GetStream().DataAvailable)
{
int actualBufferlength = tcpClient.GetStream().Read(DataReceived, 0, DataReceived.Length);
byte[] data = new byte[actualBufferlength];
Buffer.BlockCopy(DataReceived, 0, data, 0, actualBufferlength);
string asciiMessage = Encoding.ASCII.GetString(data);
MssageReceiveCallback(asciiMessage);
}
else
{
Thread.Sleep(5);
}
}
}
catch (Exception ex)
{
}
finally
{
clientsList[currentinstance].Close();
clientsList.Remove(currentinstance);
}
});
}
}
catch (Exception ex)
{
}
});
}
public void StartServer()
{
InitilizeServer();
isListening = true;
}
public void SendMessage(string msg)
{
byte[] data = ASCIIEncoding.ASCII.GetBytes(msg);
foreach (TcpClient client in clientsList.Values)
{
client.GetStream().Write(data, 0, data.Length);
}
}
public void Dispose()
{
serverListening = false;
foreach (var item in clientsList.Values)
{
if (item.Connected)
item.Close();
}
_Server.Server.Close();
}
}
UPDATE:
I've check in TCPView to see which application the listener bind to and found this:
It looks like the listener available for un exist process
The biggest problem here, I think (I've pointed out other problems in the comments) is that TCP shutdown requires network communications and by default prevents socket reuse for a period of time.
The function you need to get to is Socket.SetSocketOption, specifically the ReuseAddress option. You should be able to get at it via the Server property on the TcpListener. Pay attention that it needs to be done before you actually start the listener listening.
You could try putting:
_Server.Server =null;
After close.
Please see my code below, that I am trying to return back to method that I registered the port's DataReceived event. Basically, If I recieve data from port before read time out. I will return back where I registered DataReceived event and degister and continue process. I am trying to do it with while loop. But not sure if it is accurate, and it is the way that has to be done
or if there is any other way to do this.
public class CommClass{
private static byte[] portReturn = null;
private void setUpDevice()
{
byte[] command = { 0x11,0X51 };
try
{
port.DataReceived += new SerialDataReceivedEventHandler(serialPortDataReceived);
port.Write(command, 0, command.Length);
while (portReturn == null) { } //Not sure if this will work. If I receive data before times out I do not want to wait in the loop.
port.DataReceived -= serialPortDataReceived;
}
catch(Exception ex)
{
//to do
}
}
private void serialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
var servicePort = (SerialPort)sender;
portReturn = servicePort.ReadByte();
return;
}
}
You code will technically work; however, your while loop will max out your CPU while you're waiting for data to come in, which is not what you want. I recommend using a ManualResetEvent here to let you wait to receive data in a CPU friendly way. You can read more about them here
public class CommClass
{
private static byte[] portReturn = null;
// ManualResetEvents are great for signaling events across threads
private static ManualResetEvent dataReceivedEvent = new ManualResetEvent(false);
private void setUpDevice()
{
byte[] command = { 0x11,0X51 };
try
{
port.DataReceived += new SerialDataReceivedEventHandler(serialPortDataReceived);
port.Write(command, 0, command.Length);
// Wait for the event to be set without spinning in a loop.
// Can also specify a timeout period to wait in case the data never comes.
dataReceivedEvent.WaitOne();
// Reset the event so that you can use it again later if necessary
dataReceivedEvent.Reset();
port.DataReceived -= serialPortDataReceived;
}
catch(Exception ex)
{
//to do
}
}
private void serialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
var servicePort = (SerialPort)sender;
portReturn = servicePort.ReadByte();
// Set the event to let the main thread know you have received data
dataReceivedEvent.Set();
}
}
I'm using com0com to create a part of virtual ports comA/comB, typing the input to comA from hyperterminal and listening on comB in a wpf application. When I run the following code (by triggering Connect), the application successfully connects and is able to get the data from comA, but hangs when I do Disconnect.
public void Connect()
{
readPort = new SerialPort("COMB");
readPort.WriteTimeout = 500;
readPort.Handshake = Handshake.None;
readPort.Open();
readThread = new Thread(Read);
readRunning = true;
readThread.Start();
System.Diagnostics.Debug.Print("connected");
}
public void Disconnect()
{
if (!readRunning)
{
readPort.Close();
}
else
{
readRunning = false;
readThread.Join();
readPort.Close();
}
System.Diagnostics.Debug.Print("disconnected");
}
public void Read()
{
while (readRunning)
{
try
{
int readData = 0;
readData = readPort.ReadByte();
System.Diagnostics.Debug.Print("message: " + readData.ToString());
}
catch (TimeoutException)
{
}
}
}
I tried changing the Read function to a write by using
byte[] writeData = { 1, 2, 3 };
readPort.Write(writeData, 0, 3);
instead of port.readbyte, and it starts working perfectly when disconnecting. Does anyone know if there is anything different about readbyte that could have caused the freeze? Or is it possibly related to com0com?
Just checking back, in case anyone runs into the same issue, I found an alternative way overriding SerialPort.DataReceived like this:
public override void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
byte[] buf = new byte[sp.BytesToRead];
sp.Read(buf, 0, buf.Length);
receivedDataDel(buf);
}
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.
I am having the following issue:
Once I close my WM6 application and then try to start it again i get this error:
Only one usage of each socket address (protocol/network address/port) is normally permitted at System.Net.Sockets.Socket.Bind(EndPoint localEP)
at
System.Net.Sockets.Socket.TcpListener.Start()
...
I think this is due to the time interval for the connection to timeout, so I would like to close all open conections and force it to create a new connection, is this the correct way to proceed or is there a different way to handle this?
Here is the code used to start listening:
/// <summary>
/// Listens Asynchronously to Clients, creates a recieveMessageHandler to process the read.
///
/// Check WIKI, TODOS
/// </summary>
/// <returns></returns>
public void Listen()
{
myTcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
try
{
TcpClient myTcpClient = myTcpListener.AcceptTcpClient();
DateTime now = DateTime.Now;
//Test if it's necessary to create a client
ClientConnection client = new ClientConnection(myTcpClient, new byte[myTcpClient.ReceiveBufferSize]);
// Capture the specific client and pass it to the receive handler
client.NetworkStream.BeginRead(client.Data, 0, myTcpClient.ReceiveBufferSize, r => receiveMessageHandler(r, client), null);
}
catch (Exception excp)
{
Debug.WriteLine(excp.ToString());
}
}
}
Yes, your server socket is likely in the TIME_WAIT state.
You can access the underlying ServerSocket and then use SetSocketOption and specify ReuseAddress.
I'm going to guess here that ClientConnection is your DLL, because I don't see that already included in the CF.
You don't really need that, though, if you declare MethodInvoker.
public delegate void MethodInvoker(); // required
To make your code really slick, you should also create your very own EventArgs class:
public class WmTcpEventArgs : EventArgs {
private string data;
public WmTcpEventArgs(string text) {
data = text;
}
public string Data { get { return data; } }
}
Very simple. With this new WmTcpEventArgs class and, you should be all set to receive your data that could post to something like a TextBox control:
private void NetworkResponder(object sender, WmTcpEventArgs e) {
textBox1.Text = e.Data;
}
Instead of coding a while(true) in your code, I prefer to include a little Boolean variable
private bool abortListener;
The code would look something like this:
public void Listen() {
listener.Start();
while (!abortListener) {
try {
using (var client = listener.AcceptTcpClient()) {
int MAX = client.ReceiveBufferSize;
var now = DateTime.Now;
using (var stream = client.GetStream()) {
Byte[] buffer = new Byte[MAX];
int len = stream.Read(buffer, 0, MAX);
if (0 < len) {
string data = Encoding.UTF8.GetString(buffer, 0, len);
MethodInvoker method = delegate { NetworkResponder(this, new WmTcpEventArgs(data)); };
abortListener = ((form1 == null) || form1.IsDisposed);
if (!abortListener) {
form1.Invoke(method);
}
}
}
}
} catch (Exception err) {
Debug.WriteLine(err.Message);
} finally {
listener.Stop();
}
}
}
Notice you are still catching your Exceptions, but you also stop the TcpListener.