I've programmed a TCP server application where I can listen to incoming connections on a dedicated port. With this I'm able to get an "connected" event and then receive data (only once).
How can I receive data continuously from the port (and maybe also detect if the client is still connected)?
I've connected a NodeMCU (Arduino based) system which sends some temperature data every second using the TCP connection.
Starting and stopping the server through a toggle switch in the UI:
public async Task<bool> StartListeningAsync()
{
if (TCPSocket == null)
{
TCPSocket = new StreamSocketListener();
TCPSocket.ConnectionReceived += LocalSocketConnectionReceived;
await TCPSocket.BindServiceNameAsync(CommunicationPort);
return true;
}
return false;
}
public async Task<bool> StopListening()
{
if (connectedSocket != null)
{
connectedSocket.Dispose();
connectedSocket = null;
}
if (TCPSocket != null)
{
await TCPSocket.CancelIOAsync();
TCPSocket.ConnectionReceived -= LocalSocketConnectionReceived;
TCPSocket.Dispose();
TCPSocket = null;
return true;
}
return false;
}
Event that handles a new connection and receive data:
private async void LocalSocketConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
if (connectedSocket != null)
{
connectedSocket.Dispose();
connectedSocket = null;
}
connectedSocket = args.Socket;
await textBox_send.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
textBox_send.IsEnabled = true;
txtConnected.Text = "Client Connected";
});
using (var reader = new DataReader(args.Socket.InputStream))
{
await readTCPDataAsync(reader);
}
}
private async Task readTCPDataAsync(DataReader reader)
{
reader.InputStreamOptions = InputStreamOptions.None;
// Read the length of the payload that will be received.
byte[] payloadSize = new byte[(uint)BitConverter.GetBytes(0).Length];
await reader.LoadAsync((uint)payloadSize.Length);
reader.ReadBytes(payloadSize);
// Read the payload.
int size = BitConverter.ToInt32(payloadSize, 0);
//size = 2;
byte[] payload = new byte[size];
await reader.LoadAsync((uint)size);
reader.ReadBytes(payload);
string data = Encoding.ASCII.GetString(payload);
}
This code works perfectly to receive the data once the connection is established.
I'm thinking of a solution to get an event once new data is on the input buffer and then process the data.
I'm thinking of a solution to get an event once new data is on the input buffer and then process the data.
There is no such event in UWP API that can be triggered at each time a new date is received. What we usually do here is using a while loop to receive data continuously. For example, you can add a while loop in your LocalSocketConnectionReceived method like the following:
using (var reader = new DataReader(args.Socket.InputStream))
{
while (true)
{
await readTCPDataAsync(reader);
}
}
The while loop works here because DataReader.LoadAsync(UInt32) is a asynchronous method. It will wait there if there is no date received.
For more info, please refer to the StreamSocket sample on GitHub, especially the OnConnection method in Scenario 1.
/// <summary>
/// Invoked once a connection is accepted by StreamSocketListener.
/// </summary>
/// <param name="sender">The listener that accepted the connection.</param>
/// <param name="args">Parameters associated with the accepted connection.</param>
private async void OnConnection(
StreamSocketListener sender,
StreamSocketListenerConnectionReceivedEventArgs args)
{
DataReader reader = new DataReader(args.Socket.InputStream);
try
{
while (true)
{
// Read first 4 bytes (length of the subsequent string).
uint sizeFieldCount = await reader.LoadAsync(sizeof(uint));
if (sizeFieldCount != sizeof(uint))
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Read the string.
uint stringLength = reader.ReadUInt32();
uint actualStringLength = await reader.LoadAsync(stringLength);
if (stringLength != actualStringLength)
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Display the string on the screen. The event is invoked on a non-UI thread, so we need to marshal
// the text back to the UI thread.
NotifyUserFromAsyncThread(
String.Format("Received data: \"{0}\"", reader.ReadString(actualStringLength)),
NotifyType.StatusMessage);
}
}
catch (Exception exception)
{
// If this is an unknown status it means that the error is fatal and retry will likely fail.
if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
{
throw;
}
NotifyUserFromAsyncThread(
"Read stream failed with error: " + exception.Message,
NotifyType.ErrorMessage);
}
}
Related
I want to display a string when I receive data from a server. For that I was thinking on using delegates and events. I'm new to this topic (Delegates and Events) so I'm have not been able to set this up.
Here is what I've done:
public delegate void ClientHandleData(byte[] data, int bytesRead);
public event ClientHandleData OnDataReceived;
public void ConnectToServer(string ipAddress, int port)
{
this.port = port;
tcpClient = new TcpClient(ipAddress, port);
clientStream = tcpClient.GetStream();
Thread t = new Thread(new ThreadStart(ListenForData));
started = true;
t.Start();
}
private void ListenForData()
{
int bytesRead;
while (started)
{
bytesRead = 0;
try
{
bytesRead = clientStream.Read(buffer.ReadBuffer, 0, readBufferSize);
}
catch
{
//A socket error has occurred
MessageBox.Show("A socket error has occurred);
break;
}
if (OnDataReceived != null)
{
// display string to a textbox on the UI
}
Thread.Sleep(15);
}
started = false;
Disconnect();
}
You can just write
OnDataReceived?.Invoke(buffer.ReadBuffer, bytesRead);
If you want to be sure that your event will not be set to null after the if statement you can do this:
var handler = OnDataReceived;
handler?.Invoke(buffer.ReadBuffer, bytesRead);
Be careful when updating UI, because you can only update UI from UI thread. If you are using WPF you can do this:
Dispatcher.Invoke(() => {
// Update your UI.
});
And also make sure that someone actually subscribed to the event:
public void Foo()
{
objectWithTheEvent.OnDataReceived += OnOnDataReceived;
}
private void OnOnDataReceived(byte[] data, int count)
{
}
Let us have a look at your TcpClient listening code. When you call stream.Read() you can not be sure how much data will be read from your socket so you have to read until the end of the stream or you have to know how much date you are supposed to read from socket. Let us assume that you know how much data you are supposed to read
var readSofar = 0;
var iNeedToRead = 500;//500Bytes
try{
while(readSoFar<iNeedToRead){
var readFromSocket = clientStream.Read(buffer.ReadBuffer, readSofar, readBufferSize-readSofar);
if(readFromSocket==0){
//Remote server ended your connection or timed out etc
//Do your error handling may be stop reading
}
readSofar += readFromSocket;
}
}
catch {
//A socket error has occurred
MessageBox.Show("A socket error has occurred);
break;
}
if (OnDataReceived != null){
// display string to a textbox on the UI
}
You can use null propogation operator like this.
OnDataReceived?.Invoke(buffer.ReadBuffer, bytesRead);
If you are using WindowsForm each controller has to be updated from UI thread therefore you have to call from the subscriber method
private void IReceivedData(byte[] data, int count){
this.Invoke(()=>{...Your code});
}
I am using Arduino UNO connected with bluetooth module. I have this below code in Arduino, which listen to specific input and glow the LED.
int LED= 13;
char input;
void setup()
{
Serial.begin(9600);
pinMode(LED, OUTPUT);
Serial.println(">> START<<");
Serial.flush();
}
void loop()
{
Serial.flush();
if(Serial.available()>0)
{
input= Serial.read();
if(input=='1')
{
Serial.write(1);
Serial.println('a');
digitalWrite(LED, HIGH);
}
else if(input=='0')
{
Serial.println("OFF");
digitalWrite(LED, LOW);
}
else
{
Serial.println("NO INPUT");
Serial.println(input);
}
}
}
From Windows 8.1 (XAML/C#) application I am sending data through bluetooth. And it works perfectly as expected. But I am also trying to read data from Arduino. For that I have below code in C#.
socket = new StreamSocket();
connectAction = socket.ConnectAsync(rfcommService.ConnectionHostName, rfcommService.ConnectionServiceName, SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
await connectAction;//to make it cancellable
writer = new DataWriter(socket.OutputStream);
reader = new DataReader(socket.InputStream);
Task.Run(() =>
{
ListenForMessagesAsync();
});
The ListenForMessagesAsync method supposed to keep listening the dataReader. But it just waiting for infinite time and never returns.
private async Task ListenForMessagesAsync()
{
while (reader != null)
{
try
{
uint sizeFieldCount = await reader.LoadAsync(1);// taskLoadLength.GetResults();
if (sizeFieldCount != 1)
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Read the message.
uint messageLength = reader.ReadByte();
uint actualMessageLength = await reader.LoadAsync(messageLength);
if (messageLength != actualMessageLength)
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Read the message and process it.
string message = reader.ReadString(actualMessageLength);
}
catch (Exception ex)
{
}
}
}
What am I doing wrong here?
You need a return after a successful ReadString and of course you need to do something with the message.
Im using Async Duplex Named Pipes to send messages between Three processes.
A sender, a buffer and a receiver. The Buffer has two instances of the example code relating to one pipe coming in from the receiver and one going out to the receiver.
With one instance it runs perfectly for as long as i want.
With two instances in the buffer it works fine and quick like i expect, until after running for around 10 mins at 2000 packets per second, one of the server pipes stops working, there is no exceptions, no breaks, no information whatsoever except that the server pipe just seems to close without warning.
Has anyone ever encountered issues like this?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Pipes;
using System.Threading;
using System.Security.AccessControl;
using System.Security.Principal;
namespace Test_Processor
{
public class MessageHandler
{
//Transmit Pipe
NamedPipeClientStream TXPipe;
//Receive Pipe
NamedPipeServerStream RXPipe;
//Store Pipe Names
string _TXName;
string _RXName;
//Read Buffer to store Read Data
byte[] ReadBuffer = new byte[1040];
//Write Buffer to store write data
byte[] WriteBuffer = new byte[1040];
//Store for the latest message received
public byte[] ReceivedMessage;
//Latest error message received
public string ErrorMessage = "";
//Time of the last messages
public DateTime LastMessageReceived = new DateTime(1970, 01, 01);
public DateTime LastMessageSent = new DateTime(1970, 01, 01);
//Boolean connected variable
public Boolean IsConnected = false;
//Custom Events
public event EventHandler OnReceivedData;
public event EventHandler OnError;
public event EventHandler OnDisconnect;
//Create instance
public MessageHandler(string TXName, string RXName)
{
try
{
//Create Received Pipe
RXPipe = new NamedPipeServerStream(RXName, PipeDirection.In, 10, PipeTransmissionMode.Byte, PipeOptions.Asynchronous | PipeOptions.WriteThrough);
//Create Transmit Pipe
TXPipe = new NamedPipeClientStream(".", TXName, PipeDirection.Out, PipeOptions.Asynchronous | PipeOptions.WriteThrough);
//Store the names
_TXName = TXName;
_RXName = RXName;
//Put the listener in a thread to keep everything else running
Thread WorkerThread = new Thread(new ThreadStart(StartListener));
WorkerThread.IsBackground = true;
WorkerThread.Start();
}
catch (Exception e)
{
ErrorMessage = e.ToString();
ErrorHandler(EventArgs.Empty);
}
}
//Start Listener waits for the next connection
public void StartListener()
{
try
{
//Begin the async call
RXPipe.BeginWaitForConnection(new AsyncCallback(WaitForConnectionCallBack), RXPipe);
}
catch (Exception e)
{
ErrorMessage = e.ToString();
ErrorHandler(EventArgs.Empty);
}
}
//Callback for the async callback
private void WaitForConnectionCallBack(IAsyncResult iar)
{
try
{
// Get the pipe
NamedPipeServerStream pipeServer = (NamedPipeServerStream)iar.AsyncState;
// End waiting for the connection
pipeServer.EndWaitForConnection(iar);
//wait for data to be read
BeginRead(pipeServer);
}
catch
{
return;
}
}
//Begin read event to begin receiving data
private void BeginRead(NamedPipeServerStream Pipe)
{
// Asynchronously read a request from the client
bool isConnected = Pipe.IsConnected;
if (isConnected)
{
try
{
//Pipe.BeginRead(pd.data, 0, pd.data.Length, OnAsyncMessage, pd);
Pipe.BeginRead(ReadBuffer, 0, 1040, OnAsyncMessage, Pipe);
}
catch (Exception)
{
isConnected = false;
}
}
if (!isConnected)
{
//not connected so kill the pipe
Pipe.Close();
DisconnectHandler(EventArgs.Empty);
}
}
//Async handler for the end of the message
private void OnAsyncMessage(IAsyncResult result)
{
// Async read from client completed
NamedPipeServerStream pd = (NamedPipeServerStream)result.AsyncState;
//Read all the data
Int32 bytesRead = pd.EndRead(result);
if (bytesRead != 0)
{
LastMessageReceived = DateTime.Now;
ReceivedMessage = ReadBuffer;
//Call the on received event
ReceivedData(EventArgs.Empty);
}
BeginRead(pd);
}
//Send the Async Data to Pipe
public bool SendAsyncData(byte[] SendData)
{
try
{
//If its not connected connect
if (!TXPipe.IsConnected)
{
TXPipe.Connect(500);
}
//put data in the buffer
WriteBuffer = SendData;
//Begin writing to the pipe
TXPipe.BeginWrite(WriteBuffer, 0, 1040, OnAsyncWriteComplete, TXPipe);
return true;
}
catch (Exception ex)
{
Console.Write(ex.ToString());
return false;
}
}
//On Write Complete
private void OnAsyncWriteComplete(IAsyncResult result)
{
//Finished
PipeStream pipe = (PipeStream)result.AsyncState;
pipe.EndWrite(result);
WriteBuffer = new byte[1040];
}
//Custom Event Handlers
protected virtual void ReceivedData(EventArgs e)
{
EventHandler handler = OnReceivedData;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void ErrorHandler(EventArgs e)
{
EventHandler handler = OnError;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void DisconnectHandler(EventArgs e)
{
EventHandler handler = OnDisconnect;
if (handler != null)
{
handler(this, e);
}
}
}
}
It's a lot of irrelevant code to look through.. but pretty much it sends a packet and listens for a packet in return
if i comment the part out where it calls the ReceiveAuthPacket() method at the end of sending a packet, it will work and the label will turn blue.. but otherwise it will never activate turning the label blue and will instead turn the label red or green (depending on the returned packet).
basically im just using the label as an indicator of the status.. and no matter what i try i can't get it to turn blue because it seems to be waiting for all the code to be finished executing and it just won't work..
i even tried using data triggers in WPF and it still won't work.
any work arounds? i just don't get it..
private readonly UdpMessageAuthentication _msgAuth;
private void Button_Authenticate_OnClick(object sender, RoutedEventArgs e)
{
Label_Authentication.Content = "Attempting Authentication";
Label_Authentication.Foreground = new SolidColorBrush(Colors.Blue);
_msgAuth.SendAuthPacket(IPAddress.Parse(TextBox_IP.Text), TextBox_ClientID.Text);
}
public void SendAuthPacket(IPAddress ip, string userID)
{
_ip = ip;
_userID = userID;
if (_udpClient.Client == null)
_udpClient = new UdpClient();
//GSISClockRegRequest,<Client Id>,,1
string msg = string.Format("GSISClockRegRequest,{0},,1", _userID);
byte[] sendBytes = Encoding.ASCII.GetBytes(msg);
bool sent = false;
try
{
_label.Content = "Attempting Authentication";
_label.Foreground = new SolidColorBrush(Colors.Blue);
while (_label.Content != "Attempting Authentication")
{
//loop
}
_udpClient.Connect(_ip, 5001);
_udpClient.Send(sendBytes, sendBytes.Length);
Console.WriteLine("Sending {0} bytes. Message: {1}", sendBytes.Length, msg);
sent = true;
}
catch (Exception)
{
Console.WriteLine("UDP Auth Packet Failed to Send");
}
_udpClient.Close();
if (sent)
ReceiveAuthPacket(); //IF I COMMENT THIS OUT IT'LL WORK
}
private void ReceiveAuthPacket()
{
IPEndPoint e = new IPEndPoint(IPAddress.Any, 5001);
UdpClient u = new UdpClient(e);
u.Client.ReceiveTimeout = 3000;
Console.WriteLine("Listening for Messages: ");
try
{
Byte[] receiveBytes = u.Receive(ref e);
string receiveString = Encoding.ASCII.GetString(receiveBytes);
Console.WriteLine("Received: {0}", receiveString);
string errMsg = "";
if (AuthMessageParser.ParseMessage(receiveString, ref errMsg))
{
_label.Content = "Authentication Successful!";
_label.Foreground = new SolidColorBrush(Colors.Green);
}
else
{
_label.Content = "Authentication Unsuccessful: " + errMsg;
_label.Foreground = new SolidColorBrush(Colors.Red);
}
}
catch (Exception)
{
_label.Content = "Authentication Unsuccessful";
_label.Foreground = new SolidColorBrush(Colors.Red);
Console.WriteLine("UDP Auth Packet was NOT Received.");
}
u.Close();
}
Your UI thread is blocked by calls to things like _udpClient.Connect() and _udpClient.Send() (and the receives, too)
A workaround would be to leverage the task parallel library and perform communications asynchronously to avoid blocking the UI thread.
It will manage threads for you as long as you define tasks properly. Holler if you need an example.
protected void SomeButton_Click(Object sender, EventArgs e)
{
// Task off the work and do not wait, no blocking here.
Task.Run(PerformConnection);
}
private async Task PerformConnection()
{
// This method acts the way a thread should. We await the result of async comms.
// This will not block the UI but also may or may not run on its own thread.
// You don't need to care about the threading much.
var conn = await ListenerOrSomething.AwaitConnectionsAsync( /* ... */ );
// Now you have your result because it awaited.
using(var newClient = conn.Client())
{
var buffer = new byte[];
var recv = await newClient.ReceiveAsyncOrSomething(out buffer);
// Data received is not zero, process it or return
if(recv > 0)
newClient.Response = await ProcessRequest(buffer);
else
return;
}
}
When a client disconnects, the server closes. Tell me how to leave the ability to connect new customers after the close of the first session .Thanks in advance.
namespace tcpserver
{
class Program
{
static void Main(string[] args)
{
string cmd;
int port = 56568;
Server Serv = new Server(); // Создаем новый экземпляр класса
// сервера
Serv.Create(port);
while (true)
{
cmd = Console.ReadLine(); // Ждем фразы EXIT когда
// понадобится выйти из приложения.
// типа интерактивность.
if (cmd == "EXIT")
{
Serv.Close(); // раз выход – значит выход. Серв-нах.
return;
}
}
//while (Serv.Close() == true) { Serv.Create(port); }
}
public class Server // класс сервера.
{
private int LocalPort;
private Thread ServThread; // экземпляр потока
TcpListener Listener; // листенер))))
public void Create(int port)
{
LocalPort = port;
ServThread = new Thread(new ThreadStart(ServStart));
ServThread.Start(); // запустили поток. Стартовая функция –
// ServStart, как видно выше
}
public void Close() // Закрыть серв?
{
Listener.Stop();
ServThread.Abort();
return;
}
private void ServStart()
{
Socket ClientSock; // сокет для обмена данными.
string data;
byte[] cldata = new byte[1024]; // буфер данных
Listener = new TcpListener(LocalPort);
Listener.Start(); // начали слушать
Console.WriteLine("Waiting connections on " + Convert.ToString(LocalPort) + " port");
try
{
ClientSock = Listener.AcceptSocket(); // пробуем принять клиента
}
catch
{
ServThread.Abort(); // нет – жаль(
return;
}
int i = 0;
if (ClientSock.Connected)
{
while (true)
{
try
{
i = ClientSock.Receive(cldata); // попытка чтения
// данных
}
catch
{
}
try
{
if (i > 0)
{
data = Encoding.ASCII.GetString(cldata).Trim();
Console.WriteLine("<" + data);
if (data == "CLOSE") // если CLOSE –
// вырубимся
{
ClientSock.Send(Encoding.ASCII.GetBytes("Closing the server..."));
ClientSock.Close();
Listener.Stop();
Console.WriteLine("Server closed. Reason: client wish! Type EXIT to quit the application.");
ServThread.Abort();
return;
}
else
{ // нет – шлем данные взад.
ClientSock.Send(Encoding.ASCII.GetBytes("Your data: " + data));
}
}
}
catch
{
ClientSock.Close(); //
Listener.Stop();
Console.WriteLine("Client disconnected. Server closed.");
ServThread.Abort();
}
}
}
}
}
}
}
Typical threaded server code will read more like this (in a pseudo code, because I don't know enough Java details to write it exactly, and because C is a bit stifling):
socket s = new socket
bind s to an optional IP, port
listen s
while true
cli = accept s
t = new thread(handle_client, cli)
maybe disown thread, so no need to join it later
t.start
The important point is that creating the socket, binding it to an address, and listen are all handled outside the loop, and accept() and starting threads are inside the loop.
You may want to wrap this entire block inside another thread; that is acceptable. The important part is separating the listen from the accept and per-client thread. This allows your code to stop accepting new connections but handle existing clients until they disconnect, or disconnect existing connections when they use their allotment of resources but continue accepting connections, etc. (Note how your last catch block will terminate the server if any single client socket throws an exception; that kind of code is easy to avoid with the usual server layout.)
replace
ServThread.Abort();
return;
with a continue instead, this will not break the while loop and yet stop the current "round". Please consider reading this: http://www.codeproject.com/KB/IP/serversocket.aspx nice project to build from