C# ClientWebsocket throwing Exception on incoming binary message - c#

I implemented a websocket-client using System.Net.WebSockets to communicate with an embedded device. On the device the libwebsocket library is in use.
The main part of the protocol is implemented using JSON strings, which works perfectly but some binary transmission is also needed. Outgoing binary message from Windows are received correctly on the device, but in the case of incoming binary messages a exception is thrown at ReceiveAsync().
I implemented the receive part as a asynchronous loop that calls ReceiveAsync(). In the case of an incoming binary message the WebSocketException with the following error code is thrown:
HResult 0x83760002
E_INVALID_PROTOCOL_FORMAT Protocol data had invalid format.
I don't know what causes it, it throws before i can look at the data i receive. It already worked to receive binary data from the device, but it was an early implementation and only looped back the data that i sent.
private async void ReceiveLoop(CancellationToken cancellationToken)
{
try
{
List<byte> receivedBytes = new List<byte>();
var buffer = new byte[c_bufferSize];
while (!cancellationToken.IsCancellationRequested)
{
var receiveBuffer = new ArraySegment<Byte>(buffer);
WebSocketReceiveResult result;
result = await _webSocket.ReceiveAsync(receiveBuffer, cancellationToken);
receivedBytes.AddRange(receiveBuffer.Array);
// message is complete, return it
if (result.EndOfMessage)
{
if (result.MessageType == WebSocketMessageType.Text)
{
String receivedString = Encoding.UTF8.GetString(receivedBytes.ToArray());
Debug.Print($"{receivedString}");
Debug.Print($"{receivedString.Length.ToString()}");
WebsocketMessage response = new WebsocketMessage(receivedString);
MessagesSubject.OnNext(response);
}
if (result.MessageType == WebSocketMessageType.Binary)
{
WebsocketMessage response = new WebsocketMessage(receivedBytes.ToArray());
MessagesSubject.OnNext(response);
}
receivedBytes.Clear();
buffer = new byte[c_bufferSize];
}
}
}
catch(OperationCanceledException ex)
{
return;
}
catch(Exception ex)
{
MessagesSubject.OnError(ex);
}
}

Related

C# SocketException (0x80004005) on every two attempt

