Websocket server: onopen function on the web socket is never called
Probably it's an encoding issue. Here's a working C# server I wrote:
class Program
{
static void Main(string[] args)
{
var listener = new TcpListener(IPAddress.Loopback, 8181);
listener.Start();
using (var client = listener.AcceptTcpClient())
using (var stream = client.GetStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
{
writer.WriteLine("HTTP/1.1 101 Web Socket Protocol Handshake");
writer.WriteLine("Upgrade: WebSocket");
writer.WriteLine("Connection: Upgrade");
writer.WriteLine("WebSocket-Origin: http://localhost:8080");
writer.WriteLine("WebSocket-Location: ws://localhost:8181/websession");
writer.WriteLine("");
}
listener.Stop();
}
}
And the corresponding client hosted on localhost:8080
:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript">
var socket = new WebSocket('ws://localhost:8181/websession');
socket.onopen = function() {
alert('handshake successfully established. May send data now...');
};
socket.onclose = function() {
alert('connection closed');
};
</script>
</head>
<body>
</body>
</html>
This example only establishes the handshake. You will need to tweak the server in order to continue accepting data once the handshake has been established.
How can I guarantee that websocket onopen to be called
Because of the single-threaded event driven nature of Javascript, what you describe will not happen in real code. The "open" event can't be triggered until after your current section of Javascript finishes. Thus, you will always be able to set your onopen
event handler before the event occurs.
Inserting artificial pauses in the debugger or in the console is an artificial situation that does not occur in real code.
What happens in real code is this:
- You call
new WebSocket(uri)
- The webSocket intrastructure initiates a webSocket connection (an asynchronous operation)
- The webSocket constructor returns immediately before that connection has completed.
- The rest of your code runs (including assigning the
.onopen
property and setting up your event handler. - The Javascript interpreter is done running your code and returns back to the event loop.
- If, by now, the webSocket has connected, then there will be an
open
event in the event queue and Javascript will trigger that event, resulting in your.onopen
handler getting called. - If the
open
event has not yet completed, Javascript will wait for the next event to be inserted into the event queue and will run it, repeating that process over and over. Eventually one of these events will be youropen
event.
The key to this is that .onopen
is called via an asynchronous event. Thus, it has to go through the Javascript event queue. And, no events from the event queue can be run until after your current section of Javascript finishes and returns back to the interpreter. That's how the "event-driven" nature of Javascript works. So, because of that architecture, you cannot miss the onopen
event as long as you install your .onopen
handler in the same section of Javascript that calls the constructor.
If it provides you any comfort, there are dozens of APIs in node.js that all rely on this same concept. For example when you create a file stream with fs.createReadStream(filename)
, you have to create the stream, then add event handlers (one of those event handlers is for an open
event). The same logic applies there. Because of the event-driven nature of Javascript, there is no race condition. The open
event or the error
event cannot be triggered before the rest of your Javascript runs so you always have an opportunity to get your event handlers installed before they could get called.
In cases where errors could be detected synchronously (like a bad filename or bad uri) and could trigger an error
event immediately, then code using something like setImmediate(function() { /* send error event here*/ })
to make sure the error event is not triggered until after your code has a chance to install your event handlers.
Javascript Websocket OnOpen not being called on IOS
I figured it out. When I create the headers to send back to the browser from the server, I used "httpResponse.concat("\r\n\r\n")" to add line breaks at the end of the headers, which simply did not work. Changing it to "httpResponse = httpResponse + "\r\n\r\n";" does work.
Identify if Socket server is running from client side
There is apparently no way to detect if socket server is running or not.
Not specifically, by design. Differentiating why the connection fails is a security risk, per the WebSocket interface spec:
https://websockets.spec.whatwg.org/#feedback-from-the-protocol
User agents must not convey any failure information to scripts in a way that would allow a script to distinguish the following situations:
...
In all of these cases, the WebSocket connection close code would be 1006, as required by WebSocket Protocol. [WSP]
Allowing a script to distinguish these cases would allow a script to probe the user’s local network in preparation for an attack.
However, as you stated it in your question:
socket.onopen creates a fetal error
Except that you got one detail wrong. The onopen
event is not fired if the connection fails. The onclose
event is fired instead, as stated in the WebSockets interface spec:
https://websockets.spec.whatwg.org/#eventdef-websocket-open
If the "establish a WebSocket connection" algorithm fails, it triggers the "fail the WebSocket connection" algorithm, which then invokes the "close the WebSocket connection" algorithm, which then establishes that "the WebSocket connection is closed", which fires the close event
This matches up with the WebSocket protocol spec:
https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.4
7.1.4. The WebSocket Connection is Closed
When the underlying TCP connection is closed, it is said that The WebSocket Connection is Closed and that the WebSocket connection is in the CLOSED state. If the TCP connection was closed after the WebSocket closing handshake was completed, the WebSocket connection is said to have been closed cleanly.
If the WebSocket connection could not be established, it is also said that The WebSocket Connection is Closed, but not cleanly.
ReadyState
0 is CONNECTING
. So, if you get an onclose
event in this state (where event.wasClean
should be false and event.code
should be 1006), you know the connection was unsuccessful.
In addition, per the interface spec, you should also get an onerror
event, despite your claim that you are not. The interface spec is very clear on the client's responsibility on this matter:
https://websockets.spec.whatwg.org/#feedback-from-the-protocol
When the WebSocket connection is closed, possibly cleanly, the user agent must queue a task to run the following substeps:
- Change the ready state to
CLOSED
(3).- If the user agent was required to fail the WebSocket connection, or if the WebSocket connection was closed" after being flagged as full, fire an event named
error
at the WebSocket object. [WSP]- Fire an event named
close
at the WebSocket object, usingCloseEvent
, with thewasClean
attribute initialized to true if the connection closed cleanly and false otherwise, thecode
attribute initialized to the WebSocket connection close code, and thereason
attribute initialized to the result of applying UTF-8 decode without BOM to the WebSocket connection close reason. [WSP]
Websocket Server Issue: onOpen hit client side, but server websocket handler OnOpen and other events never hit
To be honest, I cant say with 100% certainty what fixed this, but I have a very strong suspicion.
I was including too many namespaces in my code and I believe there was some confusion in the compiler etc when it actually ran. Apparently both Microsoft.Web.Websockets and SignalR's namespaces BOTH contain WebSocketHandler. While I dont know all the ins and outs of SignalR, it looks like WebSocketHandler in THAT namespace is not meant to be used outside of SignalR. I believe that class was being referenced instead of the one in Microsoft.Web.Websockets, as it works now, but I really didn't change too much. It also let me directly call the constructor by instantiating a new instance of the handler instead of having to call it in a lambda function.
Anyway, If anyone else has similar issues, make sure you're referencing the right one!
How can I make sure a Websocket does not open before attaching an onopen handler?
JavaScript implementation in browsers is asynchronous and single threaded. Of course, it can use multiple worker threads inside (for example, for input/output operations or timers), but your application code is executing on one thread with event loop.
When you connect to the server through WebSocket:
var socket = new WebSocket('ws://localhost:8080');
JavaScript thread starts asynchronous operation for connecting socket and immediately continues to run next code. It can receive events from asynchronous operations only when it returns to the event loop. That means, that your onOpen
listener will be always fired, no matter how quickly connection will be established.
I also recommend you to check this question:
How does Asynchronous programming work in a single threaded programming model?
You'll find there a little bit better and extended explanations. Note, that V8 JavaScript engine is used by Node.js and Chromium.
Related Topics
Properties VS. Fields: Need Help Grasping the Uses of Properties Over Fields
Must Declare Scalar Variable @Id
Sorting a List Using Lambda/Linq to Objects
Xml Serialize Generic List of Serializable Objects
Why Does C# Xmldocument.Loadxml(String) Fail When an Xml Header Is Included
JSON Convert Empty String Instead of Null
Splitting a String/Number Every Nth Character/Number
Cannot Step into .Net Framework Source Code
How to Get a Human-Readable File Size in Bytes Abbreviation Using .Net
How to Reference Generic Classes and Methods in Xml Documentation
Why Should I Use Int Instead of a Byte or Short in C#
C#: Waiting for All Threads to Complete
Linq Ring: Any() VS Contains() for Huge Collections
How to Invoke an Extension Method Using Reflection
How to Use a Reserved Keyword as an Identifier in My JSON Model Class