Background: External factors force me to implement a simple websocket client in C# and I would like to keep the number of third party dependencies low. So it would be nice to stick with System.Net.WebSockets if possible.
Objective: Send a series of JSON objects as individual messages to a websocket server. Throw an exception if sending fails with any reasonable timeout. Throw an exception if the underlying TCP connection breaks between new objects becoming available.
Sending code:
// ws is an instance of System.Net.WebSockets.ClientWebSocket
ArraySegment<byte> json = new ArraySegment<byte>
Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data)));
if (!ws.SendAsync(json, WebSocketMessageType.Binary, true,
CancellationToken.None).Wait(10000))
{
throw new Exception("Error: Websocket send timeout");
}
Console.Write("Sent")
My problem with this code is that when I see the client program printing "Sent", the server will not receive that message until several more invocations of SendAsync. My impression is that it is stuck in some kind of internal message buffer until the buffer spills over.
Similarly, breaking the underlying TCP connection does not result in the next SendAsync throwing an exception until several messages later.
Checking if websocket is alive without sending:
The keepalive is set to the default value 30s. Yet I have never seen ws.State return anything but WebSocketState.Open, even minutes after disconnecting the Server if I don't have any messages to send in between.
Am I fundamentally confused about how ClientWebSocket is supposed to be used, does it simply not work, or did I miss some detail in the documentation?
You should await your async method in a Task.Run :
Task.Run(() => {
if (!await ws.SendAsync(json, WebSocketMessageType.Binary, true,
CancellationToken.None))
{
throw new Exception("Error: Websocket send timeout");
}
Console.Write("Sent");
} ).Wait(10000);
Related
I am testing how .NET WebSockets work when the client can't process data from the server side fast enough. For this purpose, I wrote an application that sends data continuously to a WebSocket, but includes an artificial delay in the receive loop. As expected, once the TCP window and other buffers fill, the SendAsync calls start to take long to return. But after a few minutes, one of these exceptions is thrown by SendAsync:
System.Net.HttpListenerException: The device does not recognize the command
System.Net.HttpListenerException: The I/O operation has been aborted because of either a thread exit or an application request.
What's weird is, that this only happens with certain message sizes and certain timing. When the client is allowed to read all data unrestricted, the connection is stable. Also, when the client is blocked completely and does not read at all, the connection stays open.
Examining the data flow through Wireshark revealed that it is the server that is resetting the TCP connection while the client's TCP window is exhausted.
I tried to follow this answer (.NET WebSockets forcibly closed despite keep-alive and activity on the connection) without success. Tweaking the WebSocket keep alive interval has no effect. Also, I know that the final application needs to be able to handle unexpected disconnections gracefully, but I do not want them to occur if they can be avoided.
Did anybody encounter this? Is there some timeout tweaking that I can do? Running this should produce the error between a minute and half to three minutes:
class Program
{
static void Main(string[] args)
{
System.Net.ServicePointManager.MaxServicePointIdleTime = Int32.MaxValue; // has no effect
HttpListener httpListener = new HttpListener();
httpListener.Prefixes.Add("http://*/ws/");
Listen(httpListener);
Thread.Sleep(500);
Receive("ws://localhost/ws/");
Console.WriteLine("running...");
Console.ReadKey();
}
private static async void Listen(HttpListener listener)
{
listener.Start();
while (true)
{
HttpListenerContext ctx = await listener.GetContextAsync();
if (!ctx.Request.IsWebSocketRequest)
{
ctx.Response.StatusCode = (int)HttpStatusCode.NotImplemented;
ctx.Response.Close();
return;
}
Send(ctx);
}
}
private static async void Send(HttpListenerContext ctx)
{
TimeSpan keepAliveInterval = TimeSpan.FromSeconds(5); // tweaking has no effect
HttpListenerWebSocketContext wsCtx = await ctx.AcceptWebSocketAsync(null, keepAliveInterval);
WebSocket webSocket = wsCtx.WebSocket;
byte[] buffer = new byte[100];
while (true)
{
await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Binary, true, CancellationToken.None);
}
}
private static async void Receive(string serverAddress)
{
ClientWebSocket webSocket = new ClientWebSocket();
webSocket.Options.KeepAliveInterval = TimeSpan.FromSeconds(5); // tweaking has no effect
await webSocket.ConnectAsync(new Uri(serverAddress), CancellationToken.None);
byte[] receiveBuffer = new byte[10000];
while (true)
{
await Task.Delay(10); // simulate a slow client
var message = await webSocket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
if (message.CloseStatus.HasValue)
break;
}
}
I'm not a .NET developer but as far as I have seen these kind of problems in websocket topic and in my own opinion, these can be the reasons:
Very short timeout setting on websocket on both sides.
Client/Server side runtime exceptions (beside of logging, must check onError and onClose methods to see why)
Internet or connection failures. Websocket sometimes goes into IDLE mode too. You have to implement a heartbeat system on websockets to keep them alive. Use ping and pong packets.
check maximum binary or text message size on server side. Also set some buffers to avoid failure when message is too big.
As you said your error usually happens within a certain time, 1 and 2 must help you. Again sorry if I cant provide you codes, but I have had same problems in java and I found out these are the settings that must be set in order to work with websockets. Search how to set these in your client and server implementations and you must be fine after that.
Apparently, I was hitting an HTTP.SYS low speed connection attack countermeasure, as roughly described in KB 3137046 (https://support.microsoft.com/en-us/help/3137046/http-sys-forcibly-disconnects-http-bindings-for-wcf-self-hosted-servic):
By default, Http.sys considers any speed rate of less than 150 bytes per second as a potential low speed connection attack, and it drops the TCP connection to release the resource.
When HTTP.SYS does that, there is a trace entry in the log at %windir%\System32\LogFiles\HTTPERR
Switching it off was simple from code:
httpListener.TimeoutManager.MinSendBytesPerSecond = UInt32.MaxValue;
I've tried checking the server:port with telnet and I'm getting the expected results. So either writer.Write() or reader.ReadLine() isn't working cause I get nothing from the server.
TcpClient socket = new TcpClient(hostname, port);
if (!socket.Connected) {
Console.WriteLine("Failed to connect!");
return;
}
TextReader reader = new StreamReader(socket.GetStream());
TextWriter writer = new StreamWriter(socket.GetStream());
writer.Write("PING");
writer.Flush();
String line = null;
while ((line = reader.ReadLine()) != null) {
Console.WriteLine(line);
}
Console.WriteLine("done");
EDIT: I might have found the issue. This code was based off examples I found on the web. I tried another irc server: open.ircnet.net:6669 and I got a response:
:openirc.snt.utwente.nl 020 * :Please wait while we process your connection.
It seems as if I probably need to run the reader in a Thread so it can just constantly wait for a response. However it does seem weird that the program got caught on the while loop without ever printing done to the console.
I think you need to provide further details. I'm just going to assume that because you can easily telnet to the server using the same port your problem lies in the evaluation of the Connected property...
if (!socket.Connected) {
Console.WriteLine("Failed to connect!");
return;
}
this is wrong because Microsoft clearly specifies in the documentation that the Connected property is not reliable
Because the Connected property only reflects the state of the connection as of the most recent operation, you should attempt to send or receive a message to determine the current state. After the message send fails, this property no longer returns true. Note that this behavior is by design. You cannot reliably test the state of the connection because, in the time between the test and a send/receive, the connection could have been lost. Your code should assume the socket is connected, and gracefully handle failed transmissions.
That said, you should not use this property to determine the state of the connection. Needless to say that using this property to control the flow of your console app will result in unexpected results.
Suggestion
Remove the evaluation of the Connected property
Wrap your GetStream and Write method calls in a try/catch block to handle network communication errors
reader.ReadLine() will just wait for any data to arrive. If no data arrive, it seems to hang. That's a feature of tcp (I don't like it either). You need to find out how the end of the message is defined and stop based on that end criterion. Be careful, the end of message identifier may be split into two or more lines...
RFC for ping says that the server may not respond to it & such connections has to be closed after a time. Please check the RFC: https://www.rfc-editor.org/rfc/rfc1459#section-4.6.2
So I've been getting this exception for about a week now, and I've finally managed to corner it into a code snippet that can be easily read.
As a background, I am programming an app for Windows RT and I am trying to use basic sockets.
For the sake of testing, I've created a local socket listener to act as a server. Both the server and client need to be able to read/write on the socket.
Neither the client nor the server can (or should) know how much data will come across the wire (if any). This is an absolute requirement. The server should be able to process an arbitrary amount of data on demand.
Here is an example. It is presented as a Unit Test, simply because that is where I consistently encounter the error. Removing any single line from this example causes the error to go away:
[TestMethod]
public async Task TestSomething()
{
// Setup local server
//
StreamSocketListener listener = new StreamSocketListener();
listener.ConnectionReceived += async (sender, args) =>
{
DataReader serverReader = new DataReader(args.Socket.InputStream);
await serverReader.LoadAsync(4096); // <-- Exception on this line
};
await listener.BindServiceNameAsync("10181");
// Setup client
//
using (StreamSocket socket = new StreamSocket())
{
await socket.ConnectAsync(new HostName("localhost"), "10181");
DataReader reader = new DataReader(socket.InputStream);
Task readTask = Listen(reader);
}
}
public async Task Listen(DataReader reader)
{
await reader.LoadAsync(4096);
}
The exception happens on the line where the server calls LoadAsync(...), and the exception is thrown when the unit test quits.
The exception is (seemingly) simple:
An existing connection was forcibly closed by the remote host.
(Exception from HRESULT: 0x80072746)
Any clues would be greatly appreciated.
With the new WinRT socket types, it's easier than ever to program sockets correctly, but make no mistake: they are still complex beasts.
The "forcibly closed" (WSAECONNRESET / 10054) error is when the remote side (in this case, the client) aborts its connection, which it does by disposing its StreamSocket. This is reported as an error but is not uncommon and should be handled gracefully. I.e., if the server has sent all its data and is just waiting to receive more (optional) data, then it should treat WSAECONNRESET as a regular close.
Tip: If you pass Exception.HResult to SocketError.GetStatus, you should see it's SocketErrorStatus.ConnectionResetByPeer. That way you can avoid magic values in your error handling code.
P.S. I have a blog post describing some of the more common socket errors and socket error handling in general.
I am writing a socket server and I noticed that if it received an empty socket buffer, it will cause a socketexception. In the receive call, how can I detect and handle the empty buffer and send a -1 response back to the client before the socket closes?
Code:
try
{
byte[] byteBuffer = new Byte[1024];
int size = m_clientSocket.Receive(byteBuffer);
if (size > 0)
{
ParseReceiveBuffer(byteBuffer, size);
}
else
{
m_clientSocket.Send(BitConverter.GetBytes(-1));
}
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.WouldBlock ||
ex.SocketErrorCode == SocketError.IOPending ||
ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
{
// socket buffer is probably empty, wait and try again
Thread.Sleep(1000);
}
// connection was unexpectively closed
}
Assuming you are working with TCP layer.
Sockets functions like Send and Receive are not actually sending or receiving data over network. They communicate with OS sockets layers to push or pop data from networking layer.
Due to this, when you run from your application method Send, it will put in queue data to send into OS networking layer. Only after that OS will actually send it and might get some issues, so that way it will store error and will throw it straight on next that specific socket method call from your application.
That is why you receive SocketException only on next call when previous might was failed or in between previous and current call network had some error (for example connection lose).
When exception is going to be received you will always receive nothing (zero bytes).
You cannot send anything after exception, due to connection lose (in most (99%) cases).
Client will receive exception as well that will have Error message about connection lose.
using DataAvailable clause in appropriate use.
i recommend you write complete class and helper methods for doing all of these regular.
and thread.sleep(1000) is not necessary since listener will wait for your command line to be executed.
another approach is using developed socket library that you can easily simulate both server and client roles.i know supersocket with that you can customize and develop your own protocol.
i am trying to disconnect a client from a server but the server still sees it as being connected. I cant find a solution to this and Shutdown, Disconnect and Close all dont work.
Some code for my disconnect from the client and checking on the server:
Client:
private void btnDisconnect_Click(object sender, EventArgs e)
{
connTemp.Client.Shutdown(SocketShutdown.Both);
connTemp.Client.Disconnect(false);
connTemp.GetStream().Close();
connTemp.Close();
}
Server:
while (client != null && client.Connected)
{
NetworkStream stream = client.GetStream();
data = null;
try
{
if (stream.DataAvailable)
{
data = ReadStringFromClient(client, stream);
WriteToConsole("Received Command: " + data);
}
} // So on and so on...
There are more writes and reads further down in the code.
Hope you all can help.
UPDATE: I even tried passing the TCP client by ref, assuming there was a scope issue and client.Connected remains true even after a read. What is going wrong?
Second Update!!:
Here is the solution. Do a peek and based on that, determine if you are connected or not.
if (client.Client.Poll(0, SelectMode.SelectRead))
{
byte[] checkConn = new byte[1];
if (client.Client.Receive(checkConn, SocketFlags.Peek) == 0)
{
throw new IOException();
}
}
Here is the solution!!
if (client.Client.Poll(0, SelectMode.SelectRead))
{
byte[] checkConn = new byte[1];
if (client.Client.Receive(checkConn, SocketFlags.Peek) == 0)
{
throw new IOException();
}
}
From the MSDN Documentation:
The Connected property gets the
connection state of the Client socket
as of the last I/O operation.
When it
returns false, the Client socket was
either never connected, or is no
longer connected. Because the
Connected property only reflects the
state of the connection as of the most
recent operation, you should attempt
to send or receive a message to
determine the current state. After the
message send fails, this property no
longer returns true. Note that this
behavior is by design. You cannot
reliably test the state of the
connection because, in the time
between the test and a send/receive,
the connection could have been lost.
Your code should assume the socket is
connected, and gracefully handle
failed transmissions.
I am not sure about the NetworkStream class but I would think that it would behave similar to the Socket class as it is primarily a wrapper class. In general the server would be unaware that the client disconnected from the socket unless it performs an I/O operation on the socket (a read or a write). However, when you call BeginRead on the socket the callback is not called until there is data to be read from the socket, so calling EndRead and getting a bytes read return result of 0 (zero) means the socket was disconnected. If you use Read and get a zero bytes read result I suspect that you can check the Connected property on the underlying Socket class and it will be false if the client disconnected since an I/O operation was performed on the socket.
It's a general TCP problem, see:
How do I check if a SSLSocket connection is sane on Java?
Java socket not throwing exceptions on a dead socket?
The workaround for this tend to rely on sending the amount of data to expect as part of the protocol. That's what HTTP 1.1 does using the Content-Length header (for a entire entity) or with chunked transfer encoding (with various chunk sizes).
Another way is to send "NOOP" or similar commands (essentially messages that do nothing but make sure the communication is still open) as part of your protocol regularly.
(You can also add to your protocol a command that the client can send to the server to close the connection cleanly, but not getting it won't mean the client hasn't disconnected.)