I have a scenario where multiple threads are sending data over a single socket. A unique ID has been inserted into the message and the unique ID is echoed back in the response message. Everything works great when the socket is isolated to a single client (as expected). Now I'm looking for an async / await pattern for multiple threads where the client waits for a specific response.
Some code to demonstrate:
using (var ns = new NetworkStream(_socket))
{
byte[] requestBuffer = GetBuffer(request);
await ns.WriteAsync(requestBuffer, 0, request.Length);
byte[] responseBuffer = await ReceiveMessageAsync(ns);
// process response message
}
The above example does not work in a multithreaded scenario because messages can come back in any order, so the next message off the wire may not belong to the current client. My thought was that the client would register a delegate or Task using its unique ID (storing that in a Dictionary) and when a message came back with that unique ID, the delegate or task would be 'completed' with the response bytes. I'm guessing this would be fairly easy to implement with an EventHandler but I'm looking for a way to await the response.
For example:
using (var ns = new CustomNetworkStream(_socket))
{
Task waitTask = ns.RegisterTask(this.UniqueId);
byte[] requestBuffer = GetBuffer(request);
await ns.WriteAsync(requestBuffer, 0, request.Length);
byte[] responseBuffer = await waitTask;
// process response message
}
I don't know what the "RegisterTask" method would look like, how to store the task, how to make the task 'wait' and later 'complete' it with Task as the Result.
Any ideas? I've researched Toub's Async Coordination Primitives series but I'm not certain if that's the right approach or not.
So you will want to wrap all of this into a new class, because you're going to need to share state between the places that you read and the places that you write.
Every time you go and write to the stream you'll need to accept the unique ID, and add an entry into a lookup that maps the id to a TaskCompletionSource. The write method can then return the Task from that TCS.
You can then have a separate reader that just sits there reading from your stream, finds the dictionary entry associated with the ID of that response, and sets its result.
public class MyNetworkStream : IDisposable
{
private NetworkStream stream;
private ConcurrentDictionary<int, TaskCompletionSource<byte[]>> lookup =
new ConcurrentDictionary<int, TaskCompletionSource<byte[]>>();
private CancellationTokenSource disposalCTS = new CancellationTokenSource();
public MyNetworkStream(Socket socket)
{
stream = new NetworkStream(socket);
KeepReading();
}
public void Dispose()
{
disposalCTS.Cancel();
stream.Dispose();
}
public Task<byte[]> WriteAndWaitAsync(byte[] buffer, int offset,
int count, int uniqueID)
{
var tcs = lookup.GetOrAdd(uniqueID, new TaskCompletionSource<byte[]>());
stream.WriteAsync(buffer, offset, count);
return tcs.Task;
}
private async void KeepReading()
{
try
{
//TODO figure out what you want for a buffer size so that you can
//read a block of the appropriate size.
byte[] buffer = null;
while (!disposalCTS.IsCancellationRequested)
{
//TODO edit offset and count appropriately
await stream.ReadAsync(buffer, 0, 0, disposalCTS.Token);
int id = GetUniqueIdFromBlock(buffer);
TaskCompletionSource<byte[]> tcs;
if (lookup.TryRemove(id, out tcs))
tcs.TrySetResult(buffer);
else
{
//TODO figure out what to do here
}
}
}
catch (Exception e)
{
foreach (var tcs in lookup.Values)
tcs.TrySetException(e);
Dispose();
//TODO consider any other necessary cleanup
}
}
private int GetUniqueIdFromBlock(byte[] buffer)
{
throw new NotImplementedException();
}
}
What you need is a TaskCompletionSource<byte[]> to use as a synchronization construct, a ConcurrentDictionary to map between an id and a TCS and a listener:
ConcurrentDictionary<UniqueId, TaskCompletionSource<byte[]>> _dictionary;
async Task Listen(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
using (var ns = new NetworkStream(_socket))
{
byte[] responseBuffer = await ReceiveMessageAsync(ns);
var id = ExtractId(responseBuffer);
TaskCompletionSource<byte[]> tcs;
if (dictionary.TryRemove(id, out tcs))
{
tcs.SetResult(responseBuffer);
}
else
{
// error
}
}
}
}
Task RegisterTask(UniqueId id)
{
var tcs = new TaskCompletionSource<byte[]>();
if (!_dictionary.TryAdd(id, tcs))
{
// error
}
return tcs.Task;
}
However, as Stephen Cleary suggested, you probably want to use an existing solution for that.
Related
Currently I try to read and write Async to/from a network stream. My software is the Client part and the server can send informations on its own or respond to commands I send him.
So I need a socket which
reads all the time (in case the server sends status informations)
stops reading when I want to send commands (commands can be sequences of data with multible Write and Read operations)
So I thought it would be a good approach to create a Semaphore and a Background Task which handles the server sent messages and in case I want to send a command I block the semaphore and have full access to read/write operations to the socket.
Here is what I do currently.
private TcpClient _tcpClient = new TcpClient();
protected SemaphoreSlim ClientSemaphore { get; } = new SemaphoreSlim(1, 1);
public async Task ConnectAsync()
{
if (_tcpClient.Connected)
{
await DisconnectAsync();
}
await _tcpClient.ConnectAsync(Hostname, RemotePort);
//here the background Task is started
_ = AutoReceiveMessages();
}
private async Task AutoReceiveMessages()
{
while (_tcpClient.Connected)
{
//enter and lock semaphore
await ClientSemaphore.WaitAsync();
try
{
//read from socket until timeout (ms)
var msg = await ReadFromSocket(2000);
foreach (var cmd in SplitMessageInTelegrams(msg))
{
Console.WriteLine("MESSAGE --> " + cmd);
}
}
catch (Exception ex)
{
}
finally
{
//release semaphore
ClientSemaphore.Release();
}
}
}
private async Task<string> ReadFromSocket(double timeout = 0)
{
var buf = new byte[4096];
var stream = _tcpClient.GetStream();
//read from stream or timeout
var amountReadTask = stream.ReadAsync(buf, 0, buf.Length);
var timeoutTask = Task.Delay(TimeSpan.FromMilliseconds(timeout));
await Task.WhenAny(timeoutTask, amountReadTask)
.ConfigureAwait(false);
//timeout
if (!amountReadTask.IsCompleted)
{
throw new TimeoutException("Timeout");
}
//no timeout
return Encoding.ASCII.GetString(buf, 0, amountReadTask.Result);
}
But this do not work as I expected...
I use this methode to send a message to the server and in WireShark I see the server resonds with the same message
protected async Task SendTelegramAsync(ITelegram telegram)
{
await ClientSemaphore.WaitAsync();
try
{
_ = telegram ?? throw new ArgumentException($"{nameof(telegram)}");
if (!_tcpClient.Connected) throw new InvalidOperationException("Socket not connected!");
var buf = new byte[4096];
var stream = _tcpClient.GetStream();
var msg = Encoding.ASCII.GetBytes("\x02" + telegram.GetCommandMessage() + "\x03");
Console.WriteLine("WRITE --> " + msg);
await stream.WriteAsync(msg, 0, msg.Length);
//comment AutoReceiveMessage and remove comment from this
//and I get responses from the server
//var test = await ReadFromSocket(2000);
}
finally
{
ClientSemaphore.Release();
}
}
I know in this case I do not need the semaphore but later I want to create sequences so one command consists of multible writes and reads and as long as the command is executed I do not want to use the AutoReceiveMessages method.
The problem now is
If I use it like this I never get a response the ReadFromSocket method always get the timeout even when wireshark tell me the server has responded
But even better if I disable AutoReceiveMessages (just comment _ = AutoReceiveMessages()) and use ReadFromSocket directly in SendTelegramAsync() everything work as expected.
So I think the problem is something related to the background task and the ReadAsync but I couldnt figure it out...
Got It!
stream.DataAvailable is your friend (or my friend :)).
If I check before the ReadAsync if DataIsAvailable then I have no problem anymore.
if (_tcpClient.GetStream().DataAvailable)
var msg = await ReadFromSocket(DEFAULT_TIMEOUT);
I'm writing a client program in a UWP application, which receive data through a TCP socket from a server which send data periodically to this client.
I'm meeting a problem that I want to receive data through a Task like below, which read data in a loop using the LoadAsync method. As a result, I need to use await before the method and async for this Task. As I create the StreamSocket
and call the Task in another method, the ListenTcpClient Task returns after the LoadAsync() method, and data isn't received and showed in the next steps. Can you tell me how can I receive data from a TCP socket while keep UI thread and other thread working on other things?
Thank you.
public async Task ListenTcpClient(StreamSocket clientTcpSocket)
{
while(clientTcpSocket!=null)
{
using (DataReader reader = new DataReader(clientTcpSocket.InputStream))
{
await reader.LoadAsync(sizeof(int));
int fdToReadRcv = reader.ReadInt32();
byte[] bs = BitConverter.GetBytes(fdToReadRcv);
Array.Reverse(bs);
int fdToRead = BitConverter.ToInt32(bs, 0);
Debug.WriteLine(fdToRead);
StartButton1.Content = fdToRead;
}
}
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
clientTcpSocket = new StreamSocket();
try
{// create the socket
await clientTcpSocket.ConnectAsync(new HostName(ip), tcpPort);
using (DataReader reader = new DataReader(clientTcpSocket.InputStream))
{
await reader.LoadAsync(sizeof(int));
int fdToReadRcv = reader.ReadInt32();
byte[] bs = BitConverter.GetBytes(fdToReadRcv);
Array.Reverse(bs);
int fdToRead = BitConverter.ToInt32(bs,0);
Debug.WriteLine(fdToRead);
}
using (DataWriter writer = new DataWriter(clientTcpSocket.OutputStream))
{
writer.WriteString(String.Format("Client Start"));
await writer.StoreAsync();
}
timer.Start();
ListenTcpClient(clientTcpSocket);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
//then do something else while the client socket is receiving data
}
As long as the Task's are independent from one another, you can run it in parallel by using Task.WhenAll(...)
private async void Button_Click(object sender, RoutedEventArgs e)
{
// generate a create socket task
var createSocketTask = CreateSocket();
// create 'do something else' task
var doSomethingElseTask = DoSomethingElse();
// run both tasks in parallel
await Task.WhenAll(createSocketTask, doSomethingElseTask);
}
private async Task CreateSocket()
{
clientTcpSocket = new StreamSocket();
try
{// create the socket
await clientTcpSocket.ConnectAsync(new HostName(ip), tcpPort);
using (DataReader reader = new DataReader(clientTcpSocket.InputStream))
{
await reader.LoadAsync(sizeof(int));
int fdToReadRcv = reader.ReadInt32();
byte[] bs = BitConverter.GetBytes(fdToReadRcv);
Array.Reverse(bs);
int fdToRead = BitConverter.ToInt32(bs,0);
Debug.WriteLine(fdToRead);
}
using (DataWriter writer = new DataWriter(clientTcpSocket.OutputStream))
{
writer.WriteString(String.Format("Client Start"));
await writer.StoreAsync();
}
timer.Start();
ListenTcpClient(clientTcpSocket);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
private async Task DoSomethingElse()
{
// do something
}
I'm building a TCP/IP connection for my application with a warehouse system. The communication goes like this.
I send a message to the TCP/IP(Socket) server of the warehouse system.
The warehouse system responds with a message the my local TCP/IP server.
So there are no direct response messages. Instead each application as it's own server.
Yet I want my application to wait for the response coming from the other server.
So basicly I have the following code.
public string ControllerFunction() {
startLocalTcpIpServer();
sendMessage("a message");
return clientMessage;
}
This is my own server started with the start() function
public void Start() {
// Start TcpServer background thread
tcpListenerThread = new Thread(new ThreadStart(ListenForIncommingRequests)) {
IsBackground = true
};
tcpListenerThread.Start();
}
private void ListenForIncommingRequests() {
try {
tcpListener = new TcpListener(IPAddress.Parse(serverIp), port);
tcpListener.Start();
byte[] bytes = new byte[1024];
Console.WriteLine("Server Started");
while(true) {
// Get a stream object for reading
using(NetworkStream stream = tcpListener.AcceptTcpClient().GetStream()) {
int length;
// Read incomming stream into byte arrary.
while((length = stream.Read(bytes, 0, bytes.Length)) != 0) {
byte[] incommingData = new byte[length];
Array.Copy(bytes, 0, incommingData, 0, length);
// Convert byte array to string message.
string clientMessage = Encoding.ASCII.GetString(incommingData);
}
}
}
}
catch(SocketException socketException) {
Console.WriteLine("SocketException " + socketException.ToString());
}
}
So I want to use the result string clientMessage again as a return for my ControllerFunction. But how do I get the data there in a proper way?
So what you need is to be able to wait for response coming from another place in your application (local server). Response will be fired there first. Local server should have an event you can subscribe to (OnMessage in my example). This event will forward result message to you.
Synchronization can be handled using TaskCompletionSource. You will create Task that you can use to obtain result synchronously or asynchronously.
Something like this:
public string ControllerFunction()
{
return ControllerFunctionTask().Result;
}
public Task<string> ControllerFunctionTask()
{
sendMessage("a message");
var task = new TaskCompletionSource<string>();
localServer.OnMessage = (message) =>
{
task.SetResult(message);
};
return task.Task;
}
As stated in comments, synchronous waiting for asynchronous Task may lead to deadlocks. This may happen when caller thread is context thread (UI, ASP). Therefore this should be better approach:
public async Task<string> ControllerFunction()
{
return await ControllerFunctionTask();
}
public Task<string> ControllerFunctionTask()
{
sendMessage("a message");
var task = new TaskCompletionSource<string>();
localServer.OnMessage = (message) =>
{
task.SetResult(message);
};
return task.Task;
}
OnMessage can be defined this way:
public event Action<string> OnMessage;
Then it will be called right after line where you get clientMessage string:
string clientMessage = Encoding.ASCII.GetString(incommingData);
if (OnMessage != null)
OnMessage(clientMessage);
I think I need async await for my program but I can't figure out how to do it. From GetData method I need send request to socket, and finally get socket data to same method. Need to pass data somehow from OnReceive to initial GetData method. Is it possible to implement await async tasks here? Here is simplified scheme:
AsyncCallback m_pfnLookupCallback;
Socket m_sock;
public void GetData()
{
string data;
if (condition) data = GetDataFromCache();
else data = GetDataFromNet(); /// !NEED AWAIT FOR SOCKET DATA HERE
//will process data here.
}
public string GetDataFromNet()
{
m_sock.Send(szCommand, iBytesToSend, SocketFlags.None);
WaitForData("s1");
}
public void WaitForData(string sSocketName)
{
m_pfnLookupCallback = new AsyncCallback(OnReceive);
m_sock.BeginReceive(m_szLookupSocketBuffer, 0, m_szLookupSocketBuffer.Length, SocketFlags.None, m_pfnLookupCallback, sSocketName);
}
private void OnReceive(IAsyncResult asyn)
{
int iReceivedBytes = 0;
iReceivedBytes = m_sockLookup.EndReceive(asyn);
string sData = Encoding.ASCII.GetString(m_szLookupSocketBuffer, 0, iReceivedBytes); //WHAT I NEED
}
p.s. I would avoid changing socket work if possible because they are used in other parts of program.
You can turn the old IAsyncResult pattern (APM) into async await quite easily. Here's an example of the socket call:
var byteCount = await Task.Factory.FromAsync(
(callback, s) =>
{
return clientSocket.BeginReceive(
m_szLookupSocketBuffer,
0,
cm_szLookupSocketBuffer.Length,
SocketFlags.None,
callback,
sSocketName);
},
result => clientSocket.EndReceive(result),
null);
I've been making a server. I am using TcpListener.AcceptTcpClientAsync() in an async method, but I have no idea how to actually make it work. My code right now is:
private static async void StartServer()
{
Console.WriteLine("S: Server started on port {0}", WebVars.ServerPort);
var listener = new TcpListener(WebVars.LocalIp, WebVars.ServerPort);
listener.Start();
var client = await listener.AcceptTcpClientAsync();
}
How do I process the client? Do I just continue coding and it will automagically make new threads of the same method or do I need to do some magic method that will do it for me?
Edit: current code:
private static Task HandleClientAsync(TcpClient client)
{
var stream = client.GetStream();
// do stuff
}
/// <summary>
/// Method to be used on seperate thread.
/// </summary>
private static async void RunServerAsync()
{
while (true)
{
Console.WriteLine("S: Server started on port {0}", WebVars.ServerPort);
var listener = new TcpListener(WebVars.LocalIp, WebVars.ServerPort);
listener.Start();
var client = await listener.AcceptTcpClientAsync();
await HandleClientAsync(client);
}
}
// all credit should go to c# 7.0 in a nutshell (Joseph Albahari & Ben Albahari)
async void RunServerAsync()
{
var listner = new TcpListener(IPAddress.Any, 9999);
listner.Start();
try
{
while (true)
await Accept(await listner.AcceptTcpClientAsync());
}
finally { listner.Stop(); }
}
const int packet_length = 2; // user defined packet length
async Task Accept(TcpClient client)
{
await Task.Yield();
try
{
using(client)
using(NetworkStream n = client.GetStream())
{
byte[] data = new byte[packet_length];
int bytesRead = 0;
int chunkSize = 1;
while (bytesRead < data.Length && chunkSize > 0)
bytesRead += chunkSize =
await n.ReadAsync(data, bytesRead, data.Length - bytesRead);
// get data
string str = Encoding.Default.GetString(data);
Console.WriteLine("[server] received : {0}", str);
// To do
// ...
// send the result to client
string send_str = "server_send_test";
byte[] send_data = Encoding.ASCII.GetBytes(send_str);
await n.WriteAsync(send_data, 0, send_data.Length);
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Nothing will magically create dedicated threads for you, although there are some threads used for IO completion which can come into play, particularly if you don't have a synchronization context that you need to return to.
You should decide whether you want your StartServer method to actually complete when it's accepted a single connection, or keep looping until you've been told to shut down.
Either way, you clearly need to decide how to handle the client too. Either you could start a new thread and use synchronous methods, or you could just use asynchronous IO to handle everything in the same thread. For example, to dump the incoming data to a file:
private Task HandleClientAsync(TcpClient client)
{
// Note: this uses a *synchronous* call to create the file; not ideal.
using (var output = File.Create("client.data"))
{
using (var input = client.GetStream())
{
// Could use CopyToAsync... this is just demo code really.
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = await input.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await output.WriteAsync(buffer, 0, bytesRead);
}
}
}
}
(That's assuming the client will just terminate the connection when it's finished writing the data.) Aside from the File.Create call, this is all asynchronous - so there's no need to create a separate thread for it.
This is just an example, of course - real connection handling would usually be more complicated. If your real handling needs anything more compute-intensive, you may well want to consider using Task.Run to use the thread pool... that way it won't interfere with accepting more connections.