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);
Related
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 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.
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.
Hi stack overflow members.
I'm struggling with some simple code but I can't get it done.
I have this asynchronous server which waits for connections.
while (clientSocket.Connected)
{
try
{
clientSocket.BeginReceive(so.buffer, 0, 200, SocketFlags.None
, new AsyncCallback(wait),so);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
where so(shorted StateObject) it's my class:
internal class StateObject
{
public TcpClient client;
public byte[] buffer;
public StateObject()
{
buffer = new byte[200];
client = new TcpClient();
}
}
I use this class to put out the information on the callback function. However I get the system lacked sufficient buffer space or because a queue was full.
I posted a short piece from the actual program.
One interesting issue, is that if I write:
while (clientSocket.Connected)
{
try
{
byte[] buffer = new byte[200];
clientSocket.BeginReceive(buffer, 0, 200, SocketFlags.None
, new AsyncCallback(wait),so);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
it will work, but I will not be able to pull out the buffer from the asynchronous function(wait).
I'm struggling with this and I can't find answers.
The while loop shouldn't be there, just call beginreceive after the endreceive.
This is a poor example, but may give you some ideas:
public class AsyncTCP
{
public void StartReceive()
{
byte[] buffer = new byte[200];
clientSocket.BeginReceive(buffer, 0, 200, SocketFlags.None, (state) =>
{
int bytesReceived = clientSocket.EndReceive(state);
// handle buffer.
if(bytesReceived != 0)
StartReceive();
} ,so);
}
}
If it's about getting the state within the EndReceive handler:
private void StartReceive()
{
StateObject myState = new StateObject();
myState.buffer = new byte[200];
myState.client = _client; // or whatever
myState.client.BeginReceive(so.buffer, 0, 200, SocketFlags.None, new AsyncCallback(wait),myState);
}
private void EndReceive(IAsyncResult result)
{
StateObject myState = (StateObject)result.State;
int bytesReceived = myState.client.EndReceive(result);
// handle myState.buffer
StartReceive();
}
I think there are better ways to do this, like:
- only constructing a receive buffer ones.
- put some packet header/data with lengths in it.
Good luck
How about using some TPL functions. So your code can be simplified a lot
int readBytes = await s.ReceiveTaskAsync(buffer, 0, buffer.Length);
This is the extension method ReceiveTaskAsync
public static class SocketExtensions
{
public static Task<int> ReceiveTaskAsync(this Socket socket, byte[] buffer, int offset, int count)
{
return Task.Factory.FromAsync<int>(
socket.BeginReceive(buffer, offset, count, SocketFlags.None, null, socket),
socket.EndReceive);
}
}
The problem is here
while (clientSocket.Connected)//checks connected
{
try
{
clientSocket.BeginReceive(so.buffer, 0, 200, SocketFlags.None, new AsyncCallback(wait),so);//says begin receive and continues to do endlessly
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
You've to call BeginReceive again only after you received the data.
Here's and example from msdn how to do that.
I resolved my previous problem(I just needed to remove the while loop, however I messed it up with some code from a synchronous server).
Now I have another problem, so I will use this same thread.
From what I've searched, and understood, you can open an asynchronous server with BecinAccept like this(main method):
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 8082);
TcpListener tcpListener = new TcpListener(IPAddress.Any, 8082);
server = tcpListener.Server;
server.Bind(ipEndPoint);
server.Listen(4);
server.BeginAccept(new AsyncCallback(beginConnection), server);
Then:
static void beginConnection(IAsyncResult iar)
{
Console.WriteLine("Client connected");
Socket s = (Socket)iar.AsyncState;
server = s.EndAccept(iar);
server.Listen(4); // this line was initially absent
server.BeginAccept(beginConnection, s);
}
I want to be able to connect to this server multiple clients.
However, when I try to do this, only the first client connects itself.
The client it's a simple socket, which just echoes back from the server, what we read from the console.
I thought that since in the main method I've called server.Listen(4), I will be able to connect 4 clients.
Then I thought of calling recursively BeginAccept in the beginConnection method.
And last, I received the error, that I must call first the Listen method, so I added that too.
Still no luck.
quite new to c# and tasks... Trying to find the correct syntax to wrap UDPclient into FromAsync(...), seems I miss some parameters...(can't use ReceiveAsync as it should work with
.NET 4.0)
public Task<byte[]> GetUDPmessageAsync()
{
byte[] data = new byte[100];
myUdpClient = new UdpClient(12000);
Task<byte[]> task = Task<byte[]>.Factory.FromAsync(myUdpClient.BeginReceive, myUdpClient.EndReceive,?,?);
}
I don't think you can use FromAsync since UdpClient.EndReceive take a ref parameter, which makes it incompatible with all the overloads of FromAsync. You can make your own extension method though:
public static Task<byte[]> ReceiveAsync(this UdpClient client, IPEndPoint endpoint)
{
TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>();
AsyncCallback callback = ar => {
try
{
byte[] bytes = client.EndReceive(ar, ref endpoint);
tcs.SetResult(bytes);
}
catch(Exception ex)
{
tcs.SetException(ex);
}
};
client.BeginReceive(callback, null);
return tcs.Task;
}