Socket.io, and WebSockets in general, require an http server for the initial upgrade handshake. So even if you don’t supply Socket.io with an http server it will create one for you.
The issue is that the second parameter in your io.listen(3000, '0.0.0.0')
is ignored by Socket.io. If you need to control which network interface to listen on, you should use the code in your last snippet as it is essentially what Socket.io does behind the scenes.
var http = require('http').createServer().listen(3000, '0.0.0.0');
var io = require('socket.io').listen(http);
WebSocket connections are initiated by the client performing a Protocol Upgrade request over the regular HTTP protocol, which is why an HTTP server is required.
Socket.io on the other hand is a bit different than other WebSocket servers. It uses Engine.IO under the hood, which is an implementation with goals of being reliable. It defaults to emulating a WebSocket connection via XHR / JSONP polling (regular HTTP request-response happening periodically), but upgrades the connection to a WebSocket connection if it is possible. The reason for that is because their research has found that various firewalls, proxies and anti-virus software does in fact block WebSocket connections, and their Engine.IO implementation hides this fact to you without having to implement a fallback solution in case the WebSocket connection is blocked.