My server codes use io.to(socketId).emit('xxx');
to a message to a particular socket, refer to https://socket.io/docs/emit-cheatsheet/
But when the connection is bad and the client disconnects and connects again, socketId changes and io.to(socketId).emit('xxx');
then fails. So how do I implement a re-emit method to handle the reconnection ?
My question is kind of opposite to this one, socket.io stop re-emitting event after x seconds/first failed attempt to get a response, from it I learn that socket.io client can re-emit when it reconnects. But the server code seems to lack that support.
— update —
As the comment said I show here "the code context where you’re saving the socketId value that gets stale."
First, each user has a unique id in our system, when socket connects the client will send a login message so I save the userId and socketId pair. When I get disconnect event I clear that info.
Second, the user will get messages through redis pubsub (from other service), and I will send that message to the client throught the socketid I record. So when socket connects I will subscribe a channel identified by userid and unsubscribe it in disconnect event.
Third, my problem happens when I get redis pub event while socketId value gets stale. Actually the value of socketid is the latest one but the socket may fail to send the message.
io.on('connection', async function (socket) {
socket.on('login', async function (user_id, fn) {
...
await redis
.multi()
.hset('websocket:socket_user', socket.id, user_id)
.hset(
`websocket:${user_id}`,
'socketid',
socket.id
)
.exec()
await sub.subscribe(user_id)
...
}
socket.on('disconnect', async function (reason) {
let user_id = await redis.hget('websocket:socket_user', socket.id)
await redis.del(`websocket:${user_id}`)
sub.unsubscribe(user_id)
...
}
//Here is what problem happens when socket is trying to reconnect
//while I got redis pub message
sub.on('message', async function (channel, message) {
let socketid = await redis.hget(`websocket:${channel}`, 'socketid')
if (!socketid) {
return
}
//so now I assume the socketid is valid,
//but it turns out clients still complain they didn't receive message
io.to(socketid).emit('msg', message) //When it fails how do I re-emit?
})
3
Answers
I can't find any existing solution for that so this is what I come up with:
How to record the failed message is another task I find socket.io doesn't support and this is what I do,
io.to(socketId).emit('xxx');
because "acknowledgements are not supported when emitting from namespace." so I need to first get socket object from id usingio.sockets.connected[socketid]
The codes to record failed one are somewhat like this
If someone can come up with a better solution I am all ears! Thanks.
You can generate custom socket.id for your socket clients by overwriting default method to generate socket id. in this case socket.id will be known for you and can be re-emit when same id comes online.
//The function is called with a node request object (http.IncomingMessage) as first parameter.
As per socket.io docs
I would suggest you use your unique id as a room, that is the best solution I come up with, no matter how many times the user is joined the user will be joined in a single room only(with their unique id).
see my answer on
NodeJs Socket.io Rooms