Multi client Asynchronous TCP Server
This post is based on a tracer I am currently working on. My current project requires a TCP server implementation where other client could establish communication and perform data processing. The server should be able to handle multiple request from client. I never did any work with TCP Listener, initial one hour struggled a lot to get it working.
First approach I took was kind of Server – Server communication. That means there is no client both system act as server, it’s bit complex but I created a working prototype in next couple of hours. Yes it is complex, client app needs lot of work to establish a connection.
I need the server more simple and less code for the client to establish connection. So I started searching for simple TCP based client server approach. I stumble upon this post, where the author does a multi client communication. I got a start for my second tracer, a true client server model. I modified it and removed looping construct and used Async model instead. You all can see the code below.
TCP Server
private static TcpListener _listener; public static void StartServer() { System.Net.IPAddress localIPAddress = System.Net.IPAddress.Parse("10.91.173.201"); IPEndPoint ipLocal = new IPEndPoint(localIPAddress, 8888); _listener = new TcpListener(ipLocal); _listener.Start(); WaitForClientConnect(); } private static void WaitForClientConnect() { object obj = new object(); _listener.BeginAcceptTcpClient(new System.AsyncCallback(OnClientConnect), obj); } private static void OnClientConnect(IAsyncResult asyn) { try { TcpClient clientSocket = default(TcpClient); clientSocket = _listener.EndAcceptTcpClient(asyn); HandleClientRequest clientReq = new HandleClientRequest(clientSocket); clientReq.StartClient(); } catch (Exception se) { throw; } WaitForClientConnect(); } }
Here I use a async wait approach rather than waiting in a loop. So whenever a client connect OnClientConnected will fire and it handover the request to Client Request handler. After that it will again go back to wait mode for the next request. The advantage of this approach, the application is responsive and less CPU intensive compare to the looping approach.
Let’s see the client request handler
public class HandleClientRequest { TcpClient _clientSocket; NetworkStream _networkStream = null; public HandleClientRequest(TcpClient clientConnected) { this._clientSocket = clientConnected; } public void StartClient() { _networkStream = _clientSocket.GetStream(); WaitForRequest(); } public void WaitForRequest() { byte[] buffer = new byte[_clientSocket.ReceiveBufferSize]; _networkStream.BeginRead(buffer, 0, buffer.Length, ReadCallback, buffer); } private void ReadCallback(IAsyncResult result) { NetworkStream networkStream = _clientSocket.GetStream(); try { int read = networkStream.EndRead(result); if (read == 0) { _networkStream.Close(); _clientSocket.Close(); return; } byte[] buffer = result.AsyncState as byte[]; string data = Encoding.Default.GetString(buffer, 0, read); //do the job with the data here //send the data back to client. Byte[] sendBytes = Encoding.ASCII.GetBytes("Processed " + data); networkStream.Write(sendBytes, 0, sendBytes.Length); networkStream.Flush(); } catch (Exception ex) { throw; } this.WaitForRequest(); }
}
Here also I converted the waiting in loop to Async wait model. The looping model causes a constant hike in CPU utilization. This post explain how to asynchronously read request from client.
That’s all the server side. The client side is shown below
public class TCPClient { System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient(); NetworkStream serverStream; public void ConnectToServer() { clientSocket.Connect("10.91.173.201", 8888); } public void SendData(string dataTosend) { if (string.IsNullOrEmpty(dataTosend)) return; NetworkStream serverStream = clientSocket.GetStream(); byte[] outStream = System.Text.Encoding.ASCII.GetBytes(dataTosend); serverStream.Write(outStream, 0, outStream.Length); serverStream.Flush(); } public void CloseConnection() { clientSocket.Close(); }
public string ReceiveData() { StringBuilder message = new StringBuilder(); NetworkStream serverStream = clientSocket.GetStream(); serverStream.ReadTimeout = 100; //the loop should continue until no dataavailable to read and message string is filled. //if data is not available and message is empty then the loop should continue, until //data is available and message is filled. while (true) { if (serverStream.DataAvailable) { int read = serverStream.ReadByte(); if (read > 0) message.Append((char)read); else break; } else if (message.ToString().Length > 0) break; } return message.ToString(); } }
As you can see most of the function is very simple to understand except ReceiveData(). I haven’t concentrated much on the client side as client of the actual TCP server will not be a .NET one. I am not discarding the fact, I will refactor ReceiveData().
This was very helpful. Thank you!
Anonymous
March 6, 2012 at 9:03 pm
Thanks for leaving your valuable comment.
Sony Arouje
March 7, 2012 at 11:27 am
Why do you create the HandleClientRequest class instead of just parsing the request within the same class?
ie:
Private Sub OnClientConnect(asyn As IAsyncResult)
Try
Dim clientSocket As TcpClient = Nothing
_clientSocket = _listener.EndAcceptTcpClient(asyn)
_networkStream = _clientSocket.GetStream()
WaitForRequest()
Catch se As Exception
End Try
WaitForClientConnect()
End Sub
Private Sub WaitForRequest()
Dim buffer As Byte() = New Byte(_clientSocket.ReceiveBufferSize – 1) {}
Try
_networkStream.BeginRead(buffer, 0, buffer.Length, AddressOf ReadCallback, buffer)
Catch ex As Exception
End Try
End Sub
Private Sub ReadCallback(result As IAsyncResult)
Dim networkStream As NetworkStream = _clientSocket.GetStream()
Try
Dim read As Integer = networkStream.EndRead(result)
If read = 0 Then
_networkStream.Close()
_clientSocket.Close()
Return
End If
Dim buffer As Byte() = TryCast(result.AsyncState, Byte())
Dim data As String = Encoding.[Default].GetString(buffer, 0, read)
‘do the job with the data here
RaiseEvent ioReceived(data)
‘send the data back to client.
‘Dim sendBytes As [Byte]() = Encoding.ASCII.GetBytes(“Processed ” + data)
‘networkStream.Write(sendBytes, 0, sendBytes.Length)
‘networkStream.Flush()
Catch ex As Exception
End Try
Me.WaitForRequest()
End Sub
atappy@gmail.com
March 7, 2012 at 1:43 am
Hi,
As I said it’s a multi client TCP server, if I handle the request in the same class then I may need to queue the request. Think about a scenario that the server receives request from more than one client at the same time, then how the server will able to cater the request as he is busy processing the request he picked thus by blocking one client. Or we need to queue the request. I felt this approach is good for my scenario rather than implementing a queue. Hope I clarify your question.
Regards,
Sony Arouje
Sony Arouje
March 7, 2012 at 11:32 am
What can you do if the Server needs to broadcast a message to a specific connected client?
Michael
August 30, 2012 at 10:23 pm
One option I can think of is, queue HandleRequest instance created in TCPServer->OnClientConnect with some identifier to uniquely identify the client. Whenever we want to send any data back to client, get the specific HandleRequestInstance from the queue and tell him to send the data. You may need to add some code as shown below in HandleRequest class.
public void BroadCast(string message)
{
NetworkStream networkStream = _clientSocket.GetStream();
Byte[] sendBytes = Encoding.ASCII.GetBytes(message);
networkStream.Write(sendBytes, 0, sendBytes.Length);
networkStream.Flush();
}
I haven’t tried this option consider it as pseudo code.
Hope it will help you.
Sony Arouje
August 31, 2012 at 10:34 am
I implemented this example and it stops running after some time. I connected 5 clients to a server and pipe messages from each client at 200 ms. 5 messages per second from each client. After running for about 4 hours all communication stopped. Any ideas?
Michael
September 5, 2012 at 6:35 pm
I am sorry Michael, I don’t know what could be the reason. I implemented a service like this and is running 24/7 without any issue.
Sony Arouje
September 5, 2012 at 11:35 pm
Hmm. If I sent it to you could you take a quick look at it and see if you can see anything?
Michael
September 5, 2012 at 11:41 pm
The code is problematic since it relies on the receive buffer size, as the size of a message. The code then attempts to decode the message, which might not be a valid sequence of bytes suitable for string-decoding.
This is wrong, and will cause exactly this behavior, where the code works on one machine (usually the dev’s machine), and fail on other.
There’re 2 solutions to this:
1. The client and server should instead either agree in advance on a fixed message size. The server should then read exactly the agreed amount of bytes and treat them as a single message.
2. Alternatively, every message should include in its first (say) 4 bytes, its size. The server then reads 4 bytes, constructs an int, and knows how many more bytes to read.
Yaakov Davis
September 21, 2012 at 9:11 pm
Your points are valid Yaakov. In my scenario we have a predefined end of string, so the server keeps on read the stream until it receives the EOS. Thus I can avoid a predefined buffer size. If we don’t have a predefined EOS then we have to go with your approach.
Thanks for your valid comments.
Sony Arouje
September 22, 2012 at 12:09 pm
Another thing I notice is when you close a client, the server fails.
Anonymous
September 6, 2012 at 6:39 pm
I found a problem in the code. When A client disconnects an exception will be thrown here
public void WaitForRequest()
{
byte[] buffer = new byte[_clientSocket.ReceiveBufferSize];
_networkStream.BeginRead(buffer, 0, buffer.Length, ReadCallback, buffer);
}
This function should be
public void WaitForRequest()
{
if (clientSocket.Connected)
{
byte[] Buffer = new Byte[clientSocket.ReceiveBufferSize];
_networkStream.BeginRead(Buffer, 0, Buffer.Length, new AsyncCallback(ReadCallback), Buffer);
}
else
{
// Fire custom event letting the server class know the connection has closed
ConnectionClosed(m_GUID, m_Client);
clientSocket.Close();
_networkStream.Close();
}
}
Michael
September 6, 2012 at 7:35 pm
Thanks Michael, that was really helpful.
Sony Arouje
September 7, 2012 at 5:38 pm
Can you pls help me to implement ConnectionClosed(m_GUID, m_Client) ?
Kalyan
February 5, 2013 at 7:27 pm
Not sure what kind of help you are expecting?
Sony Arouje
February 6, 2013 at 3:20 pm
I Also noticed that when you flood the server with messages it cant respond quick enough. It will get several message before it responds to the server. I am sending a message every 200ms from each client to the server. Here is a screenshot
http://brownspy.com/Downloads/Messages.bmp
Here is the ReadCallback function
private void ReadCallback(IAsyncResult Ar)
{
try
{
NetworkStream Stream = m_Client.GetStream();
int Read = Stream.EndRead(Ar);
if (Read == 0)
{
m_Stream.Close();
m_Client.Close();
return;
}
byte[] Buffer = Ar.AsyncState as Byte[];
string Data = Encoding.Default.GetString(Buffer, 0, Read);
string[] Address = m_Client.Client.RemoteEndPoint.ToString().Split(‘:’);
Console.WriteLine(DateTime.Now.ToString() + ” – Data Received from Client (” + Address[0] + “): ” + Data);
byte[] Ack = Encoding.ASCII.GetBytes(“ACK: ” + Data);
Stream.Write(Ack, 0, Ack.Length);
Stream.Flush();
Console.WriteLine(DateTime.Now.ToString() + ” – Ack sent to client”);
WaitForRequest();
}
catch (Exception Ex)
{
MessageBox.Show(“Message: ” + Ex.Message + Environment.NewLine + Environment.NewLine + Ex.StackTrace.ToString());
}
}
Any and all help will be appreciated,
Thanks,
MIke
Michael
September 6, 2012 at 10:15 pm
if multiple clients send request to server. Then server creates handlers for each request and responding to client request is handlers responsibility. Handler will respond to client when ever it finishes the request and that may not be instantaneous. Server will continue receiving further request, even though handlers are processing several other request. I don’t find any issue with it.
Sony Arouje
September 7, 2012 at 5:35 pm
How can I implement this such that each message sent by the Clients, the client will not send another message until the server Ack’s the message?
Michael
September 7, 2012 at 6:34 pm
I actually got a solution for only sending one message at a time and waiting for an ack that the receiving end got the message ok before sending another message.
Michael
September 8, 2012 at 12:33 am
It was very helpful. But I can not stop TcpListener. It always fails. Could you advise me.? thank you
FG
February 25, 2013 at 2:54 pm
Not sure what error you are getting. Can you pls post the stack trace?
Sony Arouje
February 25, 2013 at 3:17 pm
Hi.
I do not know how to stop TcpListener. I have a stop button, where I have this
_listener.Server.Close();
_listener.Stop();
But it always ends the message
System.ObjectDisposedException: K uvolněnému objektu nelze přistupovat.
v System.Net.Sockets.Socket.EndAccept(IAsyncResult asyncResult)
v System.Net.Sockets.TcpListener.EndAcceptTcpClient(IAsyncResult asyncResult)
v Server.Komunikace.OnClientConnect(IAsyncResult asyn) v c:\Users\Adminis\Documents\SharpDevelop Projects\Server\Server\apk_Komunikace.cs:řádek 166
v System.Net.LazyAsyncResult.Complete(IntPtr userToken)
v System.Net.ContextAwareResult.CompleteCallback(Object state)
v System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
v System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
v System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
v System.Net.ContextAwareResult.Complete(IntPtr userToken)
v System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
v System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
v Server.Komunikace.OnClientConnect(IAsyncResult asyn) v c:\Users\Adminis\Documents\SharpDevelop Projects\Server\Server\apk_Komunikace.cs:řádek 166
v System.Net.LazyAsyncResult.Complete(IntPtr userToken)
v System.Net.ContextAwareResult.CompleteCallback(Object state)
v System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
v System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
v System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
v System.Net.ContextAwareResult.Complete(IntPtr userToken)
v System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
v System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
v System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
Please how do I stop TcpListener, so that no client is not connected and then possibly re-launched.
Thanks
FG
February 25, 2013 at 5:57 pm
Very usefull 🙂 This is a great example for AsyncCallback.
Mihail Nikolov
April 7, 2013 at 1:28 am
I need to update UI control in readCallBack but there is isue thacontrol is not accessible
Simin
May 17, 2014 at 10:23 pm
Hi Simin,
I dont know what could be the issue here, its difficult to identify the issue from your comment.
Regards,
Sony
Sony Arouje
May 18, 2014 at 10:48 am
Hi Thank you for your reply
I want to implement this scenario
the server Liston on a port and two client connect to it and they send their IDs to server
server itself has ID it should receive Client IDs and see if it has the minimum ID update its UI (for example text box.text to “Leader””
I’ve considered an array of length 3 and put server ID to its zero index and when server receive clients’ IDs in “ReadClallBack” method it set index 0 and 1 of the array and see if it is leader among it and two clients and try to update text box.text.
private static void WaitForClientConnect()
{
object obj = new object();
_listener.BeginAcceptTcpClient(new System.AsyncCallback(OnClientConnect), obj);
}
private static void OnClientConnect(IAsyncResult asyn)
{
try
{
TcpClient clientSocket = default(TcpClient);
clientSocket = _listener.EndAcceptTcpClient(asyn);
HandleClientRequest clientReq = new HandleClientRequest(clientSocket);
clientReq.StartClient();
}
catch (Exception se)
{
throw;
}
WaitForClientConnect();
}
public class HandleClientRequest
{
TcpClient _clientSocket;
NetworkStream _networkStream = null;
public HandleClientRequest(TcpClient clientConnected)
{
this._clientSocket = clientConnected;
}
public void StartClient()
{
_networkStream = _clientSocket.GetStream();
WaitForRequest();
}
public void WaitForRequest()
{
byte[] buffer = new byte[_clientSocket.ReceiveBufferSize];
_networkStream.BeginRead(buffer, 0, buffer.Length, ReadCallback, buffer);
isleader = ellect(nodesID, 0);
}
private int getIndex(int[] array)
{
if (array[1] == 0)
return 1;
else if (array[2] == 0)
return 2;
else return 3;
}
private bool ellect(int[] _nodesID, int thisNodeID)
{
if (_nodesID[0] == _nodesID.Min() && _nodesID.Min() != 0)
{
return true;
}
else return false;
}
private void ReadCallback(IAsyncResult result)
{
NetworkStream networkStream = _clientSocket.GetStream();
try
{
int read = networkStream.EndRead(result);
if (read == 0)
{
_networkStream.Close();
_clientSocket.Close();
return;
}
byte[] buffer = result.AsyncState as byte[];
string data = Encoding.Default.GetString(buffer, 0, read);
//do the job with the data here
Byte[] sendBytes = null;
string[] datastructure = data.Split(‘|’);
bool ellection = false;
ellection = Convert.ToBoolean(datastructure[0]);
if (ellection)
{
int index = getIndex(nodesID);
if (index <= 2)
{
nodesID[index] = int.Parse(datastructure[1]);
index = getIndex(nodesID);
if (index == 3)
{
isleader = ellect(nodesID, nodesID[index]);
try
{
textebox1.text = isleader.ToString() ; // Need access to contor In GUI //***************but I cant See any GUI control**************************************************************
}
catch
{
}
}
}
}
else
{
// do somthing else
}
//send the data back to client.
sendBytes = Encoding.ASCII.GetBytes("Processed " + data + "|" + nodesID[0].ToString() + "|" + nodesID[1].ToString() + "|" + nodesID[2].ToString());
networkStream.Write(sendBytes, 0, sendBytes.Length);
networkStream.Flush();
}
catch (Exception ex)
{
throw;
}
this.WaitForRequest();
}
}
in static method I have No Access to GUI control
in readcallback that is in another class called " HandleClientRequest" Ican't GUI control either
Can you please Help me?
I can also send my project if you have time to look at it.
Simin
May 18, 2014 at 4:49 pm
Great example! Thanks!
Anonymous
October 9, 2014 at 8:00 pm
Firstly, many thanks for great example!
But I had a issue when call function _listener.Stop(); This make sense that I want to open socket on other IP address or Port.
It looks like that all resources relating to the thread has not released, or disposed. Consequently, a exception rising.
How do I can stop a running tcplistener?
Thanks in advance!
TienHung
November 22, 2014 at 3:43 pm
How do can I stop a running listener?
Tien Hung
November 23, 2014 at 8:08 am
Thanks for useful example!
However I faced half-open connections problem occasionally,
the exception is ‘Unable to read data from the transport connection’,
and I try to put WaitForRequest() method in the try catch part of the ReadCallback(),
(just next line of networkStream.Flush(); )
then the exception disappeared, but I’m not sure it’s the right way to solve the problem or not……
(Or can you give me some advice to solve the problem correctly)
Since the exception doesn’t often occurs, I try to trigger the exception by the steps:
1. open server side
2. client connect to server
3. shut down network of the server
4. the exception occurs
Just like if there is any accident that server side cannot get return ACK of client side…
Sorry for old replying and poor English, and thanks in advance!
Shuinvy
March 24, 2015 at 9:23 am
Hi,
How do I update UI whenever I received data from client?
Thanks.
Anonymous
July 5, 2015 at 2:11 pm
Hi,
you can update the UI on HandleClientRequest.ReadCallback().
Regards,
Sony
Sony Arouje
July 6, 2015 at 3:57 pm
Hi Sony,
My Requirement is server will be running in the device. and client from the desktop handled in UI.
i want multiple client to connect to the same port to the server and server handles everyone by sending their responses.
could you please help me…
Harish P
August 28, 2015 at 5:14 pm
Hi Harish,
I am not sure how can I help you.
Regards,
Sony Arouje
Sony Arouje
September 3, 2015 at 10:36 pm
Hey.
I copied your code to my own project but the server application seems to stop immediatly after I run it.
Do you know what I’m doing wrong?
daansterk
September 21, 2015 at 7:35 pm
Hi Daansterk,
I dont know what could be the reason. I am using that code in lot of other applications.
Regards,
Sony Arouje
Sony Arouje
September 21, 2015 at 8:52 pm
Are you running the code from a console app?
Joshua
November 19, 2015 at 8:25 am
yes
Sony Arouje
March 16, 2016 at 10:20 pm
Hello again!
Is it possible test the connection of the client closed?
I find the code that useful for others:
http://stackoverflow.com/questions/1387459/how-to-check-if-tcpclient-connection-is-closed
(from stackoverflow)
TcpClient client = new TcpClient(host, port);
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections().Where(x => x.LocalEndPoint.Equals(client.Client.LocalEndPoint) && x.RemoteEndPoint.Equals(client.Client.RemoteEndPoint)).ToArray();
if (tcpConnections != null && tcpConnections.Length > 0)
{
TcpState stateOfConnection = tcpConnections.First().State;
if (stateOfConnection == TcpState.Established)
{
// Connection is OK
}
else
{
// No active tcp Connection to hostName:port
}
}
I have tried but it’s in vain for me, and even use Ping class( for test the network of client is on or off) but this is worse that my computer even crash……
So, could you give me some advice or direction about that or is it truly impossible( just because .Net framework)?
Thanks in advanced!
Shuinvy
December 11, 2015 at 3:43 pm
Finally, I use timer to do the trick!
I use System.Timers.Timer to dynamically create a timer, and there is a list( int id, string IP, int tick in a custom class) for each client connection.
So each time the client connect to the server,
( It’s easy use string clientIP = ((IPEndPoint)clientSocket.Client.RemoteEndPoint).Address.ToString(); in OnClientConnect(IAsyncResult asyn))
check for there is the clientIP in the list or not?
If there is, just set 0 to the clientIP( if(list[i].IP==clientIp) list[i].tick=0)
else add new clientIP to the list.
Then, in the ElapsedEvent of the timer, check all IP in the list( because the IP of the list is which have connecting the server) and the tick of each other.
If the tick is bellow 3( the interval of my timer is 1000, so I want to wait 3 seconds for each client) then just do the increment.
else set the status of the clientIP to false( use an Array or List to record or refresh your UI).
The result is if the clientIP still connect to the server, the tick of it will reset to 0, or the timer will set it to 3 and tag it as disconnect.
Another problem should be improved is remove the IP in the list carefully( or you may get Array out of index exception).
Maybe there is another method better than this way, still appreciated for suggestion.
Shuinvy
December 14, 2015 at 1:52 pm
Can you pls help me to implement ConnectionClosed(m_GUID, m_Client) ?
mudasir (@mudasir3580)
May 12, 2016 at 11:34 am
thanks for the nice post . but i have one more problem as i as more then one clients sending the data . but the problem is each device sends login data first time then followed by location data . how can i diiferenciate which device send data. because login data contains imei number while location data does not.
mudasir (@mudasir3580)
April 30, 2016 at 11:37 am
Simple and efficient server! Thank you so much.
But it can receive only 8192 bytes (_tcpClient.ReceiveBufferSize).
When I tried to send a text file of 300 KB, it receives only the first 8192 bytes then it hangs.
What code should I add to the handler so it can read large data?
Jalil
July 22, 2016 at 6:43 pm
Simple and efficient server! Thank you so much.
But it can only receive 8192 bytes (_tcpClient.ReceiveBufferSize).
When I tried to send to it a file of 300 KB it read the first 8192 then it hangs.
What code should I add to the handler to read large data?
jalil1408
July 22, 2016 at 6:45 pm
you could try increasing
_clientSocket.ReceiveBufferSize
you can increase the value in the constructor of HandleClientRequest
Sony Arouje
October 4, 2016 at 4:17 pm
Hi Mr Sony,
I have to try your sample code, it works on console application. I want to running the server in UI application. but I stuck. Could you help me how to passing the data from the client and displaying to text box ?
Thank you
clarky
October 4, 2016 at 3:52 pm
I never had any issues running this system in WinForm.
The data you received from below line
string data = Encoding.Default.GetString(buffer, 0, read);
in ReadCallback() can be displayed in a text box. Some thing like
Text1.Text=data;
Sony Arouje
October 4, 2016 at 4:12 pm