i need to read data from my virtual com port and detect the message "Dreq". Once i press the connect button, it connects to my COM8 port and begins reading in a new thread. I also have a disconnect button in which i wish to close the reading and disconnect from the COM8 port. However, i have problems closing the BeginRead.
public partial class Form1 : Form
{
SerialPort sp;
Stream stream;
IAsyncResult recv_result;
private void button1_Click(object sender, EventArgs e)
{
sp = new SerialPort("COM8", 9600);
sp.Open();
sp.ReadTimeout = 50000;
sp.NewLine = "\n\r\0";
stream = sp.BaseStream;
recv_result = stream.BeginRead(new byte[1], 0, 0, new
AsyncCallback(ReadCallBack), stream);
}
private void ReadCallBack(IAsyncResult ar)
{
Stream stream = (Stream)ar.AsyncState;
string temp;
while (stream.CanRead)
{
temp = sp.ReadLine();
// ... do something with temp
}
}
private void disconnectButton_Click(object sender, EventArgs e)
{
stream.EndRead(recv_result);
sp.Close();
}
}
You can try calling sp.DiscardOutBuffer(). It will call your read callback and you can then use stream.EndRead().
private void disconnectButton_Click(object sender, EventArgs e)
{
sp.DiscardOutBuffer();
stream.EndRead(recv_result);
sp.Close();
}
You can try this program.
using System;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;
using System.IO;
using System.Text;
public class clsState {
private const int BUFFER_SIZE = 1024;
public byte[] BytesBuffer = new byte[BUFFER_SIZE];
public SerialPort sp;
public Stream stream;
}
public partial class Form1 : Form {
SerialPort sp;
Stream stream;
IAsyncResult recv_result;
bool endLoop = false;
Thread _readThread;
private ManualResetEvent _readDone = new ManualResetEvent(false);
private void button1_Click(object sender, EventArgs e) {
sp = new SerialPort("COM8", 9600);
sp.Open();
sp.ReadTimeout = 50000;
sp.NewLine = "\n\r\0";
stream = sp.BaseStream;
// save serial port and stream to state object
clsState state = new clsState();
state.sp = sp;
state.stream = stream;
// create worker thread
endLoop = false;
_readThread = new Thread(() => ReadThreadProc(state))
_readThread.IsBackground = true;
_readThread.Start();
}
private void ReadThreadProc(clsState state) {
while (endLoop == false){
// open and then close the gate as soon as after one thread passed
_readDone.Reset();
// starting ascynchronous read
recv_result = state.stream.BeginRead(state.BytesBuffer, 0, state.BytesBuffer.Length, new AsyncCallback(ReadCallBack), state.stream);
// worker thread block in here (waiting for... _readDone.Set())
_readDone.WaitOne();
}
}
private void ReadCallBack(IAsyncResult ar) {
string temp;
int bytesRead = 0;
// read serial port and stream from IAsyncResult
clsState state = (clsState) ar.AsyncState;
// if port serial is open and..
if (state.sp.IsOpen) {
// the stream can read then..
if(state.stream.CanRead) {
// wait for asynchronous read to completed
bytesRead = state.stream.EndRead(ar);
}
}
if(bytesRead > 0) {
// convert data in state.BytesBuffer from bytes array to string and save to temp variable
temp = Encoding.ASCII.GetString(state.BytesBuffer);
// open gate for next data
_readDone.Set();
}
}
private void disconnectButton_Click(object sender, EventArgs e) {
// ending loop and will kill worker thread...
endLoop = true;
// release begin read
_readDone.Set();
if (_readThread != null){
if (_readThread.IsAlive){ // if worker thread still live
_readThread.Join(); // wait thread off in here..
}
}
// close serial port
if (sp.IsOpen) sp.Close();
// close stream and dispose it
if (stream.CanRead || stream.CanWrite) {
stream.Close();
stream.Dispose();
}
}
}
It's an old post, but I think this solution can help people with the same problem.
I was using legacy code for an application and I found that the problem with BeginRead and EndRead is that there is no way to cancel the asynchronous operation. Therefore, when you close the port, your call to BeginRead stays there forever until another byte is received in the port, then your call to EndRead will free up the port. If it does not happen this way, then your application may hang and not even task manager can close it until you unplug the serial port cable!
Fortunately the TPL library can fix this problem in a very simple and elegant way. The CancelToken is what you need:
On port open:
while (x)
var myTask = _serialPort.BaseStream.ReadAsync(_serialBuffer, 0, _bufferLength, cancelToken.Token);
var bytesRead = await myTask;
... your business logic here...
if ((myTask.IsCanceled) || (myTask.IsFaulted)) break;
}
On port close:
cancelToken.Cancel(false);
Please note a while loop is better than recursive call because when port is broadcasting lots of information, a stack overflow exception is thrown after 15 minutes.
It's a very simple implementation. I hope it helps
Related
I am working on a C# application. My application has to do serial communication with a hardware device. The device is connected with my system on "COM4" com port. Code:
serialPort = new SerialPort("COM4", 2400, Parity.Odd, 8, StopBits.One);
serialPort.WriteTimeout = 5000;
serialPort.ReadTimeout = 5000;
serialPort.Open();
serialPort.Handshake = Handshake.None;
serialPort.DtrEnable = true;
serialPort.RtsEnable = true;
After that i do a write operation, this works fine. Code:
private void WriteMessage(byte[] busMsg)
{
BinaryWriter writer = new BinaryWriter(serialPort.BaseStream);
writer.Write(busMsg);
writer.Flush();
}
After write, when i do ReadByte operation, i get the timeout exception. Code:
private byte ReadAEBusMessageResponse()
{
BinaryReader reader = new BinaryReader(serialPort.BaseStream);
return reader.ReadByte();
}
I read somewhere on google that BaseStream might cause issue, so i tried the below code for reading but, still no luck and i am still getting the timeout exception.
private byte ReadAEBusMessageResponse()
{
SerialPort currentPort = serialPort;
return Convert.ToByte(currentPort.ReadByte());
}
I do get the response when i try communication with Hercules, so i assume there is no issue with response from device. What am i doing wrong with serial communication ? any help would be much appreciated.
I have only found the async event to work. I have created my own byte receive buffer and then I handle the event:
static byte[] RXbuffer = new byte[512]; //"static" so it can be used all over
public void SerialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
Byte_Size = SerialPort.BytesToRead;
SerialPort.Read(RXbuffer, 0, Byte_Size);
/* Return to UI thread */
this.Invoke(new EventHandler(DisplayText));
}
the DisplayText is a method to print the buffer in a textbox.
private void DisplayText(object sender, EventArgs e)
{
int i = 0;
byte[] Received_Bytes = new byte[Byte_Size];
while (i < Byte_Size)
{
Received_Bytes[i] = RXbuffer[i];
i++;
}
TB_Info.AppendText("\r\nBytes Received: ");
TB_Info.AppendText(Byte_Size.ToString());
TB_Info.AppendText("\r\n--> ");
TB_Info.AppendText(BitConverter.ToString(Received_Bytes));
TB_Info.AppendText("\r\n");
//Array.Copy(RXbuffer,Full_RX_Buff,)
}
You can then read/manipulate the values later.
***Don't forget that in the SerialPort object that put on your form, you have to go into the properties/events dialog and assign the function that handles the receivedata event!
I have a Java android code that sends data (image or text) to a C# application, to receive these data I'm using Async socket. But exists a problem that is relative to BeginReceive() function is not receiving the complete data when is sent an image.. Then how I can make a kind of "loop" to receive full data and after show the image on Picturebox (for example)?
Form
private Listener listener;
private Thread startListen;
private Bitmap _buffer;
public frmMain()
{
InitializeComponent();
}
private void serverReceivedImage(Client client, byte[] image)
{
try
{
byte[] newImage = new byte[image.Length - 6];
Array.Copy(image, 6, newImage, 0, newImage.Length);
using (var stream = new MemoryStream(newImage))
{
using (var msInner = new MemoryStream())
{
stream.Seek(2, SeekOrigin.Begin);
using (DeflateStream z = new DeflateStream(stream, CompressionMode.Decompress))
{
z.CopyTo(msInner);
}
msInner.Seek(0, SeekOrigin.Begin);
var bitmap = new Bitmap(msInner);
Invoke(new frmMain.ImageCompleteDelegate(ImageComplete), new object[] { bitmap });
}
}
}
catch (Exception)
{
System.Diagnostics.Process.GetCurrentProcess().Kill();
}
}
private delegate void ImageCompleteDelegate(Bitmap bitmap);
private void ImageComplete(Bitmap bitmap)
{
if (_buffer != null)
_buffer.Dispose();
_buffer = new Bitmap(bitmap);
pictureBox1.Size = _buffer.Size;
pictureBox1.Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (_buffer == null) return;
e.Graphics.DrawImage(_buffer, 0, 0);
}
private void startToolStripMenuItem_Click(object sender, EventArgs e)
{
startListen = new Thread(listen);
startListen.Start();
}
private void listen()
{
listener = new Listener();
listener.BeginListen(101);
listener.receivedImage += new Listener.ReceivedImageEventHandler(serverReceivedImage);
startToolStripMenuItem.Enabled = false;
}
Listener
class Listener
{
private Socket s;
public List<Client> clients;
public delegate void ReceivedImageEventHandler(Client client, byte[] image);
public event ReceivedImageEventHandler receivedImage;
private bool listening = false;
public Listener()
{
clients = new List<Client>();
s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
public bool Running
{
get { return listening; }
}
public void BeginListen(int port)
{
s.Bind(new IPEndPoint(IPAddress.Any, port));
s.Listen(100);
s.BeginAccept(new AsyncCallback(AcceptCallback), s);
listening = true;
}
public void StopListen()
{
if (listening == true)
{
s.Close();
listening = false;
}
}
void AcceptCallback(IAsyncResult ar)
{
Socket handler = (Socket)ar.AsyncState;
Socket sock = handler.EndAccept(ar);
Client client = new Client(sock);
clients.Add(client);
sock.BeginReceive(client.buffer, 0, client.buffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), client);
client.Send("REQUEST_PRINT" + Environment.NewLine);
handler.BeginAccept(new AsyncCallback(AcceptCallback), handler);
}
void ReadCallback(IAsyncResult ar)
{
Client client = (Client)ar.AsyncState;
try
{
int rec = client.sock.EndReceive(ar);
if (rec != 0)
{
string data = Encoding.UTF8.GetString(client.buffer, 0, rec);
if (data.Contains("SCREEN"))
{
byte[] bytes = Encoding.UTF8.GetBytes(data);
receivedImage(client, bytes);
}
else // not is a image, is a text
{
// prepare text to show in TextBox
}
}
else
{
Disconnected(client);
return;
}
client.sock.BeginReceive(client.buffer, 0, client.buffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), client);
}
catch
{
Disconnected(client);
client.sock.Close();
clients.Remove(client);
}
}
}
Client
class Client
{
public Socket sock;
public byte[] buffer = new byte[8192];
public Client(Socket sock)
{
this.sock = sock;
}
public void Send(string data)
{
byte[] buffer = Encoding.ASCII.GetBytes(data);
sock.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback((ar) =>
{
sock.EndSend(ar);
}), buffer);
}
}
Android code
private byte[] compress(byte[] data) {
Deflater deflater = new Deflater();
deflater.setInput(data);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
deflater.finish();
byte[] buffer = new byte[1024];
while (!deflater.finished()) {
int count = deflater.deflate(buffer);
outputStream.write(buffer, 0, count);
}
outputStream.close();
byte[] output = outputStream.toByteArray();
return output;
}
public static DataOutputStream dos;
public static byte[] array;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
array = compress(bos.toByteArray());
//...
dos = new DataOutputStream(SocketBackgroundService.clientSocket.getOutputStream());
byte[] header = ("SCREEN").getBytes(StandardCharsets.UTF_8);
byte[] dataToSend = new byte[header.length + array.length];
System.arraycopy(header, 0, dataToSend, 0, header.length);
System.arraycopy(array, 0, dataToSend, header.length, array.length);
dos.writeInt(dataToSend.length);
dos.write(dataToSend, 0, dataToSend.length);
dos.flush();
EDITION
i'm always getting the error Invalid Parameter in this line
var bitmap = new Bitmap(msInner);
and using compression also happens the same here
z.CopyTo(msInner);
IvalidDataException
on ServerReceivedImage() method respectively.
using this
File.WriteAllBytes(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "image.png"), newImage);
i noted that is receiving only 15KB (size of file without use compression).
I was writing a comment but it does not give me enough space to express my frustration with your code.
My main points are
You try to recompress and perfectly compressed image. PNG is portable network graphics. It was designed for network transfers. If it is acceptable you should use something like jpeg.
You just decode received buffer using UTF8.GetString and search for a text, then re-encode that string and try to decompress and read an image from it, by starting from index 6 which is pretty meaningless considering you added a two byte size field to the start of stream and you really do not know position of "SCREEN".
You do not check if you have received ALL of the stream data.
All of the code looks like you have scoured the SO questions and answers and created a copy pasta.
Now my recommendations.
When transferring data from network, do not try to invent wheels. Try something like gRPC which has both android java and c# packages.
If you will use raw data, please, please know your bytes.
I assume you will extend your code by adding new command pairs. Since you have no magic markers of some kind of signal system, it will be very hard for you to distinguish data from header. For a simple implementation add some kind of magic data to your header and search for that data, then read header and then read data. You may need to read from socket again and again until you receive all of the data.
424A72 0600 53435245454E 008E0005 ..... 724A42
B J r 6 S C R E E N 36352 ..... rJB
this sample data shows that we have a valid stream by looking at "BJr". Then read a 2 byte unsigned integer to read command size which is 6 for SCREEN. Read command and then read four bytes unsigned length for command data. For our sample it is 36352. Just to be safe I've added an end of command marker "rJB".
For a bonus point try reducing memory allocations / copies, you can look at System.Span<T>
I'm trying to realize a program that sends data to a TCP-server and then receives the same message by COM-port. I have the following code but it sends the data but doesn't receive any data.
When I monitor the serial port I can't see that the data is received.
How do I fix this? Do I have to switch to a multi threading system or is there a other way to sync this? Because I can see that the COM-port is opened but not read out.
And when I test this with Putty I can get it to work.
public class Variables
{
private static int v_VarI = 0;
public static int VarI
{
get { return v_VarI; }
set { v_VarI = value; }
}
private static int v_VarJ = 0;
public static int VarJ
{
get { return v_VarJ; }
set { v_VarJ = value; }
}
}
class Program
{
public class TcpTimeClient
{
private const int portNum = 6666;
private const string hostName = "192.168.1.51";
private const string data = "test";
public static int Main(String[] args)
{
new SerialPortProgram();
while (Variables.VarI < 10)
{
Byte[] bytesSent = Encoding.ASCII.GetBytes(data);
try
{
TcpClient client = new TcpClient(hostName, portNum);
NetworkStream ns = client.GetStream();
byte[] bytes = new byte[1024];
ns.Write(bytesSent, 0, bytesSent.Length);
int bytesRead = ns.Read(bytes, 0, bytes.Length);
client.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Variables.VarI++;
while (Variables.VarJ < Variables.VarI)
{
}
}
Console.ReadLine();
return 0;
}
}
public class SerialPortProgram
{
public SerialPort receiveport = new SerialPort("COM3", 115200, Parity.None, 8, StopBits.One);
public SerialPortProgram()
{
receiveport.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
receiveport.Open();
}
public void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// Show all the incoming data in the port's buffer
string rec_data = receiveport.ReadExisting();
Console.WriteLine(rec_data);
Variables.VarJ++;
}
}
}
Update
I've found that the program I wrote and putty and an other program named Serial Port Monitor differ in the IOCTL_SERIAL_SET_WAIT_MASK.
Serial Port Monitor: 0x00 00 01 19
Putty: Unkown
Own program: 0x00 00 01 FB
Does anyone know how to change this mask? Because it's not in the System.IO.Ports.SerialPort class.
you can use this :
public void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int length = receiveport.BytesToRead;
byte[] buf = new byte[length];
SerialPort2.Read(buf, 0, length);
}
buf array is your answer but in decimal mode . and you can changed it to want.
The wait mask is not your problem. Your wait mask enables the serial port for RXCHAR RXFLAG CTS DSR RLSD BRK ERR RING and has everything except ERR turned on.
This is correct for the way .Net wraps serial ports, and anyway it is not exposed in the .NET serial port class. Don't waste time pursuing that difference; your problem lies elsewhere.
Your serial port code ran perfectly on my system and printed all the data received from an external serial source, so I suspect your problem is with the TCP server whose code you did not post, or with your serial ports. Perhaps your configuration requires that you transmit something before the port begins sending? Try something like:
receiveport.Open();
receiveport.WriteLine("?");
Then if you still have problems, try connecting to another serial device to see if you can prove your receive code is working for you as it is for me. If you still can't figure it out, post more information about what's happening on the serial source end.
This code is reading images from socket using threads in parallel using background worker. i want to just reuse the same sockets for my new coming image again but the following error occurs .
*ERROR : The process can not access the file "D:\image 1.png because it is being used by another process *
i.e my idea is to kill the previous process and start a new process in the same socket .
enter code here
namespace ScreenCapture
{
public partial class Form1 : Form
{
String ipaddress;
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
}
private void button1_Click(object sender, EventArgs e)
{
ipaddress=textBox1.Text;
try
{
TcpClient client = new TcpClient(ipaddress, 5000); // create the client socket and connect to port number 5000
Stream s = client.GetStream(); // create the object of stream and assign client socket stream
StreamWriter sw = new StreamWriter(s);
StreamReader sr = new StreamReader(s);
sw.AutoFlush = true; // auto flush the stream writer
sw.WriteLine("Send");
client.Close();
s.Close();
Thread.Sleep(15000);
backgroundWorker1.RunWorkerAsync();
}
catch (Exception ee)
{
MessageBox.Show(ee.ToString());
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i < 10; i++)
{
try
{
TcpClient client = new TcpClient(ipaddress, 5001); // create the client socket and connect to port number 5000
Stream s = client.GetStream(); // create the object of stream and assign client socket stream
StreamWriter sw = new StreamWriter(s);
sw.AutoFlush = true;
sw.WriteLine(i.ToString());
client.Close();
s.Close();
Thread proces = new Thread(delegate()
{
int portno = 5001 + i;
int j = i;
// MessageBox.Show(portno.ToString());
TcpClient clientt = new TcpClient(ipaddress,portno); // create client socket and connect to prot number 5000
using (var stream = clientt.GetStream()) // create the stream variable
using (var output = File.Create("D:\\img"+j+".png")) // create variable output
{
var buffer = new byte[1024]; // crfeate variable buffer of length 1024
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) // get the number of bytes from stream and greater then
{
output.Write(buffer, 0, bytesRead);
}
}
backgroundWorker1.ReportProgress(j);
clientt.Close();
return;
});
proces.Start();
Thread.Sleep(5200);
}
catch (Exception ee)
{
MessageBox.Show(ee.ToString());
}
if (i == 9)
i = 0;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.SuspendLayout();
pictureBox1.Image = new Bitmap(#"D:\\img" +e.ProgressPercentage + ".png");
if (File.Exists(#"D:\\img" + (e.ProgressPercentage - 1).ToString() + ".png"))
{
File.Delete(#"D:\\img" + (e.ProgressPercentage - 1).ToString() + ".png");
}
this.ResumeLayout();
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
}
I wrote a class that tries to read byte data sent by a server, and when my app ends, I also want the loop to end, but it appears NetworkStream.Read() just waits if no data is ever available.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Threading.Tasks;
namespace Stream
{
class Program
{
private static TcpClient client = new TcpClient("127.0.0.1", 80);
private static NetworkStream stream = client.GetStream();
static void Main(string[] args)
{
var p = new Program();
p.Run();
}
void Run()
{
ThreadPool.QueueUserWorkItem(x =>
{
while (true)
{
stream.Read(new byte[64], 0, 64);
}
});
client.Close();
// insert Console.ReadLine(); if you want to see an exception occur.
}
}
}
With this code, we'd get
System.IO.IOException saying "Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host",
ObjectDisposedException saying "Cannot access a disposed object", or
System.IO.IOException saying "A blocking operation was interrupted by a call to WSACancelBlockingCall".
So how could I safely end this method?
From what I can see when running your program, your 1st exception "Unable to read data from the transport connection..." is not thrown by the Stream.Read but by the TcpClient's constructor and is most probably due to the fact that there is no server accepting connections on 127.0.0.1:80.
Now if you want to end nicely on the Stream.Read, I would do it asynchronously. In this way you can catch any exceptions thrown during the Read and clean you program accordingly. Here is a modified version of your program:
using System;
using System.Net.Sockets;
namespace Stream
{
class Program
{
private static TcpClient client;
private static NetworkStream stream;
static void Main(string[] args)
{
var p = new Program();
p.Run();
}
void Run()
{
try
{
client = new TcpClient("127.0.0.1", 80);
stream = client.GetStream();
byte[] buffer = new byte[64];
stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(OnRead), buffer);
client.Close();
Console.ReadKey();
}
catch (Exception)
{
//...
}
}
void OnRead(IAsyncResult result)
{
try
{
stream.EndRead(result);
byte[] buffer = result.AsyncState as byte[];
if (buffer != null)
{
//...
}
// continue to read the next 64 bytes
buffer = new byte[64];
stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(OnRead), buffer);
}
catch (Exception)
{
// From here you can get exceptions thrown during the asynchronous read
}
}
}
}
To avoid blocking, only call Read() when DataAvailable is true.
The ThreadPool isn't suited for long-running tasks, so refactor your while loop
You close the connection before you read from it (as the work item is executed async).
This might help
void Run() {
ThreadPool.QueueUserWorkItem(ReadLoop);
}
void ReadLoop(object x) {
if (stream.DataAvailable)
stream.Read(new byte[64], 0, 64);
else
Thread.Sleep(TimeSpan.FromMilliseconds(200));
if (Finished)
client.Close();
else if (!Disposed && !Finished)
ThreadPool.QueueUserWorkItem(ReadLoop);
}
You'll need to manage the Finished & Disposed yourself in your real class.
If the simple console client application is implemented, then why use another thread to connect and receive the data from server?
static void Main(string[] args)
{
var client = new TcpClient("...", ...);
var buffer = new byte[1024];
using (var networkStream = client.GetStream())
{
int bytesRead;
while ((bytesRead = networkStream.Read(buffer, 0, buffer.Length)) > 0)
{
var hexString = BitConverter.ToString(buffer, 0, bytesRead);
Console.WriteLine("Bytes received: {0}", hexString);
}
}
}