I want to receive message from websocket (broker's websocket to get live stock data) after I send some message to server. I created dummy websocket server in Nodejs by which I can receive message from server but when I try to use client's websocket, it is not working.
Please note : I am able to connect to client's websocket.
Below are methods which I have used to connect, send and receive
Connect method
public static async Task Connect(string uri)
{
ClientWebSocket webSocket = null;
try
{
webSocket = new ClientWebSocket();
webSocket.Options.SetRequestHeader("x-session-token", "efcb0a89db75d4faa147035461224182");
await webSocket.ConnectAsync(new Uri(uri), CancellationToken.None);
await Task.WhenAll(Send(webSocket), Receive(webSocket));
}
catch (Exception ex)
{
Console.WriteLine("Exception: {0}", ex);
}
finally
{
if (webSocket != null)
webSocket.Dispose();
Console.WriteLine();
lock (consoleLock)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("WebSocket closed.");
Console.ResetColor();
}
}
}
For send, I want to send JSON data to server so that it will return me result accordingly. below is Send method
private static async Task Send(ClientWebSocket webSocket)
{
while (webSocket.State == WebSocketState.Open)
{
Symbol[] sym =
{
new Symbol
{
symbol = "1330_NSE"
}
};
RequestBody requestBody = new RequestBody()
{
request = new Request
{
streaming_type = "quote",
data = new Data()
{
symbols = sym
},
request_type = "subscribe",
response_format = "json"
}
};
var json = JsonConvert.SerializeObject(requestBody);
var sendBuffer = new ArraySegment<Byte>(Encoding.UTF8.GetBytes(json));
await webSocket.SendAsync(sendBuffer, WebSocketMessageType.Text, true, CancellationToken.None);
await Task.Delay(delay);
}
}
Below is receive method
private static async Task Receive(ClientWebSocket webSocket)
{
byte[] buffer = new byte[receiveChunkSize];
while (webSocket.State == WebSocketState.Open)
{
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}
else
{
string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine(message);
}
}
}
when I try to run it comes up to websocket.ReceiveAsync function and does nothing after it
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
I think, it is not waiting to receive messages from server?
Also I want to know if there is any way by which I can confirm that message I send from send method get sent to server or not ?
i developed a guessing game in c# using websockets and https calls. Each time the user is creating an instance of the console application to play, it connects with the server. (await webSocket.ConnectAsync(new Uri("ws://localhost:xxx"), CancellationToken.None);).
I have a websocketmiddlewear class to accept the websocket reqeust and a manager class where i handle the websockets requests. Everything works fine except when the user exit the console game. For example when i press "x" from the console (cmd) i get the following error
The remote party closed the WebSocket connection without completing
the close handshake.
on the following line
WebSocketReceiveResult result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
Why the code is not handling the error and how i can fix it?
ConnectionManagerService.cs
public async Task Receive(WebSocket socket)
{
var buffer = new byte[1024 * 4];
while (socket.State == WebSocketState.Open)
{
WebSocketReceiveResult result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text)
{
Console.WriteLine($"Receive->Text");
return;
}
else if (result.MessageType == WebSocketMessageType.Close)
{
string id = _sockets.FirstOrDefault(s => s.Value == socket).Key;
//Console.WriteLine($"Receive->Close on: " + id);
_sockets.TryRemove(id, out socket);
Console.WriteLine("Managed Connections: " + _sockets.Count.ToString());
await socket.CloseOutputAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
return;
}
}
}
WebsocketMiddleware.cs
}
public async Task Invoke(HttpContext context)
{
var isSocketRequest = context.WebSockets.IsWebSocketRequest;
if (isSocketRequest)
{
WebSocket socket = await context.WebSockets.AcceptWebSocketAsync();
_manager.AddSocket(socket);
await _manager.Receive(socket);
}
else
{
await _next(context);
}
}
It happens because you are treating only the graceful connection disconnect.The client might just disconnect and you will get big ol SocketIO Exception in the middle of a read/write operation.
Consider wrapping your Socket operation(s) in a try-catch and deal with them as you please.
public async Task Receive(WebSocket socket)
{
var buffer = new byte[1024 * 4];
try{
while (true)
{
WebSocketReceiveResult result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text)
{
Console.WriteLine($"Receive->Text");
return;
}
else if (result.MessageType == WebSocketMessageType.Close)
{
string id = _sockets.FirstOrDefault(s => s.Value == socket).Key;
//Console.WriteLine($"Receive->Close on: " + id);
_sockets.TryRemove(id, out socket);
Console.WriteLine("Managed Connections: " + _sockets.Count.ToString());
await socket.CloseOutputAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
return;
}
}
} catch(Exception ex){ //not so graceful disconnect
_sockets.TryRemove(id, out socket);
//deal with closed connection
//maybe reconnect ?
}
}
I'm attempting to write a simple async https proxy server in c#.
I would like to know how I should detect/handle when the request is complete, and how to exit my bActive loop, assuming a loop like this is appropriate.
Would really appreciate some pointers on if my approach is correct and what I could do to improve the logic.
The issue I seem to be running into is that the time it takes for an endpoint to respond along with the network delay means I DataAvailable doenst always have data but there may still be some sending. Requiring a sleep and another attmempt which in turn causes the long completion time in requests.
Listen for TCP connection
Extract CONNECT header and open a connection to the requested server
Copy the requestStream to proxyStream
Copy the proxyStream to the requestStream
Sleep waiting for data and repeat 3 - 4 until no data is avaiable on both streams. Then break out of the loop and close connection.
public async Task Start()
{
listener.Start();
while (listen)
{
if (listener.Pending())
{
HandleClient(await listener.AcceptTcpClientAsync());
}
else
{
await Task.Delay(100); //<--- timeout
}
}
}
private static async Task HandleClient(TcpClient clt)
{
var bytes = new byte[clt.ReceiveBufferSize];
var hostHeaderAvailable = 0;
NetworkStream requestStream = null;
int count;
const string connectText = "connect";
const string hostText = "Host: ";
bool bActive = true;
List<Task> tasks = new List<Task>();
try
{
using (NetworkStream proxyStream = clt.GetStream())
using (TcpClient requestClient = new TcpClient())
{
proxyStream.ReadTimeout = 100;
proxyStream.WriteTimeout = 100;
while (bActive)
{
if (proxyStream.DataAvailable && hostHeaderAvailable == 0)
{
count = await proxyStream.ReadAsync(bytes, 0, bytes.Length);
var text = Encoding.UTF8.GetString(bytes);
Console.WriteLine(text);
if (text.ToLower().StartsWith(connectText))
{
// extract the url and port
var host = text.Remove(0, connectText.Length + 1);
var hostIndex = host.IndexOf(" ", StringComparison.Ordinal);
var hostEntry = host.Remove(hostIndex).Split(new[] { ":" }, StringSplitOptions.None);
// connect to the url and prot supplied
await requestClient.ConnectAsync(hostEntry[0], Convert.ToInt32(hostEntry[1]));
requestStream = requestClient.GetStream();
requestStream.ReadTimeout = 100;
requestStream.WriteTimeout = 100;
// send 200 response to proxyStream
const string sslResponse = "HTTP/1.0 200 Connection established\r\n\r\n";
var sslResponseBytes = Encoding.UTF8.GetBytes(sslResponse);
await proxyStream.WriteAsync(sslResponseBytes, 0, sslResponseBytes.Length);
// delay here seems to prevent the following proxyStream.read from failing as data is not yet avaiable
// without it the loop runs and has to timeout before running again
await Task.Delay(1);
}
}
hostHeaderAvailable++;
if (requestStream == null || !requestClient.Connected || !clt.Connected)
{
bActive = false;
break;
}
Console.WriteLine(proxyStream.DataAvailable || requestStream.DataAvailable);
if (proxyStream.DataAvailable || requestStream.DataAvailable)
{
Task task = proxyStream.CopyToAsync(requestStream);
Task task2 = requestStream.CopyToAsync(proxyStream);
tasks.Add(task);
tasks.Add(task2);
await Task.WhenAll(tasks).ConfigureAwait(false);
bActive = false;
break;
}
await Task.Delay(10);
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
clt.Close();
}
An older attempt that used ReadAsync/WriteAsync too longer to response and still had the timeout issue.
Listen for TCP connection
Extract CONNECT header and open a connection to the requested server
Read data from requestStream and copy to proxyStream
Wait checking if data is avaiable on either stream
If data avaiable read from proxyStream and write to requestStream
If data avaiable read from requestStream and write to proxyStream
Sleep waiting for data and repeat 5 - 6 until no data is avaiable on eitboth streams. Then break out of the loop and close connection.
private static TcpListener listener = new TcpListener(IPAddress.Parse("192.168.0.25"), 13000);
private static bool listen = true;
public async Task Start()
{
listener.Start();
while (listen)
{
if (listener.Pending())
{
await HandleClient(await listener.AcceptTcpClientAsync());
}
else
{
await Task.Delay(100);
}
}
}
private static async Task HandleClient(TcpClient clt)
{
var bytes = new byte[clt.ReceiveBufferSize];
var hostHeaderAvailable = 0;
NetworkStream requestStream = null;
int count;
const string connectText = "connect";
const string hostText = "Host: ";
bool bActive = true;
try
{
using (NetworkStream proxyStream = clt.GetStream())
using (TcpClient requestClient = new TcpClient())
{
while (bActive)
{
while (proxyStream.DataAvailable)
{
// handle connect
if (hostHeaderAvailable == 0)
{
count = await proxyStream.ReadAsync(bytes, 0, bytes.Length);
var text = Encoding.UTF8.GetString(bytes);
Console.WriteLine(text);
if (text.ToLower().StartsWith(connectText))
{
// extract the url and port
var host = text.Remove(0, connectText.Length + 1);
var hostIndex = host.IndexOf(" ", StringComparison.Ordinal);
var hostEntry = host.Remove(hostIndex).Split(new[] { ":" }, StringSplitOptions.None);
// connect to the url and prot supplied
await requestClient.ConnectAsync(hostEntry[0], Convert.ToInt32(hostEntry[1]));
requestStream = requestClient.GetStream();
// send 200 response to proxyStream
const string sslResponse = "HTTP/1.0 200 Connection established\r\n\r\n";
var sslResponseBytes = Encoding.UTF8.GetBytes(sslResponse);
await proxyStream.WriteAsync(sslResponseBytes, 0, sslResponseBytes.Length);
// delay here seems to prevent the following proxyStream.read from failing as data is not yet avaiable
// without it the loop runs and has to timeout before running again
await Task.Delay(20);
}
}
hostHeaderAvailable++;
if (requestClient.Connected && hostHeaderAvailable > 1)
{
count = await proxyStream.ReadAsync(bytes, 0, bytes.Length);
await requestStream.WriteAsync(bytes, 0, count);
}
}
while (requestStream.DataAvailable)
{
count = await requestStream.ReadAsync(bytes, 0, bytes.Length);
await proxyStream.WriteAsync(bytes, 0, count);
}
// attempt to detect a timeout / end of data avaiable
var timeout = 0;
while (!proxyStream.DataAvailable && !requestStream.DataAvailable)
{
if (timeout > 5)
{
bActive = false;
break;
}
await Task.Delay(10);
timeout++;
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
UPDATE
As per AgentFire's answer I have now come to the following working code:
public static async Task HandleDisconnect(TcpClient tcp, TcpClient tcp2, CancellationToken cancellationToken)
{
while (true)
{
if (tcp.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (tcp.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Client disconnected
Console.WriteLine("The requesting client has dropped its connection.");
cancellationToken = new CancellationToken(true);
break;
}
}
if (tcp2.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (tcp2.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Server disconnected
Console.WriteLine("The destination client has dropped its connection.");
cancellationToken = new CancellationToken(true);
break;
}
}
await Task.Delay(1);
}
}
private static async Task HandleClient(TcpClient clt)
{
List<Task> tasks = new List<Task>();
var bytes = new byte[clt.ReceiveBufferSize];
var hostHeaderAvailable = 0;
NetworkStream requestStream = null;
const string connectText = "connect";
try
{
using (NetworkStream proxyStream = clt.GetStream())
using (TcpClient requestClient = new TcpClient())
{
proxyStream.ReadTimeout = 100;
proxyStream.WriteTimeout = 100;
if (proxyStream.DataAvailable && hostHeaderAvailable == 0)
{
await proxyStream.ReadAsync(bytes, 0, bytes.Length);
var text = Encoding.UTF8.GetString(bytes);
Console.WriteLine(text);
if (text.ToLower().StartsWith(connectText))
{
// extract the url and port
var host = text.Remove(0, connectText.Length + 1);
var hostIndex = host.IndexOf(" ", StringComparison.Ordinal);
var hostEntry = host.Remove(hostIndex).Split(new[] { ":" }, StringSplitOptions.None);
// connect to the url and prot supplied
await requestClient.ConnectAsync(hostEntry[0], Convert.ToInt32(hostEntry[1]));
requestStream = requestClient.GetStream();
requestStream.ReadTimeout = 100;
requestStream.WriteTimeout = 100;
// send 200 response to proxyStream
const string sslResponse = "HTTP/1.0 200 Connection established\r\n\r\n";
var sslResponseBytes = Encoding.UTF8.GetBytes(sslResponse);
await proxyStream.WriteAsync(sslResponseBytes, 0, sslResponseBytes.Length);
}
}
hostHeaderAvailable++;
CancellationToken cancellationToken = new CancellationToken(false);
Task task = proxyStream.CopyToAsync(requestStream, cancellationToken);
Task task2 = requestStream.CopyToAsync(proxyStream, cancellationToken);
Task handleConnection = HandleDisconnect(clt, requestClient, cancellationToken);
tasks.Add(task);
tasks.Add(task2);
tasks.Add(handleConnection);
await Task.WhenAll(tasks).ConfigureAwait(false);
// close conenctions
clt.Close();
clt.Dispose();
requestClient.Close();
requestClient.Dispose();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
UPDATE
Attempt at using CancellationTokenSource
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken cancellationToken = source.Token;
TaskFactory factory = new TaskFactory(cancellationToken);
tasks.Add(factory.StartNew(() => {proxyStream.CopyToAsync(requestStream);}, cancellationToken));
tasks.Add(factory.StartNew(() => {requestStream.CopyToAsync(proxyStream);}, cancellationToken));
tasks.Add(factory.StartNew(async () => {
//wait for this to retur, then cancel the token
await HandleDisconnect(clt, requestClient);
source.Cancel();
}, cancellationToken));
try
{
await factory.ContinueWhenAll(tasks.ToArray(),
(results) =>
{
Console.WriteLine("Tasks complete");
}, cancellationToken);
}
catch (AggregateException ae)
{
foreach (Exception e in ae.InnerExceptions)
{
if (e is TaskCanceledException)
Console.WriteLine("Unable to compute mean: {0}",
((TaskCanceledException)e).Message);
else
Console.WriteLine("Exception: " + e.GetType().Name);
}
}
finally
{
source.Dispose();
}
UPDATE
public static class extensionTcpClient{
public static bool CheckIfDisconnected(this TcpClient tcp)
{
if (tcp.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (tcp.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Client disconnected
return false;
}
}
return true;
}
}
class ProxyMaintainer
{
private static TcpListener listener = new TcpListener(IPAddress.Parse("192.168.0.25"), 13000);
public ProxyMaintainer()
{
}
public async Task Start()
{
Console.WriteLine("###############################");
Console.WriteLine("Listening on 192.168.0.25:13000");
Console.WriteLine("###############################\n");
listener.Start();
while (listen)
{
if (listener.Pending())
{
HandleClient(await listener.AcceptTcpClientAsync());
}
else
{
await Task.Delay(100); //<--- timeout
}
}
}
private static async Task Transport(NetworkStream from, NetworkStream to, Func<bool> isAlivePoller, CancellationToken token)
{
byte[] buffer = new byte[4096];
while (isAlivePoller())
{
while (from.DataAvailable)
{
int read = await from.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false);
await to.WriteAsync(buffer, 0, read, token);
}
// Relieve the CPU a bit.
await Task.Delay(10, token).ConfigureAwait(false);
}
}
private static async Task HandleClient(TcpClient clientFrom)
{
var hostHeaderAvailable = 0;
int count;
var bytes = new byte[clientFrom.ReceiveBufferSize];
const string connectText = "connect";
NetworkStream toStream = null;
using (var fromStream = clientFrom.GetStream())
using(TcpClient clientTo = new TcpClient())
using (var manualStopper = new CancellationTokenSource())
{
count = await fromStream.ReadAsync(bytes, 0, bytes.Length);
var text = Encoding.UTF8.GetString(bytes);
Console.WriteLine(text);
if (text.ToLower().StartsWith(connectText))
{
// extract the url and port
var host = text.Remove(0, connectText.Length + 1);
var hostIndex = host.IndexOf(" ", StringComparison.Ordinal);
var hostEntry = host.Remove(hostIndex).Split(new[] { ":" }, StringSplitOptions.None);
// connect to the url and prot supplied
await clientTo.ConnectAsync(hostEntry[0], Convert.ToInt32(hostEntry[1]));
toStream = clientTo.GetStream();
// send 200 response to proxyStream
const string sslResponse = "HTTP/1.0 200 Connection established\r\n\r\n";
var sslResponseBytes = Encoding.UTF8.GetBytes(sslResponse);
await fromStream.WriteAsync(sslResponseBytes, 0, sslResponseBytes.Length);
}
bool Poller() => clientFrom.CheckIfDisconnected() && clientTo.CheckIfDisconnected();
Task one = Transport(fromStream, toStream, Poller, manualStopper.Token);
Task two = Transport(toStream, fromStream, Poller, manualStopper.Token);
await Task.WhenAll(one, two).ConfigureAwait(false);
//await one; await two; // To get exceptions if you want them and there are any.
// Alternatively, you can use Task.WhenAll to get exceptions aggregated for you.
}
Console.WriteLine("Closing connection");
}
}
Well, tell you what. The data availability, when it comes to HTTP, lies only in one parameter (if we omit things like WebSocket), which is called Connection and is passed as a Header as a one of two possible states: Close or Keep-Alive.
If Close is chosen by the client, the server is obliged to close the conection as soon as the request is served, whereas Keep-Alive tells the server that, if it doesn't want to, it may leave connection open for another request.
Let's consider both cases.
If client chooses Keep-Alive, the connection will persist and work as intended, indefinetely. But:
If either side drops the connection, there is an easy way to detect that. This piece of code was found on StackOverflow and it was told that it still works perfectly:
public static bool CheckIfDisconnected(this TcpClient tcp)
{
if (tcp.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (tcp.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Client disconnected
return true;
}
}
return false;
}
So I believe that you, as a proxy-server, are not obliged to manage connection states at all and can leave it to the actual communication parties. All you have to do is to detect when either of your connections - proxy or request - is dropped, drop the other one and call it a day.
P.S. Now, you also asked about asynchronicity.
I must add that TCP connections are considered full-duplex. which means you are free to create two async-running tasks, both reading and writing to their own sinks. My thoughts, it would be the optimal course of action.
To answer your other question
You are still using Stream.CopyToAsync which, as I have told you, is not going to succeed as long as any communicating party decides to wait a bit before sending another chunk of data.
You are also somewhat overcomplicating your solution.
I would put it this way:
async Task Transport(NetworkStream from, NetworkStream to, Func<bool> isAlivePoller, CancellationToken token)
{
byte[] buffer = new byte[4096];
while (isAlivePoller())
{
while (from.DataAvailable)
{
int read = await from.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false);
await to.WriteAsync(buffer, 0, read, token).ConfigureAwait(false);
}
// Relieve the CPU a bit.
await Task.Delay(100, token).ConfigureAwait(false);
}
}
And then in your main code:
using TcpClient clientFrom = ...;
using TcpClient clientTo = ...;
using var fromStream = clientFrom.GetStream();
using var toStream = clientTo.GetStream();
using var manualStopper = new CancellationTokenSource();
bool Poller() => clientFrom.CheckIfDisconnected() && clientTo.CheckIfDisconnected();
Task one = Transport(fromStream, toStream, Poller, stopper.Token);
Task two = Transport(toStream, fromStream, Poller, stopper.Token);
await Task.WhenAny(one, two).ConfigureAwait(false);
//await one; await two; // To get exceptions if you want them and there are any.
// Alternatively, you can use Task.WhenAll to get exceptions aggregated for you.
And you are pretty much done here.
I use the websocket API for speech recognition.
I send audio to the server and receive a response.
With this websocket API, it is possible to obtain voice recognition results while streaming voice data.
I can get the final recognition result, but I can not get the recognition result on the way.
The following shows the referenced code.
https://gist.github.com/arjun-g/75961830d363cc9265e4ac2ca095168b
After various trials, I found that line:85 was a problem.
This part seems to be waiting for the final result.
I may not have received the result on the way at all.
Please tell me why I can't get result on the way.
private async Task Connect(ClientWebSocket clientWebSocket)
{
CancellationTokenSource cts = new CancellationTokenSource();
clientWebSocket.Options.SetRequestHeader("Authorization", $"Bearer {this.accessToken}");
Uri connection = new Uri($"wss://hogehoge");
await clientWebSocket.ConnectAsync(connection, cts.Token);
}
The above is the part of the connect method.
Here is my whole code.
private async Task Connect(ClientWebSocket clientWebSocket)
{
CancellationTokenSource cts = new CancellationTokenSource();
clientWebSocket.Options.SetRequestHeader("Authorization", $"Bearer {this.accessToken}");
Uri connection = new Uri($"wss://hogehoge");
await clientWebSocket.ConnectAsync(connection, cts.Token);
}
static async Task SendAudio(ClientWebSocket ws)
{
ArraySegment<byte> closingMessage = new ArraySegment<byte>(Encoding.UTF8.GetBytes(
"{\"command\": \"recog-break\"}"
));
using (FileStream fs = File.OpenRead("audio.raw"))
{
byte[] b = new byte[3200];
while (fs.Read(b, 0, b.Length) > 0)
{
await ws.SendAsync(new ArraySegment<byte>(b), WebSocketMessageType.Binary, true, CancellationToken.None);
}
await ws.SendAsync(closingMessage, WebSocketMessageType.Text, true, CancellationToken.None);
}
}
// prints results until the connection closes or a delimeterMessage is recieved
private async Task HandleResults(ClientWebSocket ws)
{
var buffer = new byte[60000];
while (true)
{
var segment = new ArraySegment<byte>(buffer);
var result = await ws.ReceiveAsync(segment, CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
return;
}
int count = result.Count;
while (!result.EndOfMessage)
{
if (count >= buffer.Length)
{
await ws.CloseOutputAsync(WebSocketCloseStatus.InvalidPayloadData, "That's too long", CancellationToken.None);
return;
}
segment = new ArraySegment<byte>(buffer, count, buffer.Length - count);
result = await ws.ReceiveAsync(segment, CancellationToken.None);
count += result.Count;
}
var message = Encoding.UTF8.GetString(buffer, 0, count);
this.textBox2.AppendText(message);
}
}
private async void button1_Click_1(object sender, EventArgs e)
{
ClientWebSocket cws = new ClientWebSocket();
await this.Connect(cws);
await Task.WhenAll(SendAudio(cws), HandleResults(cws));
}
I have async tcp server that can accept multiple clients at the same time. The scenarios where client requests data from server are served well. Now I am trying to implement the situation in which server has to find a particular client and send some data to it i.e. the client is connected but has not requested data but server wants to send some data to it. How can I find the thread that is already running between the server and client and place data on it?
Here is server code:
public async void RunServerAsync()
{
tcpListener.Start();
while (true)
{
try
{
var client = await tcpListener.AcceptTcpClientAsync();
Accept(client);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
private async void Accept(TcpClient client)
{
//get client information
String clientEndPoint = client.Client.RemoteEndPoint.ToString();
Console.WriteLine("Client connected at " + clientEndPoint );
await Task.Yield ();
try
{
using (client)
using (NetworkStream stream = client.GetStream())
{
byte[] dataReceived = new byte[100];
while (true) //read input stream
{
try
{
int x = await stream.ReadAsync(dataReceived, 0, dataReceived.Length);
if (x != 0)
{
//pass on data for processing
byte[] dataToSend = await ProcessData(dataReceived);
//send response,if any, to the client
if (dataToSend != null)
{
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
ConsoleMessages.DisplayDataSent(dataReceived, dataToSend);
}
}
}
catch (ObjectDisposedException)
{
stream.Close();
}
}
}
} //end try
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}//end Accept(TcpClient client)
You need to keep track of your clients. For example:
ConcurrentDictionary<Guid, TcpClient> _clients = new ConcurrentDictionary<Guid, TcpClient>();
public async void RunServerAsync()
{
tcpListener.Start();
while (true)
{
try
{
var client = await tcpListener.AcceptTcpClientAsync();
var clientId = Guid.NewGuid();
Accept(clientId, client);
_clients.TryAdd(clientId, client);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
public Task SendAsync(Guid clientId, Byte[] buffer, Int32 offset, Int32 count)
{
TcpClient client;
if (_clients.TryGetValue(clientId, out client))
return client.GetStream().WriteAsync(buffer, offset, count);
// client disconnected, throw exception or just ignore
return Task.FromResult<Object>(null);
}
public Boolean TryGetClient(Guid clientId, out TcpClient client)
{
return _clients.TryGetValue(clientId, out client);
}
private async void Accept(Guid clientId, TcpClient client)
{
//get client information
String clientEndPoint = client.Client.RemoteEndPoint.ToString();
Console.WriteLine("Client connected at " + clientEndPoint);
await Task.Yield();
try
{
using (client)
using (NetworkStream stream = client.GetStream())
{
byte[] dataReceived = new byte[100];
while (true) //read input stream
{
try
{
int x = await stream.ReadAsync(dataReceived, 0, dataReceived.Length);
if (x != 0)
{
//pass on data for processing
byte[] dataToSend = await ProcessData(dataReceived);
//send response,if any, to the client
if (dataToSend != null)
{
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
ConsoleMessages.DisplayDataSent(dataReceived, dataToSend);
}
}
}
catch (ObjectDisposedException)
{
stream.Close();
}
}
}
} //end try
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// deregister client
_clients.TryRemove(clientId, out client);
}
}//end Accept(TcpClient client)
TCP is bi-directional, so either side can send data whenever they want.
What you need to do is store a reference to the TcpClient on Accept, something like:
//Class Level
List<TcpClient> connectedClients = List<TcpClient>();
private async void Accept(TcpClient client)
{
connectedClients.Add(client);
...
}
public SendMessage(int index, String message)
{
//pseudo-code
connectedClients[index].Send(message);
}
Normally I will raise an event on connect so whoever is using the class can get the new client's index. The send message code is pseudo-code, there are good examples on MSDN of how to actually perform the send.