I'm kinda new to C# and I'm trying to create a Modbus-TCP slave.
All i want to do is to call an event handler when i recieve data from the TCP Master.
namespace Mark_II.Device
{
class Slave_TCP : mSlave
{
short trans_ID;
byte[] Respond;
byte[] MasterMessage;
TcpClient Client;
NetworkStream stream;
public Slave_TCP(String IP, int Port)
{
Client = new TcpClient(IP, Port);
stream = Client.GetStream();
// insert "call event handler" here<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
private void client_OnReceiveData(object sender, SerialDataReceivedEventArgs e)
{
byte[] message = new byte[Client.ReceiveBufferSize];
stream.Read(message, 0, message.Length);
}
}
}
I have been looking around but i couldn't find anything... please help me.
tl;dr: I'm looking for a way to raise an event, when my Client receives data from a master.
In general, events in C# works like this:
public delegate void MessageHandler(string message);
public class Client
{
public event MessageHandler MessageArrived;
public void CheckForMessage() //logic to check if message is received
{
//other code to check for message
if(MessageArrived != null)
MessageArrived("message received");
}
}
public class DisplayMessage
{
public void DisplayMessage(string message)
{
Console.WriteLine("Message: {0}", message);
}
}
Code to hook up an event:
public class ProcessMessage
{
Client client = new Client();
DisplayMessage msg = new DisplayMessage();
client.MessageArrived += new MessageHandler(msg.DisplayMessage);
client.CheckForMessage();
}
Related
I have a simple socket listener application. It needs to be able to receive requests and give a response and also send requests itself and receive the responses for them.
As soon as my application starts, it will start receiving in a separate thread and send a response. This part works fine.
However when I send requests through the SendRequest()-Method, I need to filter incoming responses, so the correct responses go to the correct requets earlier made. I do this (as seen in code below) with the class ResponseHandler, which lets me register a request and in return notifies my registered request, as soon as the correct response came in. A placed request should however time out after 10 seconds, so I used a CountdownEvent, which waits these 10 seconds, but releases earlier, if the response came in earlier.
Problem: My CountdownEvent waits always the whole 10 seconds and only after that, the thread, where I receive messages will continue and thus receive the response. How is this possible, when I receive on a different thread?
I would think, that my program continues to receive in that separate thread, even when the CountdownEvent.Wait() is active.
Note: The awaited response really comes back instantly after I placed the request as seen with the NetworkTool WireShark. So the timeout is not correct.
Edit: In a simple WPF-Application, where the SendRequest() is called from a button, it works. Unfortunately, this means my big program is the problem.
Service:
public class Service
{
private readonly ResponseHandler _responseHandler;
private readonly SyncSocketServer _serverSocket;
private static readonly int ServerPort = 9090;
public Service()
{
_responseHandler = new ResponseHandler();
_serverSocket = new SyncSocketServer(ServerPort);
_serverSocket.StartListening();
_serverSocket.DataReceived += ServerSocket_DataReceived;
}
public void ServerSocket_DataReceived(object sender, string message)
{
// Here I left irrelevant code out: Originally, I check here,
// whether the message is a request or response and so on, and
// I only forward the message to the _responseHandler, if it is
// indeed a response. If it is a request I send an answer.
string messageId = GetIdFromMessage(message);
_responseHandler.DataReceived(messageId, message);
}
public void SendRequest(string message)
{
string messageId = Guid.NewGuid().ToString();
string request = CreateRequest(messageId, message);
_responseHandler.Register(messageId);
_serverSocket.Send(request);
string response = _responseHandler.WaitForResponse(messageId);
Debug.WriteLine("I got the correct response: " + response);
}
}
SyncSocketServer:
public class SyncSocketServer
{
public event EventHandler<string> DataReceived;
private const int BufferSize = 1024;
private const string EndDelimiter = "\n";
private Socket _listenerSocket;
private Socket _client;
private string _data;
private Byte[] _buffer;
private readonly int _port;
public SyncSocketServer(int port)
{
_port = port;
_buffer = new Byte[BufferSize];
}
public void StartListening()
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[3];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, _port);
_listenerSocket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_listenerSocket.Bind(localEndPoint);
_listenerSocket.Listen(5);
_client = _listenerSocket.Accept();
Debug.WriteLine("Local socket opened on: {0}", _listenerSocket.LocalEndPoint);
StartReceiving();
}
private void StartReceiving()
{
Thread d = new Thread(() => {
Thread.CurrentThread.IsBackground = true;
while (true)
{
_data = null;
while (true)
{
int bytesReceived = _client.Receive(_buffer);
_data += Encoding.ASCII.GetString(_buffer, 0, bytesReceived);
if (_data.IndexOf(EndDelimiter, StringComparison.OrdinalIgnoreCase) > -1)
break;
}
Debug.WriteLine("Message received:" + _data);
OnDataReceived(_data);
}
});
d.Start();
}
public void Send(string message)
{
byte[] bytesMessage = Encoding.ASCII.GetBytes(message + EndDelimiter);
_client.Send(bytesMessage);
Debug.WriteLine("Message sent: " + message);
}
protected virtual void OnDataReceived(string data)
{
EventHandler<string> handler = DataReceived;
if (handler != null)
handler(this, data);
}
}
ResponseHandler:
public class ResponseHandler
{
private const int WaitForResponseTimeout = 10000;
private readonly Dictionary<string, PendingRequest> _pendingRequests;
public ResponseHandler()
{
_pendingRequests = new Dictionary<string, PendingRequest>();
}
public void DataReceived(string messageId, string response)
{
_pendingRequests.TryGetValue(messageId, out var pendingRequest);
if (pendingRequest == null)
Debug.WriteLine("Received response for request, that has been removed");
else
{
pendingRequest.ResponseReceived(response);
_pendingRequests.Remove(messageId);
}
}
public void Register(string messageId)
{
_pendingRequests.Add(messageId, new PendingRequest());
}
public string WaitForResponse(string messageId)
{
_pendingRequests.TryGetValue(messageId, out var pendingRequest);
if (pendingRequest == null)
return null;
pendingRequest.Await();
return pendingRequest.Response;
}
private class PendingRequest
{
public string Response { get; private set; }
private readonly CountdownEvent _countdownEvent;
public PendingRequest()
{
_countdownEvent = new CountdownEvent(1);
}
public void Await()
{
// Here, the current thread gets blocked, but
// I expect, that the thread, where I receive
// would continue receiving
_countdownEvent.Wait(WaitForResponseTimeout);
}
public void ResponseReceived(stringresponse)
{
Response = response;
_countdownEvent.Signal();
}
}
}
So, your PendingRequest and ResponseHandler classes are being accessed from different threads. So, there are a couple of things you need to do, for the sanity of your program:
a) Make sure that when you are adding and removing requests from your pending requests dictionary, you get a lock, because you are simultaneously accessing a shared datastructure from different threads. Otherwise you can corrupt your datastructure.
b) Your more immediate problem is the Await() method in PendingRequest. You are calling CountdownEvent.Wait() without verifying if your response is already set. If your response is already set, it would mean that you would wait for 10 seconds before you process it. This can happen if your response arrives, even before you invoke CountdownEvent.Wait(). In that case, CountdownEvent.Signal() will just be ignored. You should change the PendingRequest.Wait() as follows:
while (Response is not set) {
CountdownEvent.Await();
}
Also, doesn't your CountdownEvent.Wait() semaphore require a mutex to be passed to it ? Remember that your Response object is being shared between threads. This is the general paradigm for using the wait() method:
mutex.lock();
while (Response is not set) {
CountdownEvent.Await(mutex);
}
// Do your stuff, since your condition is satisfied
mutext.unlock();
The problem is actually the false assumption, that firing an event, like I did below, would result in a fire and forget:
protected virtual void OnDataReceived(string data)
{
EventHandler<string> handler = DataReceived;
if (handler != null)
handler(this, data);
}
In the function StartReceiving(), where I receive data and forward it to the subscribers, it would pause at the call, that fires the event and wait for all subscribers to finish their work (which includes, of course, waiting 10 seconds for the response). This leads to the fact, that my receiver-thread, waits for the other thread.
The solution is, to implement the call, so it will do a fire and forget:
protected virtual void OnDataReceived(string data)
{
EventHandler<string> handler = DataReceived;
if (handler != null)
handler.BeginInvoke(this, data, null, null);
}
I've been trying to implement the Emit Broadcast and On methods on C#.
I KNOW there is a package called SocketIO4DotNet and I do not wish to use it as it is deprecated. I rather understand how to do it.
I have tried to read the code of that package but it uses too dependancies over dependencies and it is going nowhere.
Currently I have the following code:
public class SocketIO
{
protected string host;
protected int port;
TcpClient client;
public SocketIO(string host, int port)
{
this.host = host;
this.port = port;
}
public bool connect()
{
if (isConnected())
{
return false;
}
try
{
client = new TcpClient();
client.Connect(host, port);
return true;
}
catch (Exception ex)
{
client = null;
return false;
}
}
public bool close()
{
if (!isConnected())
{
return false;
}
try
{
client.Close();
}
catch (Exception ex)
{
}
finally
{
client = null;
}
return true;
}
public void Emit(string message, string data)
{
if (!isConnected())
{
return;
}
NetworkStream nwStream = client.GetStream();
byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(data);
nwStream.Write(bytesToSend, 0, bytesToSend.Length);
byte[] bytesToRead = new byte[client.ReceiveBufferSize];
int bytesRead = nwStream.Read(bytesToRead, 0, client.ReceiveBufferSize);
string received = Encoding.ASCII.GetString(bytesToRead, 0, bytesRead);
}
protected bool isConnected()
{
if (client == null)
{
return false;
}
return true;
}
I need to understand how to create the listener (On method) and how to use the Emit and Broadcast in a way that I send a message along with the data.
So since I am unity but I do not want to use SocketIO as MonoBehaviour, I tweaked the Unity SocketIO package for unity to work without the Unity itself.
This is what I did:
First I have downloaded the package itself:
https://www.assetstore.unity3d.com/en/#!/content/21721
Then I changed the SocketIOComponent Awake method to be its constructor and changed its signature to accept the url (more params can be sent)
public SocketIOComponent(string url)
The next thing I did was creating a wrapper which I have called SocketIOAdapter
This is the code:
public class SocketIOAdapter {
SocketIOComponent socketIO;
protected Thread socketThread;
public SocketIOAdapter(string url)
{
socketIO = new SocketIOComponent(url);
}
public void Connect()
{
socketIO.Connect();
socketThread = new Thread(socketIO.Update);
socketThread.Start();
}
public void Emit(string eventName, JSONObject json)
{
socketIO.Emit(eventName, json);
}
public void On(string eventName, System.Action<SocketIOEvent> callback)
{
socketIO.On(eventName, callback);
}
}
And lastly I have tweaked the Update method of SocketIOComponent to sleep and continue rather than return, like this:
if (ackList.Count == 0)
{
Thread.Sleep(100);
continue;
}
if (DateTime.Now.Subtract(ackList[0].time).TotalSeconds < ackExpirationTime)
{
Thread.Sleep(100);
continue;
}
Finally I have used it like this:
socketIO = new SocketIOAdapter(serviceManager.getConfig().get("servers.socket") as string);
socketIO.Connect();
socketIO.On("connect", this.OnConnect);
Listened like this:
void OnConnect(SocketIOEvent e)
{
socketIO.Emit("shoot", new JSONObject("{}"));
}
So far so good, it's working well.
Hope this helps someone out there.
Bear with me here and try and go easy on bad practice :)
I am beginning to understand the concept of interfaces and I have implemented one in my program.. So I'll try and explain.. I am creating a class library dll that will interface with my alarm panel. The alarm panel can have two types of connection, IP and Serial.. So I have implemented an interface for this called IConnection.
and create a connection as follows:
//IConnection connection = new SerialConnection("com1", 9600);
IConnection conn = new TcpConnection(System.Net.IPAddress.Parse("192.168.0.14"), 1234);
AlarmPanel alarm = new AlarmPanel(conn, Pass);
alarm.SetLogger(logger);
alarm.Connect();
in the concrete class (correct terminology?) I implement a method called SendMessage which I use to be transport agnostic which is working well.
However I now want to add a async handler to process adhoc messages sent back that aren't command/response style messages.
I have an eventhandler working in my main TCPConnection Class:
private static void tcpReceive(Socket client)
{
try
{
// Create the state object.
StateObject state = new StateObject {workSocket = client};
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(receiveCallback), state);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void receiveCallback(IAsyncResult ar)
{
try
{
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead <= 0) return; // No data...
// Console.WriteLine("Ascii {0}", Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
Console.WriteLine("Raw: {0}", BitConverter.ToString(state.buffer, 0, bytesRead));
processMessage(new Response {Data = state.buffer,BytesLength = bytesRead} );
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(receiveCallback), state);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void processMessage(Response resp)
{
// Do something with the message here..
}
However I want to abstract the IP stuff from the processing code and move the processMessage back up into my Class which uses the interface.. (I know I not explaining this well.. so let me re-try)
Set up event handler in my "class TcpConnection : IConnection"
Turn on event handling from AlarmPanel Class which the constructor looks like:
public AlarmPanel(IConnection connection, int Password)
{
_connection = connection;
_Password = Password;
}
which uses that interface (IConnection), and be able to say use the ProcessMessage method from the alarmPanel Class, so that I can then call the same method for when I get the serial event handling working..
It sounds like you want to register an event on your interface IConnection, which AlarmPanel can subscribe to. This way you can let the IConnection implementation handle the logic for retrieving the message, but let AlarmPanel do what it wants with the recieved message.
public class AlarmPanel
{
public AlarmPanel(IConnection connection, int Password)
{
_connection = connection;
_Password = Password;
// Bind event.
_connection.MessageReceived += ProcessMessage;
}
private void ProcessMessage(object sender, MessageEventArgs e)
{
// Do your central processing here with e.Message.
}
}
public interface IConnection
{
event Action<object, MessageEventArgs> MessageRecieved;
}
public class TcpConnection : IConnection
{
// Other code.
private static void processMessage(Response resp)
{
// Do something with the message here..
var eventArgs = new MessageEventArgs
{
Message = response
};
OnMessageReceived(eventArgs);
}
protected virtual void OnMessageReceived(MessageEventArgs e)
{
// Call subscribers.
var handler = MessageRecieved;
if (handler != null) handler(this, e);
}
public event Action<object, MessageEventArgs> MessageRecieved;
}
// Class for passing Response back to AlarmPanel.
public class MessageEventArgs : System.EventArgs
{
Response Message { get; set; } // Consider using an interface for Response.
}
We have an android app that on one particular device acts like a server. And all the other devices act like clients.
Server is starting to listen for incoming tcp connections in while loop. When connection request received it fires an event, so the user of this class can handle request and then call WriteData method of the MyTcpServer class to respond to income request. Then networkStream of connected client is closed and new iteration of loop is started.
Is it a good practice to receive incoming requests in a while loop and then to close it after sending data to client?
A server and clients use the same class listed below:
public class MyTcpServer
{
#region Fields
TcpListener listener;
TcpClient client;
#endregion
#region Properties
public bool IsRunning { get; private set; }
#endregion
#region Protected methods
protected static byte[] StringToBytes(string input)
{
return Encoding.UTF8.GetBytes (input);
}
#endregion
#region Public methods
public void Start(string SERVER_IP, int PORT_NO)
{
if (IsRunning)
return;
IsRunning = true;
Task.Run (() => {
//---listen at the specified IP and port no.---
IPAddress localAdd = IPAddress.Parse (SERVER_IP);
listener = new TcpListener (localAdd, PORT_NO);
Console.WriteLine ("Listening...");
listener.Start ();
while (true) {
client = listener.AcceptTcpClient ();
//---get the incoming data through a network stream---
NetworkStream nwStream = client.GetStream ();
byte[] buffer = new byte[client.ReceiveBufferSize];
//---read incoming stream---
int bytesRead = nwStream.Read (buffer, 0, client.ReceiveBufferSize);
//---convert the data received into a string---
string dataReceived = Encoding.ASCII.GetString (buffer, 0, bytesRead);
var ip = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString ();
Array.Clear (buffer, 0, buffer.Length);
Console.WriteLine ("[" + DateTime.Now.ToString ("HH:mm:ss") + "] Received : " + dataReceived);
DataReceived?.Invoke (this, new Data () {Stream = nwStream, Message = dataReceived,
IP = ip
});
nwStream.Close();
}
});
}
public void WriteData(NetworkStream nwStream, byte[] responseData)
{
try {
nwStream.Write (responseData, 0, responseData.Length);
} catch (Exception e) {
Console.WriteLine (e.ToString ());
}
}
public void WriteData(string ip, int port, byte[] data)
{
try {
var client = new TcpClient (ip, port);
var nwstream = client.GetStream ();
nwstream.Write (data, 0, data.Length);
nwstream.Close ();
} catch (Exception e) {
Console.WriteLine (e.ToString ());
}
}
public void Stop()
{
if (client != null)
client.Close ();
listener.Stop ();
IsRunning = false;
}
#endregion
public event EventHandler<Data> DataReceived;
public class Data
{
public NetworkStream Stream {get;set;}
public string Message {get;set;}
public string IP {get;set;}
}
}
My udp class is not receiving the updated value from the stream. Device sends data continuously and when any alarm activated it add the code in stream and sends the updated value but my class is not receiving the updated value unless I restart the program.
here is my UDPListener class.
public class
{
public static int PORT_NUMBER { get; set; }
public string IpAddress { get; set; }
private readonly UdpClient udp;
public event Action<object, EventArgs> msgChanged;
IAsyncResult ar_ = null;
public UDPListener(string ipaddr, int port)
{
IpAddress = ipaddr;
PORT_NUMBER = port;
udp = new UdpClient(PORT_NUMBER);
Start();
}
public void Start()
{
StartListening();
}
public void Stop()
{
try
{
udp.Close();
}
catch { /* not necessary */ }
}
private void StartListening()
{
ar_ = udp.BeginReceive(Receive, new object());
}
private void Receive(IAsyncResult ar)
{
try
{
Thread.Sleep(150);
IPEndPoint ip = new IPEndPoint(IPAddress.Parse(IpAddress), PORT_NUMBER);
byte[] bytes = udp.EndReceive(ar, ref ip);
string message = Encoding.ASCII.GetString(bytes);
//raise event..
if (message.StartsWith("S"))
if (msgChanged != null)
msgChanged(message, EventArgs.Empty);
}
catch (Exception ex)
{
Debug.WriteLine("Error in UDPListner..." + ex.Message);
}
finally
{
StartListening();
}
}
}
Now what is happening when the program starts it will receive data "S0000.." but when alarm raises data changes to "S8000..etc" but this class continuously receiving the same "S000.." data unless I restart the class.
When I run the udp listener in while loop its works perfectly fine, it receives the updated stream and changes when alarm goes off.
here is the code for while loop udp.
while (!StopRunning)
{
Thread.Sleep(150);
udp = new UdpClient(PORT_NUMBER, AddressFamily.InterNetwork);
var ep = default(IPEndPoint);
var data = udp.Receive(ref ep);
udp.Close();
string msg = Encoding.ASCII.GetString(data);
if (msgChanged != null)
msgChanged(msg, EventArgs.Empty);
}
But I cannot make use of while loop because I have to fit this program in window service.
The main difference in your UDPListener and while loop is that in loop you are creating udp variable each time you are connecting to the UDP:
udp = new UdpClient(PORT_NUMBER, AddressFamily.InterNetwork);
In Receive(IAsyncResult ar) you only connect with the same client, so you still have the same data.
I think that you can rewrite your class something like this:
private void StartListening()
{
udp = new UdpClient(PORT_NUMBER, AddressFamily.InterNetwork);
ar_ = udp.BeginReceive(Receive, new object());
}
Make sure that you're disposing the udp connection after receive with Close() method:
byte[] bytes = udp.EndReceive(ar, ref ip);
udp.Close();