My program creates a new TCP Socket, sends a request to a server and reads the response. If the response is requested, the program sends an acknowledge and if not it sends a negative acknowledge. This send and receive part work as intended.
The problem is that when i call the method a second time it throws SocketException (0x80004005). The third attempt works just as intended but every two attempt to send request to socket will fail.
public void Send(byte[] request)
{
var buffer = new byte[1024];
var received = 0;
try
{
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
socket.Connect(Address, Port);
socket.ReceiveTimeout = 5000;
socket.Send(request);
while ((received = socket.Receive(buffer)) > 0)
{
var response = buffer.Take(received);
if (IsRequested(response))
{
socket.Send(ACK);
var text = Encoding.UTF8.GetString(response);
Console.WriteLine(text);
return;
}
}
socket.Send(NAK);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
I have tried disconnecting, closing, disposing and every possible combination of the three without success.
I discovered that if I throw an exception right after sending the acknowledge, the method will work every time!
if (IsRequested(response))
{
socket.Send(ACK);
var text = Encoding.UTF8.GetString(response);
Console.WriteLine(text);
throw new Exception("Deliberate exception");
}
My question:
Why do I get SocketException (0x80004005) every two attempt if I don't throw an exception?

How to detect closed StreamSocket at client side

DataReader.LoadAsync does not detect closed sockets (with InputStreamOptions::Partial)
I am sending data to server via TCP connection and read the response but after and everything is working here. But after 5-6 message my project is hand I didn't find where is error.
Some time i found that connection is closed via host machine
so How can i found that is StreamSocket is connected or not
============Code=============
public async Task<bool> Connect(string host, string port)
{
try
{
socket = new StreamSocket();
HostName hostName = new HostName(host);
CancellationTokenSource _cts = new CancellationTokenSource();
_cts.CancelAfter(5000);
// Connect to the server
await socket.ConnectAsync(hostName, port).AsTask(_cts.Token);
return true;
}
catch (Exception ex)
{
throw ex;
}
}
public async Task<String> SendMessageToServer(string message)
{
try
{
// Create the data writer object backed by the in-memory stream.
using (writer = new DataWriter(socket.OutputStream))
{
writer.WriteString(message);
await writer.StoreAsync();
await writer.FlushAsync();
writer.DetachStream();
return await ReadResponse();
}
}
catch (Exception ex)
{
throw ex;
}
}
private async Task<String> ReadResponse()
{
DataReader reader;
StringBuilder strBuilder = new StringBuilder();
try
{
using (reader = new DataReader(socket.InputStream))
{
uint ReadBufferLength = 1024;
// If task cancellation was requested, comply
//cancellationToken.ThrowIfCancellationRequested();
// Set InputStreamOptions to complete the asynchronous reask(cancellationToken);
reader.InputStreamOptions = Windows.Storage.Streams.InputStreamOptions.Partial;
IAsyncOperation<uint> taskLoad = reader.LoadAsync(ReadBufferLength);
taskLoad.AsTask().Wait(2000);
string msg = string.Empty;
while (reader.UnconsumedBufferLength > 0)
{
strBuilder.Append(reader.ReadString(reader.UnconsumedBufferLength));
msg = strBuilder.ToString();
}
reader.DetachStream();
reader.Dispose();
}
}
catch (Exception ex)
{
return "0";
throw ex;
}
finally
{
// Cleanup once complete
}
return strBuilder.ToString();
}
How can i found that is StreamSocket is connected or not
I tested your code on my side. DataReader/DataWriter are not concerned with "connections" themself as you known. But the connection closed will be detected by method Task.Wait, which is for synchronized waiting on the task completed or throw exceptions. So in your scenario, if the StreamSocked is closed before DataReader.LoadAsync, the taskLoad.AsTask().Wait(2000); will throw the exception:
One or more errors occurred. (An existing connection was forcibly closed by the remote host. )
If the socket connection is closed during DataReader.LoadAsync processed, this time the client cannot detect the closing, it will detect the connection closed next time the client send message to server. In your scenario client will continue sending messages to server which will always catch the connection closing when send new messages. You will got connection closed exception at await writer.StoreAsync(); code line if the server close the connection.
But after 5-6 message my project is hand I didn't find where is error
You may have catch the exception about connection closing. So this may lead by some other reasons. I saw the DataReader in your code snippet only load message one time for length 1024. So if the message send from server is longer than 1024, the client will receive only partial message, and next time when server send new message, the client will still receive the remain message from the last time not the new message. After several times server will back log many data and may lead issues. I recommend you to update the reader.LoadAsync code as follows:
string msg = string.Empty;
var loadsize = await reader.LoadAsync(ReadBufferLength);
while (loadsize >= ReadBufferLength)
{
loadsize = await reader.LoadAsync(ReadBufferLength);
}
if (reader.UnconsumedBufferLength > 0)
{
strBuilder.Append(reader.ReadString(reader.UnconsumedBufferLength));
}
Addtionaly, I strongly recommend you to use StreamReader instead, which will not have the concern as DataReader has, And your ReadResponse() method can just be simply as follows:
private async Task<String> ReadResponse()
{
Stream streamIn = socket.InputStream.AsStreamForRead();
StreamReader reader2 = new StreamReader(streamIn);
string response = await reader2.ReadLineAsync();
return response;
}
More details about StreamSocket in uwp please reference the official sample.

Bluetooth Communication from Xamarin to C# Console App

I am developing an Xamarin application (currently focused on Android but will eventually support iOS) that communicates via bluetooth to a nearby C# console application. They both act as a client/server as they need to send messages back and forth.
The read code for the Android implementation is as follows:
byte[] buffer = new byte[1024];
int bytes;
// Listen to the input stream while connected
while (true)
{
try
{
// Read from the InputStream
bytes = this.InStream.Read(buffer, 0, buffer.Length);
var message = new Java.Lang.String(buffer, 0, bytes);
// TODO: Notify the UI of the message
}
catch (Java.IO.IOException e)
{
// TODO: Show some error
}
}
The problem with the above code is that since I am only reading a buffer size of 1024 I am only getting partial messages for anything over 1024 bytes. Using a DataWriter (as shown below in the console application) would be ideal for me, but it is not available for an Android implementation using Xamarin.
The write code for the Android application is as follows:
try
{
this.OutStream.Write(buffer, 0, buffer.Length);
// TODO: Notify the UI
}
catch (Java.IO.IOException e)
{
// TODO: Show some error
}
Now, for the console application the read code is as follows:
// socket here is a Windows.Networking.Sockets.StreamSocket
var reader = new DataReader(socket.InputStream);
while (true)
{
try
{
uint readLength = await reader.LoadAsync(sizeof(uint));
uint currentLength = reader.ReadUInt32();
await reader.LoadAsync(currentLength);
string message = reader.ReadString(readLength);
Console.WriteLine("Message received: " + message);
}
catch (Exception e)
{
// TODO: Show some error
}
}
The problem here, is the the await reader.LoadAsync(currentLength) is never resolved. I am guessing that is because the Android app is only sending the message and not the length, then the message. I can get rid of the second await and use a buffer size of 1024 as I am in the Android application, but I will again be getting partial messages. Simply increasing the buffer size to get a bigger message is another option, but that sounds incorrect to me.
And the write code for the console application:
// socket here is a Windows.Networking.Sockets.StreamSocket
var writer = new DataWriter(socket.OutputStream)
writer.WriteUInt32((uint)message.Length);
writer.WriteString(message);
try
{
await writer.StoreAsync();
}
catch (Exception exception)
{
// TODO: Show some error
}
I am unsure of the changes I need to make to be able to send and receive complete messages back and forth on both applications. As I stated before, the implementation of the console application will need to support iOS bluetooth communication as well.

SSLStream reads invalid data + KB3147458 SSLStream bug (?)

I'm having an issue with SSLStream returning some data when the remote client did not send anything. I am having this issue when the server is listening for a new command.
If the server doesn't receive a new request, the ReadMessage() function should catch an IOException due to the Read timeout of the SSLStream. The problem happens when the sslStream.Read() is executed the second time it seems to read 5 bytes which were not sent by the client. So the problem happens in this sequence:
-> ReadMessage() -> sslstream.Read() -> timeout exception caught as expected
-> ReadMessage() -> sslstream.Read() -> timeout exception NOT caught, 5 bytes read even even though the client did not send anything
-> ReadMessage() -> sslstream.Read() -> timeout exception caught as expected
-> ReadMessage() -> sslstream.Read() -> timeout exception NOT caught, 5 bytes read even though client did not send anything...
and so on..
public void ClientHandle(object obj)
{
nRetry = MAX_RETRIES;
// Open connection with the client
if (Open() == OPEN_SUCCESS)
{
String request = ReadMessage();
String response = null;
// while loop for the incoming commands from client
while (!String.IsNullOrEmpty(request))
{
Console.WriteLine("[{0}] {1}", RemoteIPAddress, request);
response = Execute(request);
// If QUIT was received, close the connection with the client
if (response.Equals(QUIT_RESPONSE))
{
// Closing connection
Console.WriteLine("[{0}] {1}", RemoteIPAddress, response);
// Send QUIT_RESPONSE then return and close this thread
SendMessage(response);
break;
}
// If another command was received, send the response to the client
if (!response.StartsWith("TIMEOUT"))
{
// Reset nRetry
nRetry = MAX_RETRIES;
if (!SendMessage(response))
{
// Couldn't send message
Close();
break;
}
}
// Wait for new input request from client
request = ReadMessage();
// If nothing was received, SslStream timeout occurred
if (String.IsNullOrEmpty(request))
{
request = "TIMEOUT";
nRetry--;
if (nRetry == 0)
{
// Close everything
Console.WriteLine("Client is unreachable. Closing client connection.");
Close();
break;
}
else
{
continue;
}
}
}
Console.WriteLine("Stopped");
}
}
public String ReadMessage()
{
if (tcpClient != null)
{
int bytes = -1;
byte[] buffer = new byte[MESSAGE_SIZE];
try
{
bytes = sslStream.Read(buffer, 0, MESSAGE_SIZE);
}
catch (ObjectDisposedException)
{
// Streams were disposed
return String.Empty;
}
catch (IOException)
{
return String.Empty;
}
catch (Exception)
{
// Some other exception occured
return String.Empty;
}
if (bytes != MESSAGE_SIZE)
{
return String.Empty;
}
// Return string read from the stream
return Encoding.Unicode.GetString(buffer, 0, MESSAGE_SIZE).Replace("\0", String.Empty);
}
return String.Empty;
}
public bool SendMessage(String message)
{
if (tcpClient != null)
{
byte[] data = CreateMessage(message);
try
{
// Write command message to the stream and send it
sslStream.Write(data, 0, MESSAGE_SIZE);
sslStream.Flush();
}
catch (ObjectDisposedException)
{
// Streamers were disposed
return false;
}
catch (IOException)
{
// Error while trying to access streams or connection timedout
return false;
}
catch (Exception)
{
return false;
}
// Data sent successfully
return true;
}
return false;
}
private byte[] CreateMessage(String message)
{
byte[] data = new byte[MESSAGE_SIZE];
byte[] messageBytes = Encoding.Unicode.GetBytes(message);
// Can't exceed MESSAGE_SIZE parameter (max message size in bytes)
if (messageBytes.Length >= MESSAGE_SIZE)
{
throw new ArgumentOutOfRangeException("message", String.Format("Message string can't be longer than {0} bytes", MESSAGE_SIZE));
}
for (int i = 0; i < messageBytes.Length; i++)
{
data[i] = messageBytes[i];
}
for (int i = messageBytes.Length; i < MESSAGE_SIZE; i++)
{
data[i] = messageBytes[messageBytes.Length - 1];
}
return data;
}
The very same ReadMessage(), SendMessage() and CreateMessage() functions are used also by the client to send messages to the server. MESSAGE_SIZE constant is also the same and it's set to 2048.
The problem was that I re-used the SSLStream after a timeout. So I solved the problem just by removing the nRetry variable and set a longer timeout. The related MSDN article says that SSLStream will return garbage after a timeout exception (https://msdn.microsoft.com/en-us/library/system.net.security.sslstream(v=vs.110).aspx):
SslStream assumes that a timeout along with any other IOException when one is thrown from the inner stream will be treated as fatal by its caller. Reusing a SslStream instance after a timeout will return garbage. An application should Close the SslStream and throw an exception in these cases.
Another issue is that Windows update KB3147458 (Windows 10 April's update) changes the something in the behaviour of the Read function. It looks like something in the SSLStream implementation changed and now it returns data in 2 parts, 1 byte and the rest of the bytes every single time. Actually the MSDN document doesn't say that the Read() function will return all the requested bytes in one step and the provided example uses a do-while loop in order to read the exact number of bytes. So I suppose that the Read() function doesn't guarantee to read the exact requested number of bytes all at once, more read iterations might be required.
SSLstream works properly so it's NOT BROKEN. You just need to pay attention and use of a do-while loop and check that all the bytes are read correctly.
I changed the code as shown here to address the bugs I had.
public String ReadMessage()
{
if (tcpClient != null)
{
int bytes = -1, offset = 0;
byte[] buffer = new byte[MESSAGE_SIZE];
try
{
// perform multiple read iterations
// and check the number of bytes received
while (offset < MESSAGE_SIZE)
{
bytes = sslStream.Read(buffer, offset, MESSAGE_SIZE - offset);
offset += bytes;
if (bytes == 0)
{
return String.Empty;
}
}
}
catch (Exception)
{
// Some exception occured
return String.Empty;
}
if (offset != MESSAGE_SIZE)
{
return String.Empty;
}
// Return string read from the stream
return Encoding.Unicode.GetString(buffer, 0, MESSAGE_SIZE).Replace("\0", String.Empty);
}
return String.Empty;
}
With regard to the SslStream returning five bytes on a Read() after a timeout, this is because the SslStream class doesn't gracefully handle any IOException from the underlying stream, and this is documented as previously noted.
SslStream assumes that a timeout along with any other IOException when one is thrown from the inner stream will be treated as fatal by its caller. Reusing a SslStream instance after a timeout will return garbage. An application should Close the SslStream and throw an exception in these cases.
https://msdn.microsoft.com/en-us/library/system.net.security.sslstream(v=vs.110).aspx
However, you can fix the problem by creating a wrapper class that sits between the Tcp NetworkStream and the SslStream which catches and suppresses the harmless timeout exception, with (seemingly) no loss of functionality.
The full code of this is in my answer on a similar thread, here https://stackoverflow.com/a/48231248/8915494
With regard to the Read() method returning only part of the payload on each Read(), your answer already fixes this correctly. While this is "recent" behaviour for SslStream, it is unfortunately expected behaviour for all networking and all code needs to create some form of buffer to store the fragments until you have a complete packet. For example, if your data exceeds 1500 bytes (the maximum packet size for most Ethernet adapters, assuming Ethernet transport), you are very likely to receive the data in multiple parts and have to reassemble it yourself.
Hope this helps

Effective ways for asynchronous game server implementation on C#

I've been developing pet project - framework for MMO servers. Just for skills improvement. There are a lot of tutorials but usually its doesn't contain details.
Using async/await.
async void StartReceive()
{
while (mTcpClient.Connected)
{
var stream = mTcpClient.GetStream();
try
{
//read header
byte[] headerBuffer = new byte[sizeof(int)];
int read = 0;
while (read < sizeof(int))
{
read += await stream.ReadAsync(headerBuffer, 0, sizeof(int) - read).ConfigureAwait(false);
}
//read body
read = 0;
int messageSize = BitConverter.ToInt32(headerBuffer, 0);
byte[] messageBuffer = new byte[messageSize];
while (read < messageSize)
{
read += await stream.ReadAsync(messageBuffer, read, messageSize - read).ConfigureAwait(false);
}
//parse and proccess message
ProcessMessage(messageBuffer);
}
catch (Exception ex)
{
...
}
}
}
async void ProcessMessage(byte[] buffer)
{
var message = await ParseMessageAsync(buffer).ConfigureAwait(false);
if (OnReceived != null)
OnReceived(this, message);
}
Task<IMessage> ParseMessageAsync(byte[] buffer)
{
return Task<IMessage>.Factory.StartNew(() =>
{
var header = MessageHeader.Parser.ParseFrom(buffer);
return MessagingReflection.Descriptor.MessageTypes[header.Type].Parser.ParseFrom(header.Data);
});
}
If my understanding correct, two methods will be generated and called in unknown thread from the pool. The first method includes "read body" and "parse and proccess" parts, the second - "parse and proccess".
It means that when the reading of sizeof(int) is ended, some thread will be free and some other thread will be runned to proceed reading.
Is it better to proceed reading of message body synchronously in
thread where result of reading header was done? (I mean using
synchronous read for body, after asynchronous read for header). In my
case messages should be quite simple and compact. But it's
interesting for any cases.
ProcessMessage runs task which awaiting for Google.Protobuf parsing. Then the OnReceived delegate will be invoked. If a handler are doing some heavy work, the client can disconnect from host. What ways are there for correctly stopping tasks if client was disconnected?
I have two delegates - OnReceived and OnDisconnected. The first called when full message buffer received, the second is called when exception was thrown in StartReceived(). This delegates assined in the same time, but in the catch block the OnDisconnected is always equal to null! I can't understand why (the OnReceived is still not null in this case, but OnDisconnect is gone!). Can someone explain why it's happening?
(Assigning delegates example)
public class ServerTest
{
List<Client> mClients = new List<Client>();
ConectionService mConnectionService = new ConectionService(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5555));
public ServerTest()
{
mConnectionService.OnClientConnected += OnClientConnected;
mConnectionService.Start();
}
public void OnClientConnected(Client client)
{
client.OnDisconnected += OnDisconnected;
client.OnReceived += OnDataReceived;
mClients.Add(client);
}
public void OnDisconnected(Client client)
{
Console.WriteLine("Server: client disconnected");
}
public void OnDataReceived(Client client, IMessage message)
{
var res = new LoginResponce() { Status = true };
client.SendMessage(LoginResponce.Descriptor, res);
}
}

Categories

Resources