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.
}
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);
}
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();
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();
}
I'm developing one library (DLL), in which I need to provide event (interrupt) to user as one method with data. Library's work is start listing on socket, receive data from socket and pass this data to user in one method.
Library:
public void start(string IP, int port)
{
// start logic...
// receives data from socket... send this data to user
}
Application:
Library. Class a = new Library. Class();
a.start(ip, port);
// I need this method called by library automatically when it receives data...
void receivedData(string data)
{
// data which received by library....
}
How to raise event to application with data from library?
Thanks in advance....
Add an event to your library like this:
public event Action<string> OnDataReceived = null;
Then, in Application:
Library.Class a = new Library.Class();
a.OnDataReceived += receivedData;
a.start(ip, port);
That's it.
But you may want to write events with the conventions and I suggest you'll start get use to it because .NET is using events that way so whenever you bump into that convention you'll know it's events.
So if I refactor your code a little bit it should be something like:
In your class library:
//...
public class YourEventArgs : EventArgs
{
public string Data { get; set; }
}
//...
public event EventHandler DataReceived = null;
...
protected override OnDataReceived(object sender, YourEventArgs e)
{
if(DataReceived != null)
{
DataReceived(this, new YourEventArgs { Data = "data to pass" });
}
}
When your class library wants to launch the event it should call the OnDataReceived which is responsible for checking that someone is listening and constructing the appropriate EventArgs for passing by your data to the listener.
In the Application you should change your method signature:
Library.Class a = new Library.Class();
a.DataReceived += ReceivedData;
a.start(ip, port);
//...
void ReceivedData(object sender, YourEventArgs e)
{
string data = e.Data;
//...
}
You should change signature of start method to pass there delegate:
public void start(string IP, int port, Action<string> callback)
{
// start logic...
// receives data from socket... send this data to user
callback(data);
}
Library. Class a = new Library. Class();
a.start(ip, port, receivedData);
// I need this method called by library automatically when it receives data...
void receivedData(string data)
{
// data which received by library....
}
Add event to your class
public event DataReceivedEventHandler DataReceived;
public delegate void DataReceivedEventHandler(object sender, SocketDataReceivedEventArgs e);
Create a class that contents your required parameters like Ex : SocketDataReceivedEventArgs here
Trigger event like
SocketDataReceivedEventArgs DataReceiveDetails = new SocketDataReceivedEventArgs();
DataReceiveDetails.data = "your data here";
DataReceived(this, DataReceiveDetails);
in application create method
void receivedData(object sender, SocketDataReceivedEventArgs e)
{
// e.data is data which received by library....
}
Now attach handler to it
Library. Class a = new Library. Class();
a.DataReceived += receivedData;
a.start(ip, port);
You need to write it in multiple threads as your requirement is
Here is how you can add thread safe support to above
Dispatcher.Invoke and accessing textbox from another thread
HI everyone,
I'm going to be writing some code that has to listen for TCPIP messages coming from GSM mobile phones over GPRS. In the fullness of time, I see this as running on a Virtual Private Server, and it could well be processing multiple messages every second.
I'm a bit of a network programming virgin, so I've done a bit of research on the internet, and read a few tutorials. The approach I am considering at the moment is a windows service using sockets to monitor the port. If my understanding is correct, I need one socket to listen for connections from clients, and each time someone tries to connect with the port I will be passed another socket with which to communicate with them? Does this sound about right to more experienced ears?
I'm planning on using asynchronous communication, but on of the bigger design questions is whether to use threading or not. Threading isn't something I've really played with, and I am aware of a number of pitfalls - race conditions and debugging problems being but two.
If I avoid threads, I know I have to supply an object that acts as an identifier for a particular conversation. I was thinking GUIDs for this - any opinions?
Thanks in advance for any responses...
Martin
Starting from .net framework 2.0 SP1 there are some changings in socket libraries related to asyncronous sockets.
All multithreading used under the hood. We have no need to use multithreading manually (we don't need to use even ThreadPool explicitly). All what we do - using BeginAcceptSocket for starting accepting new connections, and using SocketAsyncEventArgs after accepting new connection .
Short implementation:
//In constructor or in method Start
var tcpServer = new TcpListener(IPAddress.Any, port);
tcpServer.Start();
tcpServer.BeginAcceptSocket(EndAcceptSocket, tcpServer);
//In EndAcceptSocket
Socket sock= lister.EndAcceptSocket(asyncResult);
var e = new SocketAsyncEventArgs();
e.Completed += ReceiveCompleted; //some data receive handle
e.SetBuffer(new byte[SocketBufferSize], 0, SocketBufferSize);
if (!sock.ReceiveAsync(e))
{//IO operation finished syncronously
//handle received data
ReceiveCompleted(sock, e);
}//IO operation finished syncronously
//Add sock to internal storage
Full implementation:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace Ample
{
public class IPEndPointEventArgs : EventArgs
{
public IPEndPointEventArgs(IPEndPoint ipEndPoint)
{
IPEndPoint = ipEndPoint;
}
public IPEndPoint IPEndPoint { get; private set; }
}
public class DataReceivedEventArgs : EventArgs
{
public DataReceivedEventArgs(byte[] data, IPEndPoint ipEndPoint)
{
Data = data;
IPEndPoint = ipEndPoint;
}
public byte[] Data { get; private set; }
public IPEndPoint IPEndPoint { get; private set; }
}
/// <summary>
/// TcpListner wrapper
/// Encapsulates asyncronous communications using TCP/IP.
/// </summary>
public sealed class TcpServer : IDisposable
{
//----------------------------------------------------------------------
//Construction, Destruction
//----------------------------------------------------------------------
/// <summary>
/// Creating server socket
/// </summary>
/// <param name="port">Server port number</param>
public TcpServer(int port)
{
connectedSockets = new Dictionary<IPEndPoint, Socket>();
tcpServer = new TcpListener(IPAddress.Any, port);
tcpServer.Start();
tcpServer.BeginAcceptSocket(EndAcceptSocket, tcpServer);
}
~TcpServer()
{
DisposeImpl(false);
}
public void Dispose()
{
DisposeImpl(true);
}
//----------------------------------------------------------------------
//Public Methods
//----------------------------------------------------------------------
public void SendData(byte[] data, IPEndPoint endPoint)
{
Socket sock;
lock (syncHandle)
{
if (!connectedSockets.ContainsKey(endPoint))
return;
sock = connectedSockets[endPoint];
}
sock.Send(data);
}
//----------------------------------------------------------------------
//Events
//----------------------------------------------------------------------
public event EventHandler<IPEndPointEventArgs> SocketConnected;
public event EventHandler<IPEndPointEventArgs> SocketDisconnected;
public event EventHandler<DataReceivedEventArgs> DataReceived;
//----------------------------------------------------------------------
//Private Functions
//----------------------------------------------------------------------
#region Private Functions
//Обработка нового соединения
private void Connected(Socket socket)
{
var endPoint = (IPEndPoint)socket.RemoteEndPoint;
lock (connectedSocketsSyncHandle)
{
if (connectedSockets.ContainsKey(endPoint))
{
theLog.Log.DebugFormat("TcpServer.Connected: Socket already connected! Removing from local storage! EndPoint: {0}", endPoint);
connectedSockets[endPoint].Close();
}
SetDesiredKeepAlive(socket);
connectedSockets[endPoint] = socket;
}
OnSocketConnected(endPoint);
}
private static void SetDesiredKeepAlive(Socket socket)
{
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
const uint time = 10000;
const uint interval = 20000;
SetKeepAlive(socket, true, time, interval);
}
static void SetKeepAlive(Socket s, bool on, uint time, uint interval)
{
/* the native structure
struct tcp_keepalive {
ULONG onoff;
ULONG keepalivetime;
ULONG keepaliveinterval;
};
*/
// marshal the equivalent of the native structure into a byte array
uint dummy = 0;
var inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
BitConverter.GetBytes((uint)(on ? 1 : 0)).CopyTo(inOptionValues, 0);
BitConverter.GetBytes((uint)time).CopyTo(inOptionValues, Marshal.SizeOf(dummy));
BitConverter.GetBytes((uint)interval).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);
// of course there are other ways to marshal up this byte array, this is just one way
// call WSAIoctl via IOControl
int ignore = s.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
}
//socket disconnected handler
private void Disconnect(Socket socket)
{
var endPoint = (IPEndPoint)socket.RemoteEndPoint;
lock (connectedSocketsSyncHandle)
{
connectedSockets.Remove(endPoint);
}
socket.Close();
OnSocketDisconnected(endPoint);
}
private void ReceiveData(byte[] data, IPEndPoint endPoint)
{
OnDataReceived(data, endPoint);
}
private void EndAcceptSocket(IAsyncResult asyncResult)
{
var lister = (TcpListener)asyncResult.AsyncState;
theLog.Log.Debug("TcpServer.EndAcceptSocket");
if (disposed)
{
theLog.Log.Debug("TcpServer.EndAcceptSocket: tcp server already disposed!");
return;
}
try
{
Socket sock;
try
{
sock = lister.EndAcceptSocket(asyncResult);
theLog.Log.DebugFormat("TcpServer.EndAcceptSocket: remote end point: {0}", sock.RemoteEndPoint);
Connected(sock);
}
finally
{
//EndAcceptSocket can failes, but in any case we want to accept
new connections
lister.BeginAcceptSocket(EndAcceptSocket, lister);
}
//we can use this only from .net framework 2.0 SP1 and higher
var e = new SocketAsyncEventArgs();
e.Completed += ReceiveCompleted;
e.SetBuffer(new byte[SocketBufferSize], 0, SocketBufferSize);
BeginReceiveAsync(sock, e);
}
catch (SocketException ex)
{
theLog.Log.Error("TcpServer.EndAcceptSocket: failes!", ex);
}
catch (Exception ex)
{
theLog.Log.Error("TcpServer.EndAcceptSocket: failes!", ex);
}
}
private void BeginReceiveAsync(Socket sock, SocketAsyncEventArgs e)
{
if (!sock.ReceiveAsync(e))
{//IO operation finished syncronously
//handle received data
ReceiveCompleted(sock, e);
}//IO operation finished syncronously
}
void ReceiveCompleted(object sender, SocketAsyncEventArgs e)
{
var sock = (Socket)sender;
if (!sock.Connected)
Disconnect(sock);
try
{
int size = e.BytesTransferred;
if (size == 0)
{
//this implementation based on IO Completion ports, and in this case
//receiving zero bytes mean socket disconnection
Disconnect(sock);
}
else
{
var buf = new byte[size];
Array.Copy(e.Buffer, buf, size);
ReceiveData(buf, (IPEndPoint)sock.RemoteEndPoint);
BeginReceiveAsync(sock, e);
}
}
catch (SocketException ex)
{
//We can't truly handle this excpetion here, but unhandled
//exception caused process termination.
//You can add new event to notify observer
theLog.Log.Error("TcpServer: receive data error!", ex);
}
catch (Exception ex)
{
theLog.Log.Error("TcpServer: receive data error!", ex);
}
}
private void DisposeImpl(bool manualDispose)
{
if (manualDispose)
{
//We should manually close all connected sockets
Exception error = null;
try
{
if (tcpServer != null)
{
disposed = true;
tcpServer.Stop();
}
}
catch (Exception ex)
{
theLog.Log.Error("TcpServer: tcpServer.Stop() failes!", ex);
error = ex;
}
try
{
foreach (var sock in connectedSockets.Values)
{
sock.Close();
}
}
catch (SocketException ex)
{
//During one socket disconnected we can faced exception
theLog.Log.Error("TcpServer: close accepted socket failes!", ex);
error = ex;
}
if ( error != null )
throw error;
}
}
private void OnSocketConnected(IPEndPoint ipEndPoint)
{
var handler = SocketConnected;
if (handler != null)
handler(this, new IPEndPointEventArgs(ipEndPoint));
}
private void OnSocketDisconnected(IPEndPoint ipEndPoint)
{
var handler = SocketDisconnected;
if (handler != null)
handler(this, new IPEndPointEventArgs(ipEndPoint));
}
private void OnDataReceived(byte[] data, IPEndPoint ipEndPoint)
{
var handler = DataReceived;
if ( handler != null )
handler(this, new DataReceivedEventArgs(data, ipEndPoint));
}
#endregion Private Functions
//----------------------------------------------------------------------
//Private Fields
//----------------------------------------------------------------------
#region Private Fields
private const int SocketBufferSize = 1024;
private readonly TcpListener tcpServer;
private bool disposed;
private readonly Dictionary<IPEndPoint, Socket> connectedSockets;
private readonly object connectedSocketsSyncHandle = new object();
#endregion Private Fields
}
}
It is surprisingly simple to make a multi-threaded server. Check out this example.
class Server
{
private Socket socket;
private List<Socket> connections;
private volatile Boolean endAccept;
// glossing over some code.
/// <summary></summary>
public void Accept()
{
EventHandler<SocketAsyncEventArgs> completed = null;
SocketAsyncEventArgs args = null;
completed = new EventHandler<SocketAsyncEventArgs>((s, e) =>
{
if (e.SocketError != SocketError.Success)
{
// handle
}
else
{
connections.Add(e.AcceptSocket);
ThreadPool.QueueUserWorkItem(AcceptNewClient, e.AcceptSocket);
}
e.AcceptSocket = null;
if (endAccept)
{
args.Dispose();
}
else if (!socket.AcceptAsync(args))
{
completed(socket, args);
}
});
args = new SocketAsyncEventArgs();
args.Completed += completed;
if (!socket.AcceptAsync(args))
{
completed(socket, args);
}
}
public void AcceptNewClient(Object state)
{
var socket = (Socket)state;
// proccess
}
}
A bit of advise from the guy who deals mainly with mobile networking: do your homework with regular networking connection, preferably on the localhost. This will save you a lot of time during testing and will keep you sane until you figure out the approach that works for you best.
As for some particular implementation, I always go with synchronized sockets (you will need to configure timeouts to not to get stuck if something will go wrong) and everything runs in separate threads that are synchronized with the help of events. It's much simplier than you think. Here's some useful links to get you started:
http://msdn.microsoft.com/en-us/library/3e8s7xdd.aspx
http://msdn.microsoft.com/en-us/library/ms228969.aspx
I'm writing the same application right now and I use solution like this:
http://clutch-inc.com/blog/?p=4
It's been tested right now and works perfectly. It is important to make this service only for receiving and storing messages (somewhere) without other work. I'm using NServiceBus for saving messages. Other service takes messages from queue and do the rest.
Well, the C# syntax is not fresh in my mind now but I don't think it is to much different from the Posix standard.
What you can may do is when you create your listen socket you can stipulate a value for the backlog (maximum number of simultaneous connections for that server) and create a thread pull with the same size. Thread pools are easier to use than traditional ones. The TCP you queue for you all the connections above the backlog parameter.