how to get socketid in socket.io(nodejs) - express

In my nodejs application, i am using socket.io for sockets connection.
I am configuring my server side code like this
socket.io configuration in separate file.
//socket_io.js
var socket_io = require('socket.io');
var io = socket_io();
var socketApi = {};
socketApi.io = io;
module.exports = socketApi;
below is my server.js file in which i am attaching my socket io to the server like this
var socketApi = require('./server/socket_io');
// Create HTTP server.
const server = http.createServer(app);
// Attach Socket IO
var io = socketApi.io;
io.attach(server);
// Listen on provided port, on all network interfaces.
server.listen(port, () => console.log(`API running on localhost:${port}`));
and then i am using socket.io in my game.js file to emit updated user coins like this.
//game.js
var socketIO = require('../socket_io');
function updateUserCoins(userBet) {
userId = mongoose.Types.ObjectId(userBet.user);
User.findUserWithId(userId).then((user) => {
user.coins = user.coins - userBet.betAmount;
user.save((err, updatedUser) => {
socketIO.io.sockets.emit('user coins', {
userCoins: updatedUser.coins,
});
});
})
}
and then in my client side, i am doing something like this,
socket.on('user coins', (data) => {
this.coins = data.userCoins;
});
but with the above implementation, updating coins of any user, updates all user coins at the client side, since all the clients are listening to the same socket user coins.
To solve the above problem, i know that i have to do something like this,
// sending to individual socketid (private message)
socketIO.io.sockets.to(<socketid>).emit('user coins', {
userCoins: updatedUser.coins,
});
but my concern is that how will get <socketid> with my current implementation.

You can get it by listening to connection to your socket.io server :
io.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
Here you have a socket object ( io.on('connection', function (socket) ) and you can get his id with socket.id
So you'll probably need to wrap your code with the connection listener.
Source of the exemple for the connection listener
Socket object description

Related

Express JS Websocket Connection to wss failed without error but still able to communicate

I am using an Express JS to initiate websocket connection. Below is the following configuration that I used.
socket.js
const ip_address = `${process.env.MIX_HTTPS_APP_URL}`;
const socket_port = `${process.env.MIX_EXPRESS_PORT}`;
const URL = ip_address + ":" + socket_port;
export const socket = io(URL, { autoConnect: true });
socket.onAny((event, ...args) => {
// console.log(event, args);
});
index.js
const app = express();
// app settings
/** Create HTTP server. */
const privateKey = fs.readFileSync(env.privateKey, "utf8");
const certificate = fs.readFileSync(env.certificate, "utf8");
const options = {
key: privateKey,
cert: certificate,
};
const server = https.createServer(options, app).listen(port);
/** Create socket connection */
const socketio = new Server(server);
global.io = socketio.listen(server, {
cors: {
origin: env.url,
},
});
global.io.on("connection", WebSockets.connection);
/** Listen on provided port, on all network interfaces. */
/** Event listener for HTTP server "listening" event. */
server.on("listening", () => {
console.log(`Listening on port:: ${env.url}:${port}/`);
});
Though I can emit and listen to socket, I am still getting this error message. How should I solve this so that users wont see this error message when they view the console?
With reference to your example, the listening on port should be on http server, & socket.io should just attach to on new conn.
see doc https://socket.io/docs/v4/server-initialization/#with-an-https-server
Hope this helps.

Market data routing to frontend: Alpaca WebSocket -> Node.js WebSocket -> React Frontend

I'm trying to use the websocket example from:
https://github.com/alpacahq/alpaca-trade-api-js/blob/master/examples/websocket_example_datav2.js
In order to connect to the Alpaca V2 data stream. Currently, my stream is working but I'm trying to route my data to the client side using Server Sent Events. My data flow seems like it should be:
Alpaca Data Stream API -> My Node.js server -> React Frontend.
The issue I have is using the DataStream object in the example in order to route the data to the frontend. Since, with the object alone, I don't have any route to subscribe to via Server Sent Events, does this mean that I should also be using either express, socket.io, or ws? Since the all of the ".on_xyz" methods are defined within the DataStream object, I'm not sure how to set up the endpoint properly to allow my frontend to subscribe to it. If anyone knows how to route this datastream information forward it would be greatly appreciated- I'm particularly trying to work with the .onStockQuote method but any of them is fine! I'm simply trying to use Node as an inbetween router so that I don't have to subscribe directly from the frontend (and not use the sdk), because that limits scalability of the API's use.
"use strict";
/**
* This examples shows how to use tha alpaca data v2 websocket to subscribe to events.
* You should use the alpaca api's data_steam_v2, also add feed besides the other parameters.
* For subscribing (and unsubscribing) to trades, quotes and bars you should call
* a function for each like below.
*/
import express from 'express';
const app = express()
const Alpaca = require("#alpacahq/alpaca-trade-api");
const API_KEY = "XYZ_Key";
const API_SECRET = "XYZ_Secret";
const PORT = 3000;
// Add a new message and send it to all subscribed clients
const addMessage = (req, res) => {
const message = req.body;
// Return the message as a response for the "/message" call
res.json(message);
return ;
};
class DataStream {
constructor({ apiKey, secretKey, feed }) {
this.alpaca = new Alpaca({
keyId: apiKey,
secretKey,
feed,
});
const socket = this.alpaca.data_stream_v2;
socket.onConnect(function () {
console.log("Connected");
socket.subscribeForQuotes(["AAPL"]);
// socket.subscribeForTrades(["FB"]);
// socket.subscribeForBars(["SPY"]);
// socket.subscribeForStatuses(["*"]);
});
socket.onError((err) => {
console.log(err);
});
socket.onStockTrade((trade) => {
console.log(trade);
});
socket.onStockQuote((quote) => {
console.log(quote);
});
socket.onStockBar((bar) => {
console.log(bar);
});
socket.onStatuses((s) => {
console.log(s);
});
socket.onStateChange((state) => {
console.log(state);
});
socket.onDisconnect(() => {
console.log("Disconnected");
});
socket.connect();
// unsubscribe from FB after a second
// setTimeout(() => {
// socket.unsubscribeFromTrades(["FB"]);
// }, 1000);
}
}
app.post("/message", addMessage);
let stream = new DataStream({
apiKey: API_KEY,
secretKey: API_SECRET,
feed: "sip",
paper: false,
});
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
});

