C# WebSocket - WebSocketException while receiving data - c#

I wrote a program for the fire department to receive data from the control center.
When receiving status data, it works perfectly. But now when we would have received the first alarm via this WebSocket interface, the following error occurred:
System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake.
at System.Net.WebSockets.ManagedWebSocket.ThrowIfEOFUnexpected(Boolean throwOnPrematureClosure)
at System.Net.WebSockets.ManagedWebSocket.EnsureBufferContainsAsync(Int32 minimumRequiredBytes, CancellationToken cancellationToken, Boolean throwOnPrematureClosure)
at System.Net.WebSockets.ManagedWebSocket.ReceiveAsyncPrivate[TWebSocketReceiveResultGetter,TWebSocketReceiveResult](Memory`1 payloadBuffer, CancellationToken cancellationToken, TWebSocketReceiveResultGetter resultGetter)
at FF.Service.Services.XYZService.GetDataAsync(Boolean isRetrying) in C:\Users\XXX\source\repos\FF.Service\FF.Service\Services\XYZService.cs:line 108
But I don't find the error in my code:
using (ClientWebSocket socket = new ClientWebSocket())
{
// set login tokens as request headers
socket.Options.SetRequestHeader("master_token", masterToken);
socket.Options.SetRequestHeader("sub_token", subToken);
// read certificate string from file and prepare for certificate creation
string certificateContent = File.ReadAllText(Path.Combine(Program.AssemblyDirectory, "ff-cloud.pem"))
.Trim()
.Replace("-----BEGIN CERTIFICATE-----", null)
.Replace("-----END CERTIFICATE-----", null);
// create certificate and add to web socket options
X509Certificate2 cert = new X509Certificate2(Convert.FromBase64String(certificateContent));
socket.Options.ClientCertificates.Add(cert);
// ignore certificate errors
socket.Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
// set buffer and buffer size
int bufferSize = 8192;
byte[] buffer = new byte[bufferSize];
// connect the web socket to the remote server
await socket.ConnectAsync(new Uri(fullServerAddress), CancellationToken.None);
// work while connection is open
while (socket.State == System.Net.WebSockets.WebSocketState.Open)
{
State = WebSocketState.Open;
WebSocketReceiveResult receiveResult;
MemoryStream outputStream = new MemoryStream(bufferSize);
do
{
// receive data
// IN THE NEXT LINE OCCURS THE ERROR!!!!
receiveResult = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (receiveResult.MessageType != WebSocketMessageType.Close)
outputStream.Write(buffer, 0, receiveResult.Count);
}
// read while end of message isn't reached
while (!receiveResult.EndOfMessage);
// set the output stream position to 0 - to read from the beginning
outputStream.Position = 0;
// read the message
StreamReader streamReader = new StreamReader(outputStream);
string message = streamReader.ReadToEnd();
LogService.GetInstance().Log(message, LogService.LogTypes.AllData);
// check if the message is alarm data
if (message.Contains("alarm_data"))
{
try
{
// deserialize received data to alarm data
FF.AlarmData.Rootobject rootobject =
JsonSerializer.Deserialize<FF.AlarmData.Rootobject>(message);
LogService.GetInstance().Log("Successfully received alarm data!", LogService.LogTypes.Success);
// fire the alarm received event
if (AlarmReceived != null)
AlarmReceived(this, new AlarmEventArgs(rootobject));
}
catch (Exception ex)
{
LogService.GetInstance().Log($"Error at receiveing alarm data! ({ex.Message})", LogService.LogTypes.Error);
}
}
// check if the message is a radio status
else if (message.Contains("radio_status"))
{
try
{
// deserialize received data to radio status
FF.StatusData.Rootobject rootobject =
JsonSerializer.Deserialize<FF.StatusData.Rootobject>(message);
LogService.GetInstance().Log("Successfully received status data!", LogService.LogTypes.Success);
// fire the status received event
if (StatusReceived != null)
StatusReceived(this, new StatusEventArgs(rootobject));
}
catch (Exception ex)
{
LogService.GetInstance().Log($"Error at receiveing status data! ({ex.Message})", LogService.LogTypes.Error);
}
}
// break the loop if the received message is a close message
if (receiveResult.MessageType == WebSocketMessageType.Close)
break;
}
LogService.GetInstance().Log("Connection lost!", LogService.LogTypes.Warning);
try
{
LogService.GetInstance().Log("Try to close connection!", LogService.LogTypes.Information);
// close web socket connection
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
}
catch (Exception ex)
{
LogService.GetInstance().Log($"Error on closing connection: {ex.ToString()}", LogService.LogTypes.Warning);
}
}
The error occurs int the line unter the comment "IN THE NEXT LINE OCCURS THE ERROR!!!!". Unfortunatelly there is no way to receive alarm data only for testing.
Does someone know what I'm doing wrong?
Thank you in forward for your answers
Regards Matthias

Related

Websocket.ReceiveAsync causing a crash when a client disconnects without cancelling connection

DotNet Core 3.1 (running either Kestral or IIS)
I have the following loop, running in a Task for each connected client
using (var ms = new MemoryStream())
{
do
webSocketReceiveResult = await socket.ReceiveAsync(buffer, CancellationToken.None);
ms.Write(buffer.Array, buffer.Offset, webSocketReceiveResult.Count);
}
while (!webSocketReceiveResult.EndOfMessage);
When a client abruptly exits, network failure, crash or whatever and doesnt have a chance to close its connection, it crashes the whole class, and all tasks running. The webserver is still up and will accept all new connections, but all existing connections are terminated.
The error is : 'The remote party closed the WebSocket connection without completing the close handshake.'
which is expected, but I cannot try-catch it to preserve the rest of the connections?
Complete method here:
private static async Task SocketProcessingLoopAsync(ConnectedClient client)
{
_ = Task.Run(() => client.BroadcastLoopAsync().ConfigureAwait(false));
var socket = client.Socket;
var loopToken = SocketLoopTokenSource.Token;
var broadcastTokenSource = client.BroadcastLoopTokenSource; // store a copy for use in finally block
string sessionName = "";
string command = "";
int commandCounter = 0;
WebSocketReceiveResult webSocketReceiveResult = null;
try
{
var buffer = WebSocket.CreateServerBuffer(4096);
while (socket.State != WebSocketState.Closed && socket.State != WebSocketState.Aborted && !loopToken.IsCancellationRequested)
{
// collect all the bytes incoming
using (var ms = new MemoryStream())
{
do
{
webSocketReceiveResult = await socket.ReceiveAsync(buffer, CancellationToken.None);
ms.Write(buffer.Array, buffer.Offset, webSocketReceiveResult.Count);
}
while (!webSocketReceiveResult.EndOfMessage);
//var receiveResult = await client.Socket.ReceiveAsync(buffer, loopToken);
// if the token is cancelled while ReceiveAsync is blocking, the socket state changes to aborted and it can't be used
if (!loopToken.IsCancellationRequested)
{
// the client is notifying us that the connection will close; send acknowledgement
if (client.Socket.State == WebSocketState.CloseReceived && webSocketReceiveResult.MessageType == WebSocketMessageType.Close)
{
Console.WriteLine($"Socket {client.SocketId}: Acknowledging Close frame received from client");
broadcastTokenSource.Cancel();
await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Acknowledge Close frame", CancellationToken.None);
// the socket state changes to closed at this point
}
if (client.Socket.State == WebSocketState.Open)
{
if (webSocketReceiveResult.MessageType == WebSocketMessageType.Text)
{
// here we receive text instructioons from the clients
using (StreamReader reader = new StreamReader(ms, Encoding.UTF8))
{
ms.Seek(0, SeekOrigin.Begin);
command = reader.ReadToEnd().ToLower();
}
// var command = Encoding.UTF8.GetString(buffer.Array, 0, webSocketReceiveResult.Count).ToLower();
if (command.Contains("session:"))
{
// assign the client to a session, and inform them of their position in it
sessionName = command.Replace("session:", "");
if (!ClientControl.Sessions.ContainsKey(sessionName))
{
ClientControl.Sessions.TryAdd(sessionName, new BlockingCollection<ConnectedClient>());
}
ClientControl.Sessions[sessionName].Add(client);
// broadcast the collection count
Broadcast(ClientControl.Sessions[sessionName].Count.ToString(), client, true, sessionName);
Broadcast("Number of clients " + ClientControl.Sessions[sessionName].Count.ToString(), client, false, sessionName);
Broadcast("Number of clients " + ClientControl.Sessions[sessionName].Count.ToString(), client, true, sessionName);
}
else if (command.Contains("status:"))
{
string output = "<br/><h1>Sessions:</h1><br/><br/>";
foreach (var session in ClientControl.Sessions)
{
output += session.Key + " Connected Clients: " + session.Value.Count.ToString() + "<br/>";
}
Broadcast(output, client, true, "");
}
else if (command.Contains("ping"))
{
Console.WriteLine(command + " " + DateTime.Now.ToString() + " " + commandCounter);
}
}
else
{
// we just mirror what is sent out to the connected clients, depending on the session
Broadcast(ms.ToArray(), client, sessionName);
commandCounter++;
}
}
}
}// end memory stream
}
}
catch (OperationCanceledException)
{
// normal upon task/token cancellation, disregard
}
catch (Exception ex)
{
Console.WriteLine($"Socket {client.SocketId}:");
Program.ReportException(ex);
}
finally
{
broadcastTokenSource.Cancel();
Console.WriteLine($"Socket {client.SocketId}: Ended processing loop in state {socket.State}");
// don't leave the socket in any potentially connected state
if (client.Socket.State != WebSocketState.Closed)
client.Socket.Abort();
// by this point the socket is closed or aborted, the ConnectedClient object is useless
if (ClientControl.Sessions[sessionName].TryTake(out client))
socket.Dispose();
// signal to the middleware pipeline that this task has completed
client.TaskCompletion.SetResult(true);
}
}
Ok, couldn't get this to work, I have re-written the loop to look like this, it exits cleanly when a client hangs
do
{
cancellationTokenSource = new CancellationTokenSource(10000);
task = socket.ReceiveAsync(buffer, loopToken);
while (!task.IsCompleted && !SocketLoopTokenSource.IsCancellationRequested)
{
await Task.Delay(2).ConfigureAwait(false);
}
if (socket.State != WebSocketState.Open || task.Status != TaskStatus.RanToCompletion)
{
if (socket.State == WebSocketState.CloseReceived)
{
Console.WriteLine($"Socket {client.SocketId}: Acknowledging Close frame received from client");
broadcastTokenSource.Cancel();
await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Acknowledge Close frame", CancellationToken.None);
ClientControl.Sessions[sessionName].TryTake(out client);
}
Console.WriteLine("Client Left " + client.SocketId);
break;
}
ms.Write(buffer.Array, buffer.Offset, task.Result.Count);
}
while (!task.Result.EndOfMessage);

StreamSocketListener Recive Multiple Messages

This is my code, can you help me to change the code so that I can recive multiple messages? At the moment I can just recive one question then the client must reconnect.
I hope you can help me.
public string PortNumber = "1337";
public MainPage()
{
this.InitializeComponent();
StartServer();
}
private async void StartServer()
{
try
{
var streamSocketListener = new Windows.Networking.Sockets.StreamSocketListener();
// The ConnectionReceived event is raised when connections are received.
streamSocketListener.ConnectionReceived += this.StreamSocketListener_ConnectionReceived;
// Start listening for incoming TCP connections on the specified port. You can specify any port that's not currently in use.
await streamSocketListener.BindServiceNameAsync(this.PortNumber);
this.serverListBox.Items.Add("server is listening...");
}
catch (Exception ex)
{
Windows.Networking.Sockets.SocketErrorStatus webErrorStatus = Windows.Networking.Sockets.SocketError.GetStatus(ex.GetBaseException().HResult);
this.serverListBox.Items.Add(webErrorStatus.ToString() != "Unknown" ? webErrorStatus.ToString() : ex.Message);
}
}
private async void StreamSocketListener_ConnectionReceived(Windows.Networking.Sockets.StreamSocketListener sender, Windows.Networking.Sockets.StreamSocketListenerConnectionReceivedEventArgs args)
{
string request;
using (var streamReader = new StreamReader(args.Socket.InputStream.AsStreamForRead()))
{
request = await streamReader.ReadLineAsync();
}
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => this.TB1.Text=(string.Format("server received the request: \"{0}\"", request)));
// Echo the request back as the response.
using (Stream outputStream = args.Socket.OutputStream.AsStreamForWrite())
{
using (var streamWriter = new StreamWriter(outputStream))
{
await streamWriter.WriteLineAsync(request);
await streamWriter.FlushAsync();
}
}
}
You should always listen the upcoming message in the StreamSocketListener.ConnectionReceived Event, this event will only trigger when a connection was received on the StreamSocketListener object. If you want to always receive the sent data, you should always read it in the StreamSocketListener.ConnectionReceived Event.
Here is the sample code in the official StreamSocket:
In the Scenario1_Start.xaml.cs, the OnConnection is same as your StreamSocketListener_ConnectionReceived method. You can also learn more from the official sample.
private async void OnConnection(
StreamSocketListener sender,
StreamSocketListenerConnectionReceivedEventArgs args)
{
DataReader reader = new DataReader(args.Socket.InputStream);
try
{
while (true)
{
// Read first 4 bytes (length of the subsequent string).
uint sizeFieldCount = await reader.LoadAsync(sizeof(uint));
if (sizeFieldCount != sizeof(uint))
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Read the string.
uint stringLength = reader.ReadUInt32();
uint actualStringLength = await reader.LoadAsync(stringLength);
if (stringLength != actualStringLength)
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Display the string on the screen. The event is invoked on a non-UI thread, so we need to marshal
// the text back to the UI thread.
NotifyUserFromAsyncThread(
String.Format("Received data: \"{0}\"", reader.ReadString(actualStringLength)),
NotifyType.StatusMessage);
}
}
catch (Exception exception)
{
// If this is an unknown status it means that the error is fatal and retry will likely fail.
if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
{
throw;
}
NotifyUserFromAsyncThread(
"Read stream failed with error: " + exception.Message,
NotifyType.ErrorMessage);
}
}

cannot recieve data from c# tcp server

i've wrote a simple Tcp server in C#:
(I've replaced some of the code parts in "do some stuff", when it doesn't have anything to do with the server.
now, when I try to contact the server from a python client, or an android client, I get errors such as : "the other party actively refused connection". what am I supposed to do? is the problem in my C# code, or am I probably not contacting it correctly?
thank you.
public bool ListenLoop(Int32 port, IPAddress localAddr)
{
try
{
server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
// Buffer for reading data
Byte[] bytes = new Byte[256];
String data = null;
// Enter the listening loop.
while(true)
{
//Waiting for a connection
// Perform a blocking call to accept requests.
TcpClient client = server.AcceptTcpClient();
//connected!
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
int i;
// Loop to receive all the data sent by the client.
while((i = stream.Read(bytes, 0, bytes.Length))!=0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
//handling opCodes
if(data[0] == '0') //log in
{
//do some stuff
byte[] msg = System.Text.Encoding.ASCII.GetBytes(response);
// Send back a response.
stream.Write(msg, 0, msg.Length);
//sent
}
else if (data[0] == '1') //download tune names
{
//do some stuff
byte[] msg = System.Text.Encoding.ASCII.GetBytes(response); //response is the names
// Send back a response.
stream.Write(msg, 0, msg.Length);
//sent
}
else if (data[0] == '2') //changing choice
{
//do some stuff
byte[] msg = System.Text.Encoding.ASCII.GetBytes(response);
// Send back a response.
stream.Write(msg, 0, msg.Length);
//sent
}
}
// Shutdown and end connection
client.Close();
}
}
catch(SocketException)
{
return false;
}
finally
{
// Stop listening for new clients.
server.Stop();
}
}
Are you sure that there is no firewall that blocks the incoming connection (as it is TCP)?
Also if your server is in testing, you should write some output to either a log file or even just to console. At least you know what is going on.
server = new TcpListener(localAddr, port);
change to
server = new TcpListener(port);

Using ThreadPool.QueueUserWorkItem to open a TcpClient connection and read data in ASP.NET and SignalR

I've read a couple of posts on SignalR and thought for a fun test project that I could create a web application to poll my onkyo receiver for status and display the results in a browser. For an initial test, I was successfully able to send the current time on the server back to the client by using this code in Application_Start:
ThreadPool.QueueUserWorkItem(_ =>
{
dynamic clients = Hub.GetClients<KudzuHub>();
while (true)
{
clients.addMessage(DateTime.Now.ToString());
Thread.Sleep(1000);
}
});
In the client javascript, i have the following code:
// Proxy created on the fly
var kHub = $.connection.kudzuHub;
// Declare a function on the hub so that the server can invoke it
kHub.addMessage = function (message) {
console.log('message added');
$('#messages').append('<li>' + message + '</li>');
};
// start the connection
$.connection.hub.start();
So all of that works fine. Every second, I get a new list item containing the current server date and time.
Now when I add this code to read data from the Onkyo receiver, it breaks: (still in Application_Start)
ThreadPool.QueueUserWorkItem(_ =>
{
dynamic clients = Hub.GetClients<KudzuHub>();
try
{
while (true)
{
string host = ConfigurationManager.AppSettings["receiverIP"].ToString();
int port = Convert.ToInt32(ConfigurationManager.AppSettings["receiverPort"]);
TcpClient tcpClient = new TcpClient(host, port);
NetworkStream clientSockStream = tcpClient.GetStream();
byte[] bytes = new byte[tcpClient.ReceiveBufferSize];
clientSockStream.Read(bytes, 0, (int)tcpClient.ReceiveBufferSize);
tcpClient.Close();
clients.addMessage(System.Text.Encoding.ASCII.GetString(bytes));
Thread.Sleep(50);
}
}
catch (SocketException ex)
{
// do something to handle the error
}
});
I set a break point and stepped through the code. It gets to this line and then returns.
clientSockStream.Read(bytes, 0, (int)tcpClient.ReceiveBufferSize);
It never finishes the rest of the code to send the message to the client. What am I doing wrong?
Thanks.
I would make some structural changes to your loop to allow the receiver time to respond, remove the overhead of retrieving the configuration every 50 milliseconds, and cleanup the open network stream:
ThreadPool.QueueUserWorkItem(_ =>
{
dynamic clients = Hub.GetClients<KudzuHub>();
TcpClient tcpClient = null;
NetworkStream clientSockStream = null;
try
{
string host = ConfigurationManager.AppSettings["receiverIP"].ToString();
int port = Convert.ToInt32(ConfigurationManager.AppSettings["receiverPort"]);
while (true)
{
if (tcpClient == null) {
tcpClient = new TcpClient(host, port);
clientSockStream = tcpClient.GetStream();
}
if (clientSockStream.CanRead) {
byte[] bytes = new byte[tcpClient.ReceiveBufferSize];
try {
clientSockStream.Read(bytes, 0, (int)tcpClient.ReceiveBufferSize);
} catch (Exception ex) {
// Add some debug code here to examine the exception that is thrown
}
tcpClient.Close();
// Closing the client does not automatically close the stream
clientSockStream.Close();
tcpClient = null;
clientSockStream = null;
clients.addMessage(System.Text.Encoding.ASCII.GetString(bytes));
}
Thread.Sleep(50);
}
}
catch (SocketException ex)
{
// do something to handle the error
} finally {
if (tcpClient != null) {
tcpClient.Close();
clientSockStream.Close();
}
}
});

Close the Socket Listener application after 30 seconds if there is no response

(C#) Here I created a socket listener application which waits for the client hit. Now my requirement is my application should listen up to 30 seconds only later it should throw an error saying "Request Timed Out". Any suggestions ?
try
{
Byte[] bytes = new Byte[256];
String data = null;
// Enter the listening loop.
while (true)
{
ReceiveTimer.Stop(); ;
logger.Log("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also use server.AcceptSocket() here.
TcpClient client = server.AcceptTcpClient();
logger.Log("Connected!");
data = null;
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
logger.Log("Received: {0}", data);
// Process the data sent by the client.
data = data.ToUpper();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
// Send back a response.
stream.Write(msg, 0, msg.Length);
logger.Log("Sent: {0}", data);
}
// Shutdown and end connection
client.Close();
logger.Log("Shutdown and end connection");
}
}
catch (SocketException ex)
{
logger.Log("SocketException: " + ex);
}
I would think that the ReceiveTimeout property being set to 30 seconds would take care of that for you. Have you tried that yet?
client.ReceiveTimeout = 30000;
The property defaults to 0, so you would want to set that. It throws an IOException. You could catch that and throw the exception of your choosing to bubble up the application.
Why would you want to limit the time that the server listening ?
Server should be listening all the time that it's running.
Anyway, You can create a task(thread) that will be listening and after the timeout will be canceled.

Categories

Resources