I built a websocket server in C#. Relevant code is below. The problem is when the if (result.EndOfMessage) is called, the websocket stops listening for any more messages. If any message comes later on, then that message isnt recieved from the client.
How can I receive the messages after a while from the client?
public async Task Listen(int port)
{
try
{
IPAddress serverAddress = IPAddress.Parse("127.0.0.1"); // localhost
_listener = new TcpListener(serverAddress, port);
_listener.Start();
while (true)
{
TcpClient tcpClient = await _listener.AcceptTcpClientAsync();
await Task.Run(() =>
ProcessTcpClientAsync(tcpClient).ConfigureAwait(false));
}
}
catch (SocketException ex)
{
throw new Exception(message, ex);
}
}
private async Task<string> ProcessTcpClientAsync(TcpClient tcpClient)
{
CancellationTokenSource source = new CancellationTokenSource();
try
{
if (_isDisposed)
{
return string.Empty;
}
// get a secure or insecure stream
NetworkStream stream = tcpClient.GetStream();
WebSocketHttpContext context = await _webSocketServerFactory.ReadHttpHeaderFromStreamAsync(stream);
if (context.IsWebSocketRequest)
{
WebSocket webSocket = await _webSocketServerFactory.AcceptWebSocketAsync(context);
await RespondToWebSocketRequestAsync(webSocket, CancellationToken.None);
}
}
catch (ObjectDisposedException ex)
{
// do nothing. This will be thrown if the Listener has been stopped
}
catch (Exception ex)
{
}
}
public async Task RespondToWebSocketRequestAsync(WebSocket webSocket, CancellationToken token)
{
ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[BUFFER_SIZE]);
while (true)
{
WebSocketReceiveResult result = await webSocket.ReceiveAsync(buffer, token);
if (result.MessageType == WebSocketMessageType.Close)
{
break;
}
if (result.Count > BUFFER_SIZE)
{
await webSocket.CloseAsync(WebSocketCloseStatus.MessageTooBig,
$"Web socket frame cannot exceed buffer size of {BUFFER_SIZE:#,##0} bytes. Send multiple frames instead.",
token);
break;
}
if (result.EndOfMessage)
{
break;
}
// Process data
SomeMethod();
}
}
Related
I have created these applications. One is server socket which consists of main thread responsible for creating client/server connection and creating other threads:
thread for producing messages
thread for listening for received messages from client
thread to send produced messages by server
If there is connection disrupted server frees any resources and waits for new connections in a loop.
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class SynchronousSocketListener {
private Socket handler; //socket handlet
private byte[] bytes; // Data buffer for incoming data.
private List < String > messagesToSend;
private Mutex messagesToSendMutex;
public SynchronousSocketListener() {
messagesToSendMutex = new Mutex();
try {
messagesToSendMutex.WaitOne();
messagesToSend = new List < String > ();
} finally {
messagesToSendMutex.ReleaseMutex();
}
bytes = new Byte[1024];
handler = null;
}
private void addMessageToQueue(string message) {
try {
messagesToSendMutex.WaitOne();
messagesToSend.Add(message);
} finally {
messagesToSendMutex.ReleaseMutex();
}
}
private string readMessageFromQueue() {
string ret = null;
try {
messagesToSendMutex.WaitOne();
if (messagesToSend.Count > 0)
ret = messagesToSend[0];
} finally {
messagesToSendMutex.ReleaseMutex();
}
return ret;
}
private void removeMessageFromQueue(string messageToRemove) {
string ret = null;
try {
messagesToSendMutex.WaitOne();
if (messagesToSend.Count > 0)
messagesToSend.Remove(messageToRemove);
} finally {
messagesToSendMutex.ReleaseMutex();
}
}
private void threadForGeneratingMessages() {
while (true) {
addMessageToQueue(Console.ReadLine());
}
}
private void sendingThread(CancellationToken ct) {
while (true) {
try {
if (ct.IsCancellationRequested) {
// Clean up here, then...
ct.ThrowIfCancellationRequested();
Console.WriteLine("Task sending is cancelled");
return;
}
} catch (System.OperationCanceledException ex) {
Console.WriteLine("System.OperationCanceledException");
return;
}
if (messagesToSend.Count <= 0) {
Console.WriteLine("No more messages to send");
Thread.Sleep(5000);
continue;
}
Console.WriteLine("Server is going to write some data for client");
//send pending messages to client
string dataToSendNow = messagesToSend[0];
byte[] msg = Encoding.ASCII.GetBytes(dataToSendNow + "<EOF>");
try {
// Send the data through the socket.
int bytesSent = handler.Send(msg);
messagesToSend.Remove(dataToSendNow);
Console.WriteLine("Server send data");
} catch (SocketException ex) {
Console.WriteLine(ex.ToString());
Console.WriteLine("returning from sendingThread sockEx");
return;
} catch (ObjectDisposedException ex) {
Console.WriteLine(ex.ToString());
//socketError = true;
Console.WriteLine("returning from sendingThread objDisEx");
return;
}
Thread.Sleep(100);
}
}
private void receivingThread(CancellationToken ct) {
while (true) {
try {
if (ct.IsCancellationRequested) {
// Clean up here, then...
ct.ThrowIfCancellationRequested();
Console.WriteLine("Task receiving is cancelled");
return;
}
} catch (System.OperationCanceledException ex) {
Console.WriteLine("System.OperationCanceledException");
return;
}
Console.WriteLine("Server is waiting for a new message to arrive: ");
try {
if (!handler.Connected) throw new SocketException();
//handler.Send(new byte[] { 0 });
int bytesRec = handler.Receive(bytes);
string receivedData = Encoding.ASCII.GetString(bytes, 0, bytesRec);
Console.WriteLine("Server has received message = {0}",
receivedData);
/*
//do some stuff with the message
.
.
.
*/
} catch (SocketException ex) {
Console.WriteLine(ex.ToString());
//socketError = true;
return;
} catch (ObjectDisposedException ex) {
Console.WriteLine(ex.ToString());
//socketError = true;
return;
}
}
}
public void StartListening() {
// Establish the local endpoint for the socket.
// Dns.GetHostName returns the name of the
// host running the application.
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
while (true) {
// Create a TCP/IP socket.
// Bind the socket to the local endpoint and
// listen for incoming connections.
Socket listener = new Socket(ipAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
Console.WriteLine("New connection can be made");
try {
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11100);
listener.Bind(localEndPoint);
listener.Listen(10);
// Start listening for connections.
Console.WriteLine("Waiting for a connection...");
// Program is suspended while waiting for an incoming connection.
handler = listener.Accept();
Console.WriteLine("New connection is made");
var tokenSource2 = new CancellationTokenSource();
CancellationToken ct = tokenSource2.Token;
//when new connection is made create new thread for sending data through socket
var task_send = Task.Run(() => {
sendingThread(ct);
}, tokenSource2.Token);
var tokenSource3 = new CancellationTokenSource();
CancellationToken ct3 = tokenSource3.Token;
//when new connection is made create new thread for receiving data through socket
var task = Task.Run(() => {
receivingThread(ct3);
}, tokenSource3.Token);
while (true) {
if (task.IsCompleted || task_send.IsCompleted) { //activelly oolling the field to find out, wether threads has been cancelled/returned
Console.WriteLine("some tasks is Completed");
tokenSource2.Cancel();
tokenSource3.Cancel();
handler.Shutdown(SocketShutdown.Both);
handler.Close();
break; //breaking from polling loop to prepare for a new connection
}
}
} catch (SocketException se) {
Console.WriteLine(se.ToString());
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
listener.Dispose(); //disposing listener so that new one can be created
}
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
}
public static int Main(String[] args) {
SynchronousSocketListener synSocList = new SynchronousSocketListener();
var task_generateMsg = Task.Run(
(synSocList.threadForGeneratingMessages));
synSocList.StartListening();
return 0;
}
}
client's app structure is basically the same as server's with exception, that it is actively trying to connect to server, if connection has been disrupted:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class SynchronousSocketClient {
private Socket sender; //socket
private byte[] bytes; // Data buffer for incoming data.
private List < String > messagesToSend;
private Mutex messagesToSendMutex;
public SynchronousSocketClient() {
messagesToSendMutex = new Mutex();
try {
messagesToSendMutex.WaitOne();
messagesToSend = new List < String > ();
} finally {
messagesToSendMutex.ReleaseMutex();
}
bytes = new Byte[1024];
sender = null;
}
private void addMessageToQueue(string message) {
try {
messagesToSendMutex.WaitOne();
messagesToSend.Add(message);
} finally {
messagesToSendMutex.ReleaseMutex();
}
}
private string readMessageFromQueue() {
string ret = null;
try {
messagesToSendMutex.WaitOne();
if (messagesToSend.Count > 0)
ret = messagesToSend[0];
} finally {
messagesToSendMutex.ReleaseMutex();
}
return ret;
}
private void removeMessageFromQueue(string messageToRemove) {
string ret = null;
try {
messagesToSendMutex.WaitOne();
if (messagesToSend.Count > 0)
messagesToSend.Remove(messageToRemove);
} finally {
messagesToSendMutex.ReleaseMutex();
}
}
private void threadForGeneratingMessages() {
while (true) {
addMessageToQueue(Console.ReadLine());
}
}
private void sendingThread(CancellationToken ct) {
while (true) {
try {
if (ct.IsCancellationRequested) {
// Clean up here, then...
ct.ThrowIfCancellationRequested();
Console.WriteLine("Task sending is cancelled");
return;
}
} catch (System.OperationCanceledException ex) {
Console.WriteLine("System.OperationCanceledException");
return;
}
if (messagesToSend.Count <= 0) {
Console.WriteLine("No more messages to send");
Thread.Sleep(5000);
continue;
}
Console.WriteLine("Client is going to write some data for client");
//send pending messages to client
string dataToSendNow = messagesToSend[0];
byte[] msg = Encoding.ASCII.GetBytes(dataToSendNow + "<EOF>");
try {
// Send the data through the socket.
int bytesSent = sender.Send(msg);
messagesToSend.Remove(dataToSendNow);
Console.WriteLine("Client send data");
} catch (SocketException ex) {
Console.WriteLine(ex.ToString());
Console.WriteLine("returning from sendingThread sockEx");
return;
} catch (ObjectDisposedException ex) {
Console.WriteLine(ex.ToString());
//socketError = true;
Console.WriteLine("returning from sendingThread objDisEx");
return;
}
}
}
private void receivingThread(CancellationToken ct) {
while (true) {
try {
if (ct.IsCancellationRequested) {
// Clean up here, then...
ct.ThrowIfCancellationRequested();
Console.WriteLine("Task receiving is cancelled");
return;
}
} catch (System.OperationCanceledException ex) {
Console.WriteLine("System.OperationCanceledException");
return;
}
Console.WriteLine("Client is waiting for a new message to arrive: ");
try {
if (!sender.Connected) throw new SocketException();
//sender.Send(new byte[] { 0 });
int bytesRec = sender.Receive(bytes);
string receivedData = Encoding.ASCII.GetString(bytes, 0, bytesRec);
Console.WriteLine("Client has received message = {0}",
receivedData);
/*
//do some stuff with the message
.
.
.
*/
} catch (SocketException ex) {
Console.WriteLine(ex.ToString());
//socketError = true;
return;
} catch (ObjectDisposedException ex) {
Console.WriteLine(ex.ToString());
//socketError = true;
return;
}
}
}
private void sendingThread() {
while (true) {
Console.WriteLine("Write some data for server");
string line = Console.ReadLine();
byte[] msg = Encoding.ASCII.GetBytes(line + "<EOF>");
try {
// Send the data through the socket.
int bytesSent = sender.Send(msg);
Console.WriteLine("data are send");
// Receive the response from the remote device.
} catch (SocketException ex) {
Console.WriteLine(ex.ToString());
socketError = false;
return;
}
Thread.Sleep(100);
}
}
volatile private bool socketError = false;
private void receivingThread() {
while (true) {
Console.WriteLine("receiving:");
try {
if (!sender.Connected) throw new SocketException();
sender.Send(new byte[] {
0
});
int bytesRec = sender.Receive(bytes);
Console.WriteLine("Echoed test = {0}",
Encoding.ASCII.GetString(bytes, 0, bytesRec));
} catch (SocketException ex) {
Console.WriteLine(ex.ToString());
socketError = true;
return;
}
}
}
public void StartClient() {
// Data buffer for incoming data.
while (true) {
// Connect to a remote device.
try {
// Establish the remote endpoint for the socket.
// This example uses port 11000 on the local computer.
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, 11100);
// Create a TCP/IP socket.
sender = new Socket(ipAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
// Connect the socket to the remote endpoint. Catch any errors.
try {
sender.Connect(remoteEP);
Console.WriteLine("Client has made connection to the server");
/*
try
{
task_send.Wait();
}
catch (AggregateException ae)
{
Console.WriteLine(ae.ToString());
foreach (var ex in ae.InnerExceptions)
{
throw ex;
}
}
*/
var tokenSource2 = new CancellationTokenSource();
CancellationToken ct = tokenSource2.Token;
//when new connection is made create new thread for sending data through socket
var task_send = Task.Run(() => {
sendingThread(ct);
}, tokenSource2.Token);
var tokenSource3 = new CancellationTokenSource();
CancellationToken ct3 = tokenSource3.Token;
//when new connection is made create new thread for receiving data through socket
var task = Task.Run(() => {
receivingThread(ct3);
}, tokenSource3.Token);
while (true) {
if (task.IsCompleted || task_send.IsCompleted) { //activelly oolling the field to find out, wether threads has been cancelled/returned
Console.WriteLine("some tasks is Completed");
tokenSource2.Cancel();
tokenSource3.Cancel();
sender.Shutdown(SocketShutdown.Both);
sender.Close();
break; //breaking from polling loop to prepare for a new connection
}
}
/*
var task_send = Task.Run(
(sendingThread));
var task = Task.Run(
(receivingThread));
*/
/*
try
{
task.Wait();
}
catch (AggregateException ae)
{
foreach (var ex in ae.InnerExceptions)
{
throw ex;
}
}*/
/*
bool task_receiving_res = false;
//int timeout = 60000;
Task<bool> task_receiving = new Task<bool>(() => receivingThread());
task_receiving.Start();
Console.WriteLine("here0");
if (await Task.WhenAny(task_receiving, Task.Delay(Timeout.Infinite)) == task_receiving)
{
Console.WriteLine("here-0");
// task completed within timeout
task_receiving_res = task_receiving.Result;
}
else
{
throw new TimeoutException();
}
Console.WriteLine("here");
bool task_sending_res = false;
//int timeout = 60000;
Task<bool> task_sending = new Task<bool>(() => sendingThread());
task_sending.Start();
if (await Task.WhenAny(task_sending, Task.Delay(Timeout.Infinite)) == task_sending)
{
// task completed within timeout
task_sending_res = task_sending.Result;
}
else
{
throw new TimeoutException();
}
*/
/*
Console.WriteLine("here1");
while (true) {
if (socketError) {
/*task.Dispose();
task_send.Dispose();
*/
/*
socketError = false;
throw new Exception("restart connection");
}
}*/
/*
while (true)
{
*/
/*Console.WriteLine("Socket connected to {0}",
sender.RemoteEndPoint.ToString());*/
/*
// Encode the data string into a byte array.
byte[] msg = Encoding.ASCII.GetBytes("This is a test<EOF>");
// Send the data through the socket.
int bytesSent = sender.Send(msg);
*/
// Receive the response from the remote device.
/*int bytesRec = sender.Receive(bytes);
Console.WriteLine("Echoed test = {0}",
Encoding.ASCII.GetString(bytes, 0, bytesRec));
*/
/*
bytesRec = sender.Receive(bytes);
Console.WriteLine("Echoed test = {0}",
Encoding.ASCII.GetString(bytes, 0, bytesRec));
*/
/*
Thread.Sleep(5000);
}*/
// Release the socket.
sender.Shutdown(SocketShutdown.Both);
sender.Close();
} catch (ArgumentNullException ane) {
Console.WriteLine("ArgumentNullException : {0}", ane.ToString());
} catch (SocketException se) {
Console.WriteLine("SocketException : {0}", se.ToString());
} catch (Exception e) {
Console.WriteLine("Unexpected exception : {0}", e.ToString());
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
Thread.Sleep(5000);
}
}
public static int Main(String[] args) {
SynchronousSocketClient sc = new SynchronousSocketClient();
var task_generateMsg = Task.Run(
(sc.threadForGeneratingMessages));
sc.StartClient();
return 0;
}
}
How could these code be written do be more efficient? Is this safe, or are there any problems with these code (besides efficiency)?
Edit:
I have a problem with the code:
sometimes receivingThread is looping without halting at handler/sender.Receive() function, it seems to read 0 bytes even though other side does not send anything. How can this be corrected?
My client part is closing after I do a request to server a second time, but without errors, it just goes away:
class Client
{
static void Main(string[] args)
{
try
{
Console.Title = "Client";
AsyncClient client = new AsyncClient(60101);
client.Connect();
Console.Read();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.Read();
}
}
}
public class AsyncClient
{
private IPAddress ipAddress;
private int port;
/// <summary>
/// Connects to the local IPAddress.
/// </summary>
/// <param name="port"></param>
public AsyncClient(int port)
{
this.port = port;
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
this.ipAddress = null;
for (int i = 0; i < ipHostInfo.AddressList.Length; i++)
{
if (ipHostInfo.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
{
this.ipAddress = ipHostInfo.AddressList[i];
break;
}
}
if (this.ipAddress == null)
throw new Exception("No IPv4 address has been found");
}
public AsyncClient(string ip, int port)
{
this.port = port;
IPAddress.TryParse(ip, out ipAddress);
}
public async void Connect()
{
int attempts = 0;
TcpClient client = new TcpClient();
while (!client.Connected)
{
try
{
attempts++;
client.Connect(this.ipAddress, this.port);
Console.Clear();
Console.WriteLine("Connected");
await Process(client);
}
catch (SocketException)
{
Console.Clear();
Console.WriteLine("Connection Attempts: {0}", attempts);
}
}
}
public async Task Process(TcpClient tcpClient)
{
try
{
NetworkStream stream = tcpClient.GetStream();
StreamWriter writer = new StreamWriter(stream);
StreamReader reader = new StreamReader(stream);
writer.AutoFlush = true;
while (true)
{
Console.WriteLine("Enter a Request: ");
await writer.WriteLineAsync(Console.ReadLine());
string response = await reader.ReadLineAsync();
if (response != null)
Console.WriteLine(response);
else
break;
}
}
catch (Exception)
{
//
}
finally
{
if (!tcpClient.Connected)
{
for (int i = 5; i >= 1; i--)
{
Console.WriteLine($"Connection lost, trying to reconnect in {i}");
Thread.Sleep(1000);
}
Connect();
}
}
}
}
This under is the server side code, it's just for study purpose. I am trying to learn how to work with sockets and after trying with many different ways like "begin" methods, etc, I feel like I've finally found the right way to do it, since with the others I had problems like concurrent access, closing connection, etc, but this time I believe I got it right.
Am I wrong or this time it's really all good with my code?
class Server
{
static void Main(string[] args)
{
try
{
Console.Title = "Server";
AsyncServer server = new AsyncServer(60101);
server.Run();
Console.Read();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.Read();
}
}
}
public class AsyncServer
{
private IPAddress ipAddress;
private int port;
public AsyncServer(int port)
{
this.port = port;
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
this.ipAddress = null;
for (int i = 0; i < ipHostInfo.AddressList.Length; i++)
{
if (ipHostInfo.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
{
this.ipAddress = ipHostInfo.AddressList[i];
break;
}
}
if (this.ipAddress == null)
throw new Exception("No IPv4 address for server");
}
public async void Run()
{
TcpListener listener = new TcpListener(this.ipAddress, this.port);
listener.Start();
Console.WriteLine($"Server is now online on Port: {this.port}");
Console.WriteLine("Hit <Enter> to stop the service");
while (true)
{
try
{
TcpClient tcpClient = await listener.AcceptTcpClientAsync();
Process(tcpClient);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
private async void Process(TcpClient tcpClient)
{
string clientEndPoint = tcpClient.Client.RemoteEndPoint.ToString();
Console.WriteLine($"Received connection request from {clientEndPoint}");
try
{
NetworkStream networkStream = tcpClient.GetStream();
StreamReader reader = new StreamReader(networkStream);
StreamWriter writer = new StreamWriter(networkStream);
writer.AutoFlush = true;
while (true)
{
string request = await reader.ReadLineAsync();
if (request != null)
Handle(request, writer);
else
break;
}
}
catch (Exception)
{
//
}
finally
{
if (tcpClient.Connected)
tcpClient.Close();
Console.WriteLine($"{clientEndPoint} has closed the connection, aborting operation");
}
}
private string Response(string request)
{
Thread.Sleep(10000);
if (request.ToLower() == "get time")
return DateTime.Now.ToLongTimeString();
else
return $"\"{request}\" is a invalid request";
}
private async void Handle(string request, StreamWriter writer)
{
try
{
Console.WriteLine($"Received request: {request}");
string response = Response(request);
Console.WriteLine($"Computed response is: {response}");
await writer.WriteLineAsync(response);
}
catch (Exception)
{
//
}
}
}
Plus, I would like to know, if I want to make it work on my external IP, so ppl from different IPs can use it, what should I change?
My client part is closing after I do a request to server a second
time, but without errors, it just goes away:
The reason for this is that your client calls async method client.Connect(), but doesn't (a)wait this method, so execution on the main thread continues to the next line, Console.Read(), which blocks only until you press [ENTER] for the second time (first [ENTER] is consumed by Console.ReadLine() in a Process() method). Then there is nothing for main thread to do and main thread (as well as whole client application) exits.
As a side note, it is good practise to name all async methods such that it's name ends with 'Async', so that caller of such a method is aware of it's async behaviour and doesn't forget to (a)wait the method. So you should rename Connect to ConnectAsync and Process to ProcessAsync.
Solution is to change return type of Connect method to Task, making method awaitable (it is strongly discouraged for async method to return void anyway):
public async Task ConnectAsync()
and add .Wait() in the Main method, which blocks main thread, until ConnectAsync() exits.
client.ConnectAsync().Wait();
In C# 7.1 you could also use async Main instead:
static async Task Main(string[] args)
{
...
await client.ConnectAsync();
...
}
Plus, I would like to know, if I want to make it work on my external
IP, so ppl from different IPs can use it, what should I change?
Just make sure that if server has more than one IP address, TcpListener listens on the correct one, and enable port or application in the firewall.
I'm trying to implement an async TCP client that sends messages from a queue and listens to the response.
some of the server replies are lost (for example send 7 messages and get only 4 replies) and I don't understand why.
This is not a server issue, since the synchronize version I tested works just fine.
ConcurrentQueue<byte[]> msgQueue = new ConcurrentQueue<byte[]>();
public void Connect()
{
try
{
tcpclnt = new TcpClient();
Console.WriteLine("Connecting.....");
Task.Factory.StartNew(() =>
{
IAsyncResult res = tcpclnt.BeginConnect(_ip, _port, null, null);
if (!res.AsyncWaitHandle.WaitOne(CONNECTION_TIMEOUT_SEC * 1000))
{
tcpclnt.Close();
throw new ApplicationException("timed out trying to connect");
}
tcpclnt.EndConnect(res);
Receive();
byte[] message = null;
while (true)
{
message = null;
msgQueue.TryDequeue(out message);
if (message != null)
{
Stream stm = tcpclnt.GetStream();
Console.WriteLine("Transmitting..... " + Thread.CurrentThread.ManagedThreadId);//for debug
stm.Write(message.ToArray(), 0, message.ToArray().Length);
Receive();
}
}
});
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
//will be called from outside
public void SendMessage(byte[] msg)
{
Console.WriteLine("SendMessage..... " + Thread.CurrentThread.ManagedThreadId);//for debug
msgQueue.Enqueue(msg);
}
private void Receive()
{
SocketError error;
byte[] buffer = new byte[MAX_BUFFER_SIZE];
tcpclnt.Client.BeginReceive(buffer, 0, MAX_BUFFER_SIZE, SocketFlags.None, out error, new AsyncCallback(ReceiveHandler), buffer);
}
private void ReceiveHandler(IAsyncResult ar)
{
System.Console.WriteLine("ReceiveHandler " + Thread.CurrentThread.ManagedThreadId); //for debug
//End current async receive
int bytesRead = tcpclnt.Client.EndReceive(ar);
byte[] resultBuffer = (byte[]) ar.AsyncState;
// do a lot of things with resultBuffer
}
Im trying to make a TLS/SSL websocket connection using Fleck lib.
https://github.com/statianzo/Fleck (0.9.8.25)
now i got the server startet .
but when a client connects i get the following message.
28-02-2014 19:16:15 [Info] Server started at wss://localhost:8081
28-02-2014 19:18:51 [Debug] Client connected from 127.0.0.1:62543
28-02-2014 19:18:51 [Debug] Authenticating Secure Connection
28-02-2014 19:18:52 [Debug] 0 bytes read. Closing.
anybody got an idea of what im doing wrong ?
Browser: Chrome, version : 33.0.1750.117
class Program
{
private static UTF8Encoding encoding = new UTF8Encoding();
static void Main(string[] args)
{
Connect("wss://localhost:8081").Wait();
}
public static async Task Connect(string uri)
{
Thread.Sleep(1000);
ClientWebSocket webSocket = null;
X509Certificate2 certif = new X509Certificate2(#"QtrackerServer_TemporaryKey.pfx", "jihedgam");
webSocket.Options.ClientCertificates.Equals(certif);
try
{
webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(uri), CancellationToken.None);
await Task.WhenAll(Receive(webSocket), Send(webSocket));
}
catch (Exception ex)
{
Console.WriteLine("Exeption: {0}", ex);
}
finally
{
if (webSocket != null)
webSocket.Dispose();
Console.WriteLine();
Console.WriteLine("WebSocket closed.");
}
}
private static async Task Send(ClientWebSocket webSocket)
{
while (webSocket.State == WebSocketState.Open)
{
Console.WriteLine("Write some to send over to server..");
string stringtoSend = Console.ReadLine();
byte[] buffer = encoding.GetBytes(stringtoSend);
await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Binary, false, CancellationToken.None);
Console.WriteLine("Send: " + stringtoSend);
await Task.Delay(1000);
}
}
private static async Task Receive(ClientWebSocket webSocket)
{
byte[] buffer = new byte[1024];
while (webSocket.State == WebSocketState.Open)
{
buffer = new byte[1024];
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
{
Console.WriteLine("Recive: " + Encoding.UTF8.GetString(buffer).TrimEnd('\0'));
}
}
}
}
I have a WebSocket server that accepts a stream of binary data from a client and responds with another stream of text data for every 4MB read. The server uses IIS 8 and asp.net web api.
Server
public class WebSocketController : ApiController
{
public HttpResponseMessage Get()
{
if (!HttpContext.Current.IsWebSocketRequest)
{
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
HttpContext.Current.AcceptWebSocketRequest(async (context) =>
{
try
{
WebSocket socket = context.WebSocket;
byte[] requestBuffer = new byte[4194304];
int offset = 0;
while (socket.State == WebSocketState.Open)
{
var requestSegment = new ArraySegment<byte>(requestBuffer, offset, requestBuffer.Length - offset);
WebSocketReceiveResult result = await socket.ReceiveAsync(requestSegment, CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
// Send one last response before closing
var response = new ArraySegment<byte>(Encoding.UTF8.GetBytes("Server got " + offset + " bytes\n"));
await socket.SendAsync(response, WebSocketMessageType.Text, true, CancellationToken.None);
// Close
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
break;
}
offset += result.Count;
if (offset == requestBuffer.Length)
{
// Regular response
var response = new ArraySegment<byte>(Encoding.UTF8.GetBytes("Server got 4194304 bytes\n"));
await socket.SendAsync(response, WebSocketMessageType.Text, true, CancellationToken.None);
offset = 0;
}
}
}
catch (Exception ex)
{
// Log and continue
}
});
return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols);
}
}
The c# client uses the ClientWebSocket class to connect to the server and send requests. It creates a task for receiving responses from the server that runs in parallel with the request sending. When it is done sending the requests it calls CloseAsync on the socket and then waits for the Receive task to complete.
Client
using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WebSocketClient
{
class Program
{
static void Main(string[] args)
{
try
{
CallWebSocketServer().Wait();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
static async Task CallWebSocketServer()
{
using (ClientWebSocket socket = new ClientWebSocket())
{
await socket.ConnectAsync(new Uri("ws://localhost/RestWebController"), CancellationToken.None);
byte[] buffer = new byte[128 * 1024];
Task receiveTask = Receive(socket);
for (int i = 0; i < 1024; ++i)
{
await socket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Binary, true, CancellationToken.None);
}
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
receiveTask.Wait();
Console.WriteLine("All done");
}
}
static async Task Receive(ClientWebSocket socket)
{
try
{
byte[] recvBuffer = new byte[64 * 1024];
while (socket.State == WebSocketState.Open)
{
var result = await socket.ReceiveAsync(new ArraySegment<byte>(recvBuffer), CancellationToken.None);
Console.WriteLine("Client got {0} bytes", result.Count);
Console.WriteLine(Encoding.UTF8.GetString(recvBuffer, 0, result.Count));
if (result.MessageType == WebSocketMessageType.Close)
{
Console.WriteLine("Close loop complete");
break;
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception in receive - {0}", ex.Message);
}
}
}
}
The problem is that the client blocks at the CloseAsync call.
What would be the correct way of gracefully closing the WebSocket in this scenario?
Figured this out.
Server
Basically, I had to call the ClientWebSocket.CloseOutputAsync (instead of the CloseAsync) method to tell the framework no more output is going to be sent from the client.
await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
Client
Then in the Receive function, I had to allow for socket state WebSocketState.CloseSent to receive the Close response from the server
static async Task Receive(ClientWebSocket socket)
{
try
{
byte[] recvBuffer = new byte[64 * 1024];
while (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseSent)
{
var result = await socket.ReceiveAsync(new ArraySegment<byte>(recvBuffer), CancellationToken.None);
Console.WriteLine("Client got {0} bytes", result.Count);
Console.WriteLine(Encoding.UTF8.GetString(recvBuffer, 0, result.Count));
if (result.MessageType == WebSocketMessageType.Close)
{
Console.WriteLine("Close loop complete");
break;
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception in receive - {0}", ex.Message);
}
}
i suggest you to look at these links:
asynchronous server:
https://msdn.microsoft.com/en-us/library/fx6588te%28v=vs.110%29.aspx
asynchronous client:
https://msdn.microsoft.com/en-us/library/bew39x2a(v=vs.110).aspx
recently i implementled something similar with these links as an example. the methods "BeginReceive" (for server) and "BeginConnect" (for client) start each a new thread. so there won't be anything that blocks