Store Socket IO socket.id in Express session

I have a web app using Angular and Express. I have a rest api for database updates and I am also using SocketIO to update the clients in realtime.
I am tracking a list of active socket IDs for each user but now I would like to have access to the clients socket id in my express route so that I can emit a message to all other users (equivalent of socket.broadcast)
I'm trying to store the socket ID in an expression session so I can access it in my route but i've not been able to get it working. In the code below i'm logging my session when the socket connects and this shows the socketio key i've added but in the /jobs request the socketio key is undefined..
My server.ts is something like this:
import * as expsession from 'express-session'
// create Express app variable
const app = express()
// create HTTP server
const server = http.createServer(app);
// set up CORS so it can work Angular application at another domain/port
app.use(cors({
origin: [ "http://localhost:4200" ],
credentials: true
}))
// install session middleware
const session = expsession({
secret: 'random secret',
saveUninitialized: true,
resave: true,
cookie: { secure: false }
});
// run session middleware for regular http connections
app.use(session);
// *** SOCKET IO *** //
const io = require('socket.io')(server);
// run session middleware for socket.io connections
io.use((socket, next) => {
session(socket.request, socket.request.res || {}, next);
});
// when a socket.io connection connects, put the socket.id into the session
// so it can be accessed from other http requests from that client
io.on('connection', (socket) => {
console.log(`socket.io connected: ${socket.id}`);
// save socket.io socket in the session
socket.request.session.socketio = socket.id;
socket.request.session.save();
console.log("socket.io connection, session:\n", socket.request.session);
});
app.get('/jobs', (req, res) => {
const session = req.session;
console.log("JOBS: session at '/jobs':\n", session);
Job.getJobs((err, jobs) => {
if (err) {
res.json({success: false, msg: err});
} else {
res.json(jobs);
}
});
});
I'm also including credentials in my angular service request, e.g.:
this.http.get(api_url + '/jobs', {withCredentials:true}).subscribe((jobs:IJob[]) => {
this.jobs$.next(jobs)
})
When a socket is connected an id is created(socket.id) and by default it joins to a room with that id. If you already have id for your client just send it to the server and join to a room with the client id.
socket.leave(socket.id);//leaving default room
socket.join(my_custom_id);//joining to custom id room
the custom id will appear in client side as the websocket7 id.
If you still don't know how to get the id in your express route; use a jsonwebtoken with the id and decoded in your route, thats it.
Example:
var bearer = req.headers.authorization.split(" ")[1];//this is node, get headers with express way
var auth_data = jwt.decodeToken( bearer );
decoding:
var jwt = require('jsonwebtoken');
var secret = 'your_secret';
function decodeToken(token){
try {
var decoded = jwt.verify( token , secret );
return decoded;
} catch (error) {
console.log('jwt', error);
return null;
}
}
encoding:
//data is an object with data you want to encode, id, etc
function createJsonWebToken(data){
var token = jwt.sign( data, secret );
return token
}
/*after encoding you send this token to the
client, then you send it back to the server
to the routes you need the data, then just
decoded it in that route*/

WebRTC works locally, but not over Internet [duplicate]

This question already has answers here:
How do you get around NATs using WebRTC without a TURN server?
(2 answers)
Closed 2 years ago.
I'm trying to get a simple proof of concept WebRTC app going. I've got code that works on the local network, but not over the Internet.
I believe I've got a bug outlined here, but I can't figure it out.
server.js (expressjs)
require('dotenv').config();
const express = require('express');
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http);
app.use(express.static('public'));
io.on('connection', (socket) => {
socket.on('join', () => socket.broadcast.emit('join', socket.id));
socket.on('offer', (id, sdp) => socket.to(id).emit('offer', socket.id, sdp));
socket.on('answer', (id, sdp) => socket.to(id).emit('answer', socket.id, sdp));
socket.on('candidate', (id, candidate) => socket.to(id).emit('candidate', socket.id, candidate));
});
const port = process.env.PORT || 443;
http.listen(port, () => console.log("listening on :" + port));
client.js (built w/ webpack)
import io from 'socket.io-client';
console.log('getting video stream');
navigator.mediaDevices.getUserMedia({ video: true }).then(myStream => {
const socket = io.connect();
console.log('joining');
socket.emit('join');
let connections = {};
socket.on('join', async peer_id => {
const connection = newConnection(peer_id);
console.log('creating offer');
const sdp = await connection.createOffer();
console.log('setting local desc');
await connection.setLocalDescription(sdp);
console.log('sending offer');
socket.emit('offer', peer_id, connection.localDescription);
});
socket.on('offer', async (peer_id, remoteDescription) => {
const connection = newConnection(peer_id);
console.log('setting remote desc');
await connection.setRemoteDescription(remoteDescription);
console.log('creating answer');
const sdp = await connection.createAnswer();
console.log('setting local desc');
await connection.setLocalDescription(sdp);
console.log('sending answer');
socket.emit('answer', peer_id, connection.localDescription);
});
socket.on('answer', (peer_id, remoteDescription) => {
console.log('setting remote desc');
connections[peer_id].setRemoteDescription(remoteDescription);
});
socket.on('candidate', (peer_id, candidate) => {
console.log('adding candidate');
connections[peer_id].addIceCandidate(candidate);
});
function newConnection(peer_id) {
console.log('creating connection');
const connection = new RTCPeerConnection(
{ iceServers: [{ urls: ['stun:stun.l.google.com:19302'] }] }
);
connections[peer_id] = connection;
connection.onconnectionstatechange = () => console.log('connection state', connection.connectionState);
console.log('searching for candidates');
connection.onicecandidate = event => {
if (event.candidate) {
console.log('sending candidate');
socket.emit('candidate', peer_id, event.candidate);
} else {
console.log('all candidates found');
}
};
console.log('listening for tracks');
connection.ontrack = event => {
console.log('track received');
const stream = new MediaStream();
stream.addTrack(event.track);
document.getElementById('video').srcObject = stream;
};
for (const track of myStream.getTracks()) {
console.log('sending track');
connection.addTrack(track);
}
return connection;
}
});
Logs from local network peers (connected)
first peer
getting video stream
joining
creating connection
searching for candidates
listening for tracks
sending track
creating offer
setting local desc
sending offer
(2) sending candidate
setting remote desc
adding candidate
track received
connection state connecting
all candidates found
connection state connected
second peer
getting video stream
joining
creating connection
searching for candidates
listening for tracks
sending track
setting remote desc
track received
creating answer
setting local desc
sending answer
sending candidate
connection state connecting
all candidates found
connection state connected
(2) adding candidate
Logs from peers over the Internet (failed)
first peer
getting video stream
joining
creating connection
searching for candidates
listening for tracks
sending track
creating offer
setting local desc
sending offer
(3) sending candidate
all candidates found
setting remote desc
track received
connection state connecting
(5) adding candidate
connection state failed
second peer
getting video stream
joining
creating connection
searching for candidates
listening for tracks
sending track
setting remote desc
track received
creating answer
setting local desc
sending answer
(5) sending candidate
(3) adding candidate
connection state connecting
connection state failed
all candidates found
WebRTC isn't going to work on all connection pairings unless you use TURN.
You will see some peers able to connect though, you can read about all the cases that matter in my answer here

Open socket on dynamic endpoint

I am using loopback to build a route:
/users/{id}
where users can listen for new information pertaining to their account on their own endpoint.
I know I need the socket.io package but I'm not sure what to do with it. How can I open a socket on this dynamic endpoint in the function:
#get('/users/{id}', {
responses: {
'200': {
description: 'User socket',
content: {'application/json': {schema: {'x-ts-type': User}}},
},
},
})
async updateUser(#param.path.string('id') userId: typeof User.prototype.id)
: Promise<boolean> {
\\ Open socket here
console.log(userId)
return true;
}
if I do this:
const express = require("express");
const app = express();
const server = require("http").createServer(app);
const io = require("socket.io").listen(server);
const port = 3000;
io.on("connection", socket => {
console.log("User has connected!");
});
It doesn't open a socket on the dynamic endpoint that I want it to.
I am using loopback-4 for the backend and react-native for the front-end.