Sending some state with first connection in Socket.io - node.js

I am using socket.io and am looking for a way for the client to be able to specify which room it would like to connect to, but I can't figure out a good way to make this happen.
On the server I have:
io.sockets.on 'connection', (socket)->
console.log 'A socket connected.'
socket.join('my_room')
On the client I have:
io = io.connect("http://domain.com")
What I would like to do is get some initial state in the first connection on the client so that the server knows which room it should try and connect the person to.
So something like this on the client:
io = io.connect("http://domain.com", room_1)
Then the server would have some thing like this:
io.sockets.on 'connection', (socket, data)->
console.log 'A socket with connected!'
socket.join(data)
Is this possible to do with room? Is there a better way? I looked into namespaces but rooms more closely fit what I am trying to do.

I agree with drinchev. You should just send the data on the connect event on the client side.
io = io.connect 'http://domain.com'
io.on 'connect', () ->
socket.emit('join-room', room_1)
Another option is to use socket.io namespaces. This will allow you to avoid the round trip, but there are some differences between namespaces and channels and you will have to determine which makes more sense for your app.
io = io.connect 'http://domain.com/room_1'
The accepted answer here has a good rundown of the differences between channels and namespaces. socket.io rooms or namespacing?

Related

I can't get my head around websockets (via socket.io and node.js)

I'm new to websockets/socket.io/node.js. I'm trying to write a card game app, but pretty much all the example tutorials I've found are creating chat applications. So I'm struggling to get my head around the concepts and how they can be applied to my card game.
Keeping it simple, the card game will involve two players. The game involves moving cards around the table. Each player has to see the other player's moves as they happen (hence the need for constant connections). But the opponents cards are concealed to the other.
So two people browse to the same table then click to sit (and play, when both seats are taken). Using
io.on("connection", function(sock){
//socket events in here
});
am I creating the one socket ('io', or 'sock'?) that both clients and the server share, or is that two separate sockets (server/clientA and sever/clientB)? I ask, because I'm struggling to understand what's happening when a message is emitted and/or broadcast. If a client emits a message, is that message sent to both the server and the other client, or just the server? And then, further does it also send the message to itself as well?? It seems as though that's the logic... or what is the purpose of the 'broadcast' method?
From a functional perspective, I need the server to send different messages to each player. So it's not like a chatroom where the server sends the chat to everyone. But if it's one socket that the three of us share (clients and server), how do I manage who sees what? I've read about namespaces, but I'm struggling to work out how that could be used. And if it's two separate sockets, then I can more easily imagine sending different data to the separate clients. But how is that implemented - is that two 'io' objects, or two 'sock' objects?
Finally, I've got no idea if this is the sort of long-winded question that is accepted here, so if it's not, can someone direct me to a forum that discussions can occur? Cheers!
(in case it matters I'm also using Expressjs as the server).
Edit to add:
Part of my confusion is regarding the difference between 'io' and 'sock'. This code eg from the socket.io page is a good example of methods being applied to either of them:
io.on('connection', function(socket){
socket.emit('request', /* */); // emit an event to the socket
io.emit('broadcast', /* */); // emit an event to all connected sockets
socket.on('reply', function(){ /* */ }); // listen to the event
});
WebSocket server side listens for incoming socket connections from clients.
Each client upon connection opens its own socket between him and server. The server is the one that keeps track of all clients.
So once client emits the message server is listening for, the server can do with that message whatever. The message itself can contain information about who is the recipient of that message.
The server can pass the message to everyone or broadcast it to specific user or users based on information your client has sent you or some other logic.
For a card game:
The server listens for incoming connections. Once two clients are connected both of them should emit game ID in which they want to participate. The server can join their sockets in one game(Room) and all of the communication between those two clients can continue in that room. Each time one of the clients passes data to the server, that data should contain info about the recipient.
Here is one simple example that could maybe get you going:
Client side
// set-up a connection between the client and the server
var socket = io.connect();
// get some game identifier
var game = "thebestgameever";
socket.on('connect', function() {
// Let server know which game you want to play
socket.emit('game', game);
});
function makeAMove(move)
{
socket.emit('madeAMove', {move:move, game:game});
}
socket.on('move', function(data) {
console.log('Player made a move', data);
});
Server side
io = socketio.listen(server);
//listen for new connections from clients
io.sockets.on('connection', function(socket) {
// if client joined game get his socket assigned to the game
socket.on('game', function(game) {
socket.join(game);
});
socket.on('madeAMove', function(data){
let game = data.game;
let move = data.move;
io.sockets.in(game).emit('move', move);
});
})

socketIO handshake behavior when connecting to different namespaces

I am having troubles with socketIO (nodejs) and using multiple namespaces. Maybe I misunderstood how to use it, but this is how I understood it should work.
Server side (nodejs)
io.of('/game').on('connection', socket => {
console.log(socket.handshake.query);
console.log('user connected to game socket', socket.id);
})
io.of('/api').on('connection',socket => {
console.log(socket.handshake.query);
console.log('user connected to api socket', socket.id);
}
Client side (browser)
function test (){
gameSocketTest = io.connect('http://localhost:4003/game',{query:{test:'test'}});
apiSocketTest = io.connect('http://localhost:4003/api');
}
The weird thing is that the query (that I only send to the "game" namespace) arrives in both handlers, also the one of the api.
If I inverse the order of the client side code to:
function test (){
apiSocketTest = io.connect('http://localhost:4003/api');
gameSocketTest = io.connect('http://localhost:4003/game',{query:{test:'test'}});
}
the query in the handshake is empty in both handlers server side. So my conclusion is that one way or another the same handshake is used for both connections (however the socket ids are different for both).
Is it "uberhaupt" possible to do what I am trying to do? If so, what am I not understanding with this weird handshake behavior.
In my understanding calling on the client side "io.connect()" should create 2 separate socket io clients, each with their own handshake.
Thanks for any help!
Andries
You must open new connection explicitly as Socket.IO tries to be smart and re-use connections (which causes a lot of connection issues actually) The way around this is use the force new connection option in your io.connect
gameSocketTest = io.connect('http://localhost:4003/game',{query:{test:'test'}});
apiSocketTest = io.connect('http://localhost:4003/api', {'force new connection': true});
From docs
When called, it creates a new Manager for the given URL, and attempts
to reuse an existing Manager for subsequent calls, unless the
multiplex option is passed with false. Passing this option is the equivalent of passing 'force new connection': true

emits from client must be called again in server?

I have a problem with understanding how socket.io and node.js works.
I tried few examples with emitting messages and it worked. What I don't understand is emitting from clients.
I use emit in client, for example: socket.emit('custom_event');, but it doesn't work in other clients unless I add something like this in my server:
var io = require('socket.io').listen(app);
io.sockets.on('connection', function(socket) {
socket.on('custom_event', function() {
socket.emit('custom_event');
});
});
Am I doing something wrong or do I need to always add definition on server side for something that client should be emitting to other clients?
this is not possible, socketio need the server logic between your clients.
You can do:
socket.broadcast.emit
Broadcasting means sending a message to everyone else except for the socket that starts it.
io.sockets.emit
Emits to all, (inc the socket that starts the action.)
But allways from the server
Can you connect client to client via web sockets without touching the server?

How can I get the hostname of a socket?

When I receive a certain event from a connected socket, I have to send a request with as parameter my hostname and port. I was hoping to be able to retrieve this information from the socket object. Unfortunately, there is little documentation on this and I can't seem to be able to find out if and how this is possible.
So, is it possible to do something like this in Socket.io:
io.sockets.on('connection', function(socket){
console.log(socket.manager.server.hostname)
})'
(Or, alternatively: which thinking error am I making here in thinking that this should be possible in the first case?)
try this:
console.log(socket.handshake.headers.host);
split port if necessary:
console.log(socket.handshake.headers.host.split(":").shift());
The accepted answer does not work anymore in 2022.
Today this information can be accessed like this:
const socket = io()
socket.on('connect', () => {
console.log('Socket is connected with', this.socket.io.uri)
})

What's the correct way to pass a Socket.IO socket between modules?

At the moment I have a getter/setter socket module as follows
var socket;
module.exports.getSocket = getSocket;
module.exports.setSocket = setSocket;
function getSocket() {
return socket;
}
function setSocket(sock) {
if (undefined == socket) socket = sock;
}
In app.js I set as follows
var sio = require('./lib/socket');
var io = require('socket.io').listen(app);
io.sockets.on('connection', function (socket) {
sio.setSocket(socket);
});
In my module I use as follows
sio = require('./lib/socket');
socket.getSocket().broadcast.emit(...);
It seems a bit contrived is there a better way to do this?
Edit: I would like a general solution to the problem of firing off a message which is not initiated by the client socket. For example suppose I retrieve stock prices from an external source and wish to fire an event on price update. Since it is not client initiated, how can I get access to the socket? Or alternatively let's say I wish to fire off a socket message in response to a POST request. Once again I'm not sure how I would access the socket.
There are a number of options for sharing the socket, but it would help to know more about what your module is going to do. Without knowing more, I would recommend you just pass the socket to the module via the function you are calling instead of trying to use some shared state.
If you need to use a shared state (you're trying to send messages to specific users triggered by something other than a socket message) then I would recommend sticking with an established session framework and just persist the socket id. You can get the correct socket just using the id.
See this answer for how to use socket.io with sessions: socket.io and session?
Add some more details and I'll revise my answer.
UPDATE
If you are just trying to broadcast to everyone who is connected, you do not need a socket handle. From any module that references socket.io, you can call io.sockets.emit('stuff').

Resources