refactoring async socket programming in C# - c#

I have a bunch of async methods that I want to expose via C# sockets. The general pattern in the MSDN documentaions Has the following form:
public static void StartListening()
{
...
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);
...
listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
...
}
public static void AcceptCallback(IAsyncResult ar)
{
...
handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
public static void ReadCallback(IAsyncResult ar)
{
...
StateObject state = (StateObject) ar.AsyncState;
...
CalculateResult(state);
...
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
...
}
So writing all this in a nice and clean form without repetition of code has been a challenge. I am thinking along these lines but have not been able to connect the dots:
public static void StartListeningMaster()
{
string ipAddress = "localhost";
IPHostEntry ipHost = Dns.GetHostEntry(ipAddress);
IPAddress address = ipHost.AddressList[0];
StartListening(50000, address, AcceptCallback1);
StartListening(50001, address, AcceptCallback2);
StartListening(50002, address, AcceptCallback3);
...
}
public static void StartListening(int port, IPAddress ipAddress,
Action<IAsyncResult> acceptCallback) {...}
public static void AcceptCallback1(IAsyncResult ar)
{
...
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize,
0, new AsyncCallback1(ReadCallback1), state);
}
...
This works fine up to this point. But to refactor it properly I would like to have one AcceptCallback method that takes as its parameter a generic ReadCallback that takes as its parameter a CalculateResult method. This way I would not have any repetition of code. However, if I modify my AcceptCallback method to take any more parameters than IAsyncResult (for example something like:
public static void StartListening(int port, IPAddress ipAddress, Action<IAsyncResult, Action<IAsyncResult>> acceptCallback) {...}
public static void AcceptCallback(IAsyncResult ar, Action<IAsyncResult> readCallback) {}
I break the AsyncCallback delegate contract.
public delegate void AsyncCallback(IAsyncResult ar);
Then I looked into extending the existing interfaces to allow the functionality. I looked into extending
public interface IAsyncResult
But that does not seem to be the right approach either. So, how do I write this code so I do not copy and paste pretty much the same code all over the place?

So the way I tackle this is by moving the basic components in to their own abstract objects. Then build upon those objects. For example, the server only needs to accept/track connections. So I would make a server object that looks something like this:
namespace MultiServerExample.Base
{
public interface IAsyncServerBase
{
void StartListening();
bool IsListening { get; }
void StopListening();
void WriteDataToAllClients(byte[] data);
}
public abstract class AsyncServerBase<TClientBase> : IAsyncServerBase
where TClientBase : IAsyncClientBase, new()
{
// implement a TcpListener to gain access to Active property
private sealed class ActiveTcpListener : TcpListener
{
public ActiveTcpListener(IPAddress localaddr, int port)
: base(localaddr, port) { }
public bool IsActive => Active;
}
// our listener object
private ActiveTcpListener Listener { get; }
// our clients
private ConcurrentDictionary<string, TClientBase> Clients { get; }
// construct with a port
public AsyncServerBase(int port)
{
Clients = new ConcurrentDictionary<string, TClientBase>();
Listener = new ActiveTcpListener(IPAddress.Any, port);
}
// virtual methods for client action
public virtual void OnClientConnected(TClientBase client) { }
public virtual void OnClientDisconnected(TClientBase client, Exception ex) { }
// start the server
public void StartListening()
{
if(!IsListening)
{
Listener.Start();
Listener.BeginAcceptTcpClient(OnAcceptedTcpClient, this);
}
}
// check if the server is running
public bool IsListening =>
Listener.IsActive;
// stop the server
public void StopListening()
{
if (IsListening)
{
Listener.Stop();
Parallel.ForEach(Clients, x => x.Value.DetachClient(null));
Clients.Clear();
}
}
// async callback for when a client wants to connect
private static void OnAcceptedTcpClient(IAsyncResult res)
{
var me = (AsyncServerBase<TClientBase>)res.AsyncState;
if (!me.IsListening) { return; }
try
{
TcpClient client = null;
try
{
client = me.Listener.EndAcceptTcpClient(res);
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Warning: unable to accept client:\n{ex}");
}
if(client != null)
{
// create a new client
var t = new TClientBase();
// set up error callbacks
t.Error += me.OnClientBaseError;
// notify client we have attached
t.AttachClient(client);
// track the client
me.Clients[t.Id] = t;
// notify we have a new connection
me.OnClientConnected(t);
}
}
finally
{
// if we are still listening, wait for another connection
if(me.IsListening)
{
me.Listener.BeginAcceptSocket(OnAcceptedTcpClient, me);
}
}
}
// Event callback from a client that an error has occurred
private void OnClientBaseError(object sender, AsyncClientBaseErrorEventArgs e)
{
var client = (TClientBase)sender;
client.Error -= OnClientBaseError;
OnClientDisconnected(client, e.Exception);
client.DetachClient(e.Exception);
Clients.TryRemove(client.Id, out _);
}
// utility method to write data to all clients connected
public void WriteDataToAllClients(byte[] data)
{
Parallel.ForEach(Clients, x => x.Value.WriteData(data));
}
}
}
At this point all the basics of running a server have been accounted for. Now for the client that runs on the server:
namespace MultiServerExample.Base
{
public interface IAsyncClientBase
{
event EventHandler<AsyncClientBaseErrorEventArgs> Error;
void AttachClient(TcpClient client);
void WriteData(byte[] data);
void DetachClient(Exception ex);
string Id { get; }
}
public abstract class AsyncClientBase : IAsyncClientBase
{
protected virtual int ReceiveBufferSize { get; } = 1024;
private TcpClient Client { get; set; }
private byte[] ReceiveBuffer { get; set; }
public event EventHandler<AsyncClientBaseErrorEventArgs> Error;
public string Id { get; }
public AsyncClientBase()
{
Id = Guid.NewGuid().ToString();
}
public void AttachClient(TcpClient client)
{
if(ReceiveBuffer != null) { throw new InvalidOperationException(); }
ReceiveBuffer = new byte[ReceiveBufferSize];
Client = client;
try
{
Client.GetStream().
BeginRead(ReceiveBuffer, 0, ReceiveBufferSize, OnDataReceived, this);
OnAttachedToServer();
}
catch (Exception ex)
{
Error?.Invoke(this,
new AsyncClientBaseErrorEventArgs(ex, "BeginRead"));
}
}
public void DetachClient(Exception ex)
{
try
{
Client.Close();
OnDetachedFromServer(ex);
}
catch { /* intentionally swallow */ }
Client = null;
ReceiveBuffer = null;
}
public virtual void OnDataReceived(byte[] buffer) { }
public virtual void OnAttachedToServer() { }
public virtual void OnDetachedFromServer(Exception ex) { }
public void WriteData(byte[] data)
{
try
{
Client.GetStream().BeginWrite(data, 0, data.Length, OnDataWrote, this);
}
catch(Exception ex)
{
Error?.Invoke(this, new AsyncClientBaseErrorEventArgs(ex, "BeginWrite"));
}
}
private static void OnDataReceived(IAsyncResult iar)
{
var me = (AsyncClientBase)iar.AsyncState;
if(me.Client == null) { return; }
try
{
var bytesRead = me.Client.GetStream().EndRead(iar);
var buf = new byte[bytesRead];
Array.Copy(me.ReceiveBuffer, buf, bytesRead);
me.OnDataReceived(buf);
}
catch (Exception ex)
{
me.Error?.Invoke(me, new AsyncClientBaseErrorEventArgs(ex, "EndRead"));
}
}
private static void OnDataWrote(IAsyncResult iar)
{
var me = (AsyncClientBase)iar.AsyncState;
try
{
me.Client.GetStream().EndWrite(iar);
}
catch(Exception ex)
{
me.Error?.Invoke(me,
new AsyncClientBaseErrorEventArgs(ex, "EndWrite"));
}
}
}
}
Now all your base code is written. You don't need to change this in any way. You simply implement your own client and server to respond accordingly. For example, here is a basic server implementation:
public class MyServer : AsyncServerBase<MyClient>
{
public MyServer(int port) : base(port)
{
}
public override void OnClientConnected(MyClient client)
{
Console.WriteLine($"* MyClient connected with Id: {client.Id}");
base.OnClientConnected(client);
}
public override void OnClientDisconnected(MyClient client, Exception ex)
{
Console.WriteLine($"***** MyClient disconnected with Id: {client.Id} ({ex.Message})");
base.OnClientDisconnected(client, ex);
}
}
And here is a client that the server above uses for communication:
public class MyClient : AsyncClientBase
{
public override void OnAttachedToServer()
{
base.OnAttachedToServer();
Console.WriteLine($"{Id}: {GetType().Name} attached. Waiting for data...");
}
public override void OnDataReceived(byte[] buffer)
{
base.OnDataReceived(buffer);
Console.WriteLine($"{Id}: {GetType().Name} recieved {buffer.Length} bytes. Writing 5 bytes back.");
WriteData(new byte[] { 1, 2, 3, 4, 5 });
}
public override void OnDetachedFromServer(Exception ex)
{
base.OnDetachedFromServer(ex);
Console.WriteLine($"{Id}: {GetType().Name} detached.");
}
}
And to drive the point home, here is another client that simply would plug in to the same server implementation, but gives it different characteristics:
public class MyOtherClient : AsyncClientBase
{
public override void OnAttachedToServer()
{
base.OnAttachedToServer();
Console.WriteLine($"{Id}: {GetType().Name} attached. Writing 4 bytes back.");
WriteData(new byte[] { 1, 2, 3, 4 });
}
public override void OnDataReceived(byte[] buffer)
{
base.OnDataReceived(buffer);
Console.WriteLine($"{Id}: {GetType().Name} recieved {buffer.Length} bytes.");
}
public override void OnDetachedFromServer(Exception ex)
{
base.OnDetachedFromServer(ex);
Console.WriteLine($"{Id}: {GetType().Name} detached.");
}
}
As far as using this, here is a small test program that puts it through a stress-test:
class Program
{
static void Main(string[] args)
{
var servers = new IAsyncServerBase[]
{
new MyServer(50000),
new MyServer(50001),
new MyOtherServer(50002)
};
foreach (var s in servers)
{
s.StartListening();
}
RunTestUsingMyServer("1", 89, 50000);
RunTestUsingMyServer("2", 127, 50001);
RunTestUsingMyOtherServer("3", 88, 50002);
Console.Write("Press any key to exit... ");
Console.ReadKey(true);
foreach (var s in servers)
{
s.WriteDataToAllClients(new byte[] { 1, 2, 3, 4, 5 });
s.StopListening();
}
}
private static void RunTestUsingMyServer(string name, int clientCount, int port)
{
Parallel.For(0, clientCount, x =>
{
using (var t = new TcpClient())
{
t.Connect(IPAddress.Loopback, port);
t.GetStream().Write(new byte[] { 1, 2, 3, 4, 5 }, 0, 5);
t.GetStream().Read(new byte[512], 0, 512);
t.Close();
}
Console.WriteLine($"FINISHED PASS {name} #{x}");
});
}
private static void RunTestUsingMyOtherServer(string name, int clientCount, int port)
{
Parallel.For(0, clientCount, x =>
{
using (var t = new TcpClient())
{
t.Connect(IPAddress.Loopback, port);
t.GetStream().Read(new byte[512], 0, 512);
t.GetStream().Write(new byte[] { 1, 2, 3, 4, 5, 6 }, 0, 6);
t.Close();
}
Console.WriteLine($"FINISHED PASS {name} #{x}");
});
}
}
If interested, here is the full source code you can check out. Hopefully this gets you to where you want to be as it pertains to reusing code.

I don't know if this can help. You can define a state object with all the information related to every port:
public class StateObject
{
public string Name;
public Socket Listener;
public IPEndPoint LocalEndPoint;
//...
public StateObject(Socket listener, IPEndPoint endPoint, string name)
{
Listener = listener;
LocalEndPoint = endPoint;
Name = name;
}
}
Then, you can use it as you need:
public static void StartListeningMaster()
{
string ipAddress = "localhost";
IPHostEntry ipHost = Dns.GetHostEntry(ipAddress);
IPAddress address = ipHost.AddressList[0];
StartListening(50000, address, "Main port");
StartListening(50001, address, "Service port");
StartListening(50002, address, "Extra");
//...
}
public static void StartListening(int port, IPAddress ipAddress, string name = "")
{
//...
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
//...
StateObject state = new StateObject(listener, localEndPoint);
listener.BeginAccept(AcceptCallback, state);
//...
}
public static void AcceptCallback(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
//...
handler.BeginReceive(client.buffer, 0, StateObject.BufferSize,
0, new AsyncCallback(ReadCallback), state);
}
public static void ReadCallback(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
// Always have the info related to every socket
Socket listener = state.Listener;
string address = state.LocalEndPoint.Address.ToString();
int port = state.LocalEndPoint.Port;
string name = state.Name;
//...
StateObject state = (StateObject)ar.AsyncState;
//...
CalculateResult(state);
//...
handler.BeginReceive(client.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
//...
}
The CalculateResult(state) method will have all the necessary info to do whatever. This way, you only have one StartListening(), one AcceptCallback() and one ReadCallback() for all the ports to manage.

Related

Netcore Socket Server Not Closing

Hi All I'm developing a quite simple Socket Server on .NetCore. This server must allow multiple clients and start reading from them while it's not canceled.
This is the code of it:
public class ClientHandler
{
public ClientHandler(Socket workSocket, int bufferSize)
{
WorkSocket = workSocket;
BufferSize = bufferSize;
receiveBuffer = new byte[BufferSize];
currentBuffer = new byte[0];
}
public Socket WorkSocket { get; }
// Size of receive buffer.
public int BufferSize { get; }
// Receive buffer.
public byte[] receiveBuffer { get; set; }
// Received data.
public byte[] currentBuffer { get; set; }
}
public class MyServer
{
public MyServer(string ipAddress, int port,
IProtocolParser parser, CancellationToken token, ILogger logger)
{
_ipAddress = ipAddress;
_port = port;
InternalCts = new CancellationTokenSource();
_token = InternalCts.Token;
_parser = parser;
_logger = logger.ForContext(GetType());
}
private const int BUFFER_SIZE = 1024;
private readonly IProtocolParser _parser;
private readonly ILogger _logger;
private readonly int _port;
private readonly string _ipAddress;
private readonly CancellationToken _token;
private readonly ManualResetEvent _allDone = new ManualResetEvent(false);
private CancellationTokenSource InternalCts { get; set; }
private Socket Server { get; set; }
public void Start()
{
try
{
var ipAddress = IPAddress.Parse(_ipAddress);
var endpoint = new IPEndPoint(ipAddress, _port);
_logger.Debug("Creating Socket Server On {ipAddress}:{port}", _ipAddress, _port);
Server = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
Server.Bind(endpoint);
Server.Listen(10);
while(!_token.IsCancellationRequested)
{
_allDone.Reset();
_logger.Debug("Waiting For Client");
Server.BeginAccept(AcceptCallback, Server);
_logger.Debug("Waiting One!");
_allDone.WaitOne();
_logger.Debug("Begin Accept Finished");
}
_logger.Debug("Task Finished!");
return;
}
catch (Exception e)
{
_logger.Error("error");
}
}
private void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue.
_allDone.Set();
// Get the socket that handles the client request.
var listener = (Socket)ar.AsyncState;
var handler = listener.EndAccept(ar);
// Create the state object.
var client = new ClientHandler(handler, BUFFER_SIZE);
handler.BeginReceive(client.receiveBuffer, 0, client.BufferSize, 0,
new AsyncCallback(ReadCallback), client);
}
private void ReadCallback(IAsyncResult ar)
{
// Retrieve the state object and the handler socket
// from the asynchronous state object.
var client = (ClientHandler)ar.AsyncState;
var handler = client.WorkSocket;
// Read data from the client socket.
var bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
client.currentBuffer = ArrayExtensions.Combine(
client.currentBuffer, client.receiveBuffer.SubArray(0, bytesRead));
var (message, newBuffer) = _parser.UnpackMessage(client.currentBuffer);
if (!string.IsNullOrEmpty(message))
{
_logger.Debug("New Message Received: {message}", message);
}
client.currentBuffer = newBuffer;
}
if (!_token.IsCancellationRequested)
{
// Not all data received. Get more.
handler.BeginReceive(client.receiveBuffer, 0, client.BufferSize, 0,
new AsyncCallback(ReadCallback), client);
}
}
public void Stop()
{
InternalCts.Cancel();
_logger.Debug("Stopping Server...");
_logger.Debug("Closing Socket...");
Server.Close();
_allDone.Set();
_logger.Debug("Socket Closed!");
}
}
And this is the main program:
static void Main(string[] args)
{
var parser = new VenomOEMProtocolParser();
var cts = new CancellationTokenSource();
var logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.FromLogContext()
.Enrich.WithThreadId()
.WriteTo.Console().CreateLogger();
var venomOem = new VenomOEM("192.168.0.107", 100, parser, cts.Token, logger);
AppDomain.CurrentDomain.ProcessExit += (s, e) =>
{
venomOem.Stop();
};
Console.CancelKeyPress += delegate
{
venomOem.Stop();
};
try
{
venomOem.Start();
logger.Debug("FINISHED!");
}
catch (OperationCanceledException oce)
{
logger.Debug(oce, "Operation Canceled Exception");
}
catch (Exception e)
{
logger.Error(e, "Unexpected Exception!");
}
}
As you can see I start the sever and stop it when Ctrl+C keys are pressed on the console app and the stop method is excecuted but somehow the application freezes and doesn't close. I think that it's something related to the reset events but cannot find the problem.
Any suggestion?
Thanks!

How can I implement socketIO in C# to work with NodeJS?

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.

Improving TCP forwarder using Socket in C#

I have Implemented a simple TCP forwarder which is connected to a backend server. This server is set on http://localhost:5000 and this TCP forwarder is listening to http://localhost:5001. Using another machine to generate load using wrk which is a HTTP benchmarking tool to generator load, I have 2 different results. When I send the load directly to the service which is based on asp.net core web api on the kestrel more than 230K requests/second are handled but when I send the load to this TCP forwarder 83Krequest/second can be handled. Here is the code:
using System;
using System.Net;
using System.Net.Sockets;
namespace BrunoGarcia.Net
{
static void Main(string[] args)
{
new TcpForwarderSlim().Start(
new IPEndPoint(IPAddress.Parse(args[0]), int.Parse(args[1])),
new IPEndPoint(IPAddress.Parse(args[2]), int.Parse(args[3])));
}
public class TcpForwarderSlim
{
private readonly Socket _mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public void Start(IPEndPoint local, IPEndPoint remote)
{
_mainSocket.Bind(local);
_mainSocket.Listen(10);
while (true)
{
var source = _mainSocket.Accept();
var destination = new TcpForwarderSlim();
var state = new State(source, destination._mainSocket);
destination.Connect(remote, source);
source.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
}
private void Connect(EndPoint remoteEndpoint, Socket destination)
{
var state = new State(_mainSocket, destination);
_mainSocket.Connect(remoteEndpoint);
_mainSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, OnDataReceive, state);
}
private static void OnDataReceive(IAsyncResult result)
{
var state = (State)result.AsyncState;
try
{
var bytesRead = state.SourceSocket.EndReceive(result);
if (bytesRead > 0)
{
state.DestinationSocket.Send(state.Buffer, bytesRead, SocketFlags.None);
state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
}
catch
{
state.DestinationSocket.Close();
state.SourceSocket.Close();
}
}
private class State
{
public Socket SourceSocket { get; private set; }
public Socket DestinationSocket { get; private set; }
public byte[] Buffer { get; private set; }
public State(Socket source, Socket destination)
{
SourceSocket = source;
DestinationSocket = destination;
Buffer = new byte[8192];
}
}
}
}
What is the problem do you think?! How can I improve the result when I use TCP forwarder?!Or is there a better way to make a tunnel or a forwarder to listen to a port and send the TCP requests to one of 2 or more back end service?!
You do not start listening for a more data till state.DestinationSocket.Send completes. You can start listening for more data as soon as you start processing the Send, the order of multiple BeginSend calls is preserved so if you switched to that it would allow you to start processing the next request before the previous one finished.
Important note! You will now need to create a new buffer (or use a pool of buffers) for each new BeginReceive request. Below is untested code but hopefully is close enough to get you on the right path.
public class TcpForwarderSlim
{
private readonly Socket _mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public void Start(IPEndPoint local, IPEndPoint remote)
{
_mainSocket.Bind(local);
_mainSocket.Listen(10);
while (true)
{
var source = _mainSocket.Accept();
var destination = new TcpForwarderSlim();
var state = new State(source, destination._mainSocket);
destination.Connect(remote, source);
source.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
}
private void Connect(EndPoint remoteEndpoint, Socket destination)
{
var state = new State(_mainSocket, destination);
_mainSocket.Connect(remoteEndpoint);
_mainSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, OnDataReceive, state);
}
private static void OnDataReceive(IAsyncResult result)
{
var state = (State)result.AsyncState;
try
{
var bytesRead = state.SourceSocket.EndReceive(result);
if (bytesRead > 0)
{
//Start an asyncronous send.
var sendAr = state.DestinationSocket.BeginSend(state.Buffer, 0, bytesRead, SocketFlags.None,null,null);
//Get or create a new buffer for the state object.
var oldBuffer = state.ReplaceBuffer();
state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
//Wait for the send to finish.
state.DestinationSocket.EndSend(sendAr);
//Return byte[] to the pool.
state.AddBufferToPool(oldBuffer);
}
}
catch
{
state.DestinationSocket.Close();
state.SourceSocket.Close();
}
}
private class State
{
private readonly ConcurrentBag<byte[]> _bufferPool = new ConcurrentBag<byte[]>();
private readonly int _bufferSize;
public Socket SourceSocket { get; private set; }
public Socket DestinationSocket { get; private set; }
public byte[] Buffer { get; private set; }
public State(Socket source, Socket destination)
{
SourceSocket = source;
DestinationSocket = destination;
_bufferSize = Math.Min(SourceSocket.ReceiveBufferSize, DestinationSocket.SendBufferSize);
Buffer = new byte[_bufferSize];
}
/// <summary>
/// Replaces the buffer in the state object.
/// </summary>
/// <returns>The previous buffer.</returns>
public byte[] ReplaceBuffer()
{
byte[] newBuffer;
if (!_bufferPool.TryTake(out newBuffer))
{
newBuffer = new byte[_bufferSize];
}
var oldBuffer = Buffer;
Buffer = newBuffer;
return oldBuffer;
}
public void AddBufferToPool(byte[] buffer)
{
_bufferPool.Add(buffer);
}
}
}

UDP not receiving updated values

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();

Asynchronous TCP socket not working in C#

I keep getting this error:
"The IAsyncResult object was not returned from the corresponding asynchonous method on this class. Parameter name : aysncResult. Line 105.
This happens when I attempt to connect to a local server; It errors and won't connect.
Here's my client code:
public class Client
{
public delegate void OnConnectEventHandler(Client sender, bool connected);
public event OnConnectEventHandler OnConnect;
public delegate void OnSendEventHandler(Client sender, int sent);
public event OnSendEventHandler OnSend;
public delegate void OnDisconnectEventHandler(Client sender);
public event OnDisconnectEventHandler OnDisconnect;
Socket socket;
public bool Connected
{
get
{
if (socket != null)
{
return socket.Connected;
}
return false;
}
}
public Client()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
public void SendData()
{
}
public void Connect(string IP, int port)
{
if (socket == null)
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
socket.BeginConnect(IP, port, new AsyncCallback(sendCallback), null);
}
private void connectCallback(IAsyncResult ar)
{
//try
//{
socket.EndConnect(ar);
if (OnConnect != null)
{
OnConnect(this, Connected);
}
//}
//catch
//{
//}
}
public void Send(byte[] data, int index, int length)
{
socket.BeginSend(BitConverter.GetBytes(length), 0, 4, SocketFlags.None, new AsyncCallback(sendCallback), null);
socket.BeginSend(data, index, length, SocketFlags.None, new AsyncCallback(sendCallback), null);
}
private void sendCallback(IAsyncResult ar)
{
try
{
int sent = socket.EndSend(ar); ( errrors here )
if (OnSend != null)
{
OnSend(this, sent);
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.ToString());
return;
}
}
public void Disconnect()
{
try
{
if (socket.Connected)
{
socket.Close();
socket = null;
if (OnDisconnect != null)
{
OnDisconnect(this);
}
}
}
catch
{
}
}
you should not have two pending BeginSend operations.
Send the size and then the buffer when it completes:
public void Send(byte[] data, int index, int length)
{
//add data as state
socket.BeginSend(BitConverter.GetBytes(length), 0, 4, SocketFlags.None, sendCallback, data);
}
private void sendCallback(IAsyncResult ar)
{
try
{
int sent = socket.EndSend(ar); ( errrors here )
// check if data was attached.
if (ar.AsyncState != null)
{
byte[] buffer = (byte[])ar.AsyncState;
socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, sendCallback, null);
return;
}
if (OnSend != null)
{
OnSend(this, sent);
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.ToString());
return;
}
}
You can also use the BeginSend overload which takes a list of buffers.

Categories