I cannot get to broadcast an event to a specific room.
I check the clients that are in the room, and they seem to have properly join the room.
However socket.broadcast.to(socket.roomChannel).emit("cardSelected", cardInfo)
is simply not working,
whereas socket.broadcast.emit("cardSelected", cardInfo) works (but I need to broadcast only to the room).
This is driving me insane...
Here is my code, the part that is not working is all the way to the bottom:
// Setup basic express server
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var port = '4000';
server.listen(port, function () {
});
this.rooms = [];
let lastRoomId = 0
let lastPlayerId = 0
io.on('connection', function (socket) {
// send list of rooms at connect
socket.emit("connected", {rooms: this.rooms});
socket.id = ++lastPlayerId;
// Room
socket.on('createRoom', (roomName) => {
const room = {
id: ++lastRoomId,
name: roomName,
players: [],
}
this.rooms.push(room);
socket.emit("roomCreated", room);
socket.broadcast.emit("update_rooms", {rooms: this.rooms});
});
socket.on('joinRoom', (id) => {
const index = this.rooms.map(room => room.id).indexOf(id);
if (index >= 0) {
this.rooms[index].players.push(socket.id);
socket.emit("roomJoined", this.rooms[index]);
socket.roomId = id;
socket.roomChannel = this.rooms[index].name;
socket.join(socket.roomChannel);
socket.broadcast.emit("update_rooms", {rooms: this.rooms});
}
});
// Game
socket.on('cardSelected', (cardInfo) => {
console.log("broadcasting to", socket.roomChannel);
console.log("socket room", socket.rooms);
var clients = io.sockets.adapter.rooms[socket.roomChannel].sockets;
// Shows that the clients ARE in the room
// returns "{ '1': true, '2': true }" if I have two clients
console.log(clients);
// DOESNT WORK!!
socket.broadcast.to(socket.roomChannel).emit("cardSelected", cardInfo)
// DOESNT WORK!!
io.to(socket.roomChannel).emit("cardSelected");
// Works
socket.broadcast.emit("cardSelected", cardInfo);
});
});
I also tried socket.to(socket.roomChannel).emit("cardSelected", cardInfo); (without broadcast), without any success
After debugging the code inside socket.io, I found out that changing the value of "id" for the socket was breaking everything!
I removed this line socket.id = ++lastPlayerId; and now everything works fine :)
So, remember: Never manually change the id of a socket.
Related
i Want to Send Multiple Data Publisher to Broker and Store it Mysql Database, i am Using MQTT Broker But only Send Single Message in all Field, i Want to store Different data in all mysql Database Field Using Node JS. Please Give Solution.
Publisher code
var mqtt = require('mqtt');
var client = mqtt.connect('mqtt://192.168.0.92');
client.on('connect', function () {
setInterval(function () {
var a = " messege";
var b = "time";
var c = "address";
client.publish('myTopic', `${a}`);
client.publish('myTopic1', `${b}`);
client.publish('myTopic1', `${c}`);
console.log('Message Sent');
}, 5000);
});
Broker code
var mosca = require('mosca');
var settings = { port: 1883}
var broker = new mosca.Server(settings)
var mysql = require('mysql');
var db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'root#123',
database: 'abc'
});
db.connect(() => {
console.log("db connect");
})
broker.on('ready', () => {
console.log("broker is ready");
})
broker.on('published', (packet) => {
message = packet.payload.toString()
time = packet.payload.toString()
add = packet.payload.toString()
console.log(message);
if (message.slice(0, 1) != '{' && message.slice(0, 4) != 'mqtt') {
var dbSet = 'insert into broker set ?'
// var dbSet =`INSERT INTO mqttjs (message, time) VALUES ("a","b")`;
var data = {
message: message,
time: time,
add:add
}
db.query(dbSet,data, (error, output) => {
if (error) {
console.log(error);
} else {
console.log("data saved");
}
})
}
})
Short answer: You don't.
Slightly longer answer:
Each message is totally independent of any other.
This means you have 2 choices
Have the publishing client bundle all the values into a single message
Add an identifier to each message so the code doing the insert can work out when it has a full set of values before doing the insert.
I am creating a page to play a card game, this is for several players who can connect to different rooms. I am having problems in the lobby, where you can see which players are connected to a room. It happens that using the sockets I call the io() function in a section, this connection that is generated is used to connect the client to a room when requested, as I need to use the connection (socket) in another section, I import it with javascript, but this makes me call the io() function again and create a new connection and this is not connected to the room that I had already specified.
This is my code of one of the client parts:
const socket = io();
$("#createParty").click(() => {
const code = newCode();
socket.emit("client: newParty", {userName: sessionStorage.getItem("userName"), code: code});
window.location.href = "/room";
});
$("#connectParty").click(() => {
const codeConnect = $("#txt-codeConnect").val();
socket.emit("client: tryConnectParty", {userName: sessionStorage.getItem("userName"), codeConnect: codeConnect});
window.location.href = "/room";
});
export default socket;
There are two buttons, one that creates the room or party and another one that connects you to one of them, both emit different events to the server.
Here is my server side code:
// Import for server...
import { Server as WebSocketServer } from "socket.io";
import http from "http";
import app from "./app.js";
const server = http.createServer(app);
const io = new WebSocketServer(server);
var rooms = [];
export var usersRoom = [];
// Events socket...
io.on("connection", (socket) => {
console.log("New conection:", socket.id);
socket.on("client: newParty", (data) => { // data = {userName, code}
const code = data.code;
socket.join("room-"+ code);
rooms.push(
{
room: code,
users: [
data.userName
]
}
);
});
socket.on("client: tryConnectParty", (data) => { // data = {userName, codeConnect}
const code = data.codeConnect;
for (let i = 0; i < rooms.length; i++) {
if (rooms[i].room == code) {
const userName = data.userName;
const room = "room-"+ code;
socket.join(room);
rooms[i].users.push(userName);
usersRoom = rooms[i].users;
socket.to(room).emit("server: newConnection", userName);
}
}
});
});
export default server;
I use a variable called rooms, that is composed by the existing rooms and the users connected in it, this is so that the new player that connects can see all the players already connected, the problem is for the players that were already connected at the moment that a new one enters.
As you can see the server receives the two different events, in the event "tryConnectParty" the server checks if the code of the room is in the rooms variable, when it finds it, it emits an event to that room.
This is my code from another part of the client:
import socket from "/main.js";
const userName = sessionStorage.getItem("userName");
$("#txt-userName").html(`<b class='text-white'>${userName}</b>`);
$("#txt-codeRoom").html(`<b>${sessionStorage.getItem("code")}</b>`);
var divPlayers = $("#players");
socket.on("server: newConnection", (userName) => {
divPlayers.append(createCard(userName));
});
function createCard(user) {
return `
<div class="card d-inline-flex text-center" style="width: 300px;">
<div class="card-body">
<span class="h1 text-dark">${user}</span>
</div>
<img class="card-img-bottom" src="img/avatar.png" alt="avatar">
</div>
`;
}
This javascript code is called by a html one:
<script src="/logic/players.js" type="module"> </script>
This is the "room" section, here you can receive the "newConnection" event that creates a card with the name of the new user so that the user can see the new player.
The event "newConnection" never arrives or is received. I was checking several times the code and it was when I realized that when the "socket" is imported is when the io() function is called again and the connection is lost, as this is a new connection and the event is only sent to those who are in the room.
I want to know if there is a better way to do it or how I could solve this, since I can't move forward without this.
[My problem is that my initial socket.join (user that created the game) isnt recognized when I leave the handleNewGame() function. This image contains console.log after each socket.join call I make, I make the initial socket.join call when the game is first created by a user, and the rest are made when a user uses the code to join the game.
VERSION 2.3.0 Socket.io
IMAGE HERE! Image containing console.log made after each socket.join,]1
io.on('connection', socket => {
console.log("Connection")
socket.on('newGame', handleNewGame);
socket.on('joinGame', handleJoinGame);
async function handleNewGame() {
let roomName = makeid(5);
const game = await Game.create({roomID: roomName, Players: [socket.id]});
clientRooms[socket.id] = roomName;
socket.emit('gameCode', game.roomID);
state[roomName] = game.GameState;
socket.join(roomName);
console.log(`Socket ID: ${socket.id}`);
console.log(io.sockets.adapter.rooms);
}
function handleJoinGame(roomName) {
clientRooms[socket.id] = roomName;
socket.join(roomName);
console.log(`Socket ID: ${socket.id}`);
console.log(io.sockets.adapter.rooms);
}
});`
});
I've been studying kafkajs and socket.io I'm am very new to it and i cant seem to understand some things.
I have created a chat application that basically by opening a browser(client) you can type messages and they get displayed in a chat-window.
I found a tutorial that makes kafka print "this message + i".
I want to instead of sending to the topic and printing message+i to print what people type in chat and I'm not sure how I'm supposed to do that.
This is my consumer.js:
const { Kafka } = require("kafkajs")
const clientId = "my-app"
const brokers = ["localhost:9092"]
const topic = "message-log"
const kafka = new Kafka({ clientId, brokers })
// create a new consumer from the kafka client, and set its group ID
// the group ID helps Kafka keep track of the messages that this client
// is yet to receive
const consumer = kafka.consumer({ groupId: clientId })
const consume = async () => {
// first, we wait for the client to connect and subscribe to the given topic
await consumer.connect()
await consumer.subscribe({ topic })
await consumer.run({
// this function is called every time the consumer gets a new message
eachMessage: ({ message }) => {
// here, we just log the message to the standard output
console.log(`received message: ${message.value}`)
},
})
}
module.exports = consume
This is my producer.js:
// import the `Kafka` instance from the kafkajs library
const { Kafka } = require("kafkajs")
// the client ID lets kafka know who's producing the messages
const clientId = "my-app"
// we can define the list of brokers in the cluster
const brokers = ["localhost:9092"]
// this is the topic to which we want to write messages
const topic = "message-log"
// initialize a new kafka client and initialize a producer from it
const kafka = new Kafka({ clientId, brokers })
const producer = kafka.producer()
// we define an async function that writes a new message each second
const produce = async () => {
await producer.connect()
let i = 0
// after the produce has connected, we start an interval timer
setInterval(async () => {
try {
// send a message to the configured topic with
// the key and value formed from the current value of `i`
await producer.send({
topic,
messages: [
{
key: String(i),
value: "this is message " + i,
},
],
})
// if the message is written successfully, log it and increment `i`
console.log("writes: ", i)
i++
} catch (err) {
console.error("could not write message " + err)
}
}, 1000)
}
module.exports = produce
I know I'm supposed to somehow connect the topics brokers and clients with the socket.io but I'm not sure how.
Here is my chat.js:
/* Kane connection sto server opos prin
exw tin ikanotita na xrhsimopoihsw to io logo tou library pou phra apo to documentation*/
var socket = io.connect('http://localhost:8000');
// linking Variables toy indexhtml
var message = document.getElementById('message');
var username = document.getElementById('username');
var btn = document.getElementById('send');
var output = document.getElementById('output');
var feedback = document.getElementById('feedback');
// Stelnw events pou ginonte apo ton xristi kai stelnonte ston server
btn.addEventListener('click', function(){
socket.emit('chat', {
message: message.value,
username: username.value
});
message.value = "";
});
message.addEventListener('keypress', function(){
socket.emit('typing', username.value);
})
// Events wste na perimenw to data apo ton server
socket.on('chat', function(data){
feedback.innerHTML = '';
output.innerHTML += '<p><strong>' + data.username + ': </strong>' + data.message + '</p>';
});
socket.on('typing', function(data){
feedback.innerHTML = '<p><em>' + data + ' is typing a message...</em></p>';
});
You'll need a socket.io server.
Example:
const consume = require('consumer.js');
const produce = require('producer.js');
const { Server } = require('socket.io');
const io = new Server();
consume(({ from, to, message }) => {
io.sockets.emit('newMessage', { from, to, message });
})
io.on('connection', function(socket) {
socket.emit('Hi!', { message: 'Chat connected', id: socket.id });
socket.on('sendMessage', ({ message, to }) => {
produce({ from: socket.id, to, message });
});
});
You also need to modify your consumer & producer to accept parameters and callbacks.
Consumer Example:
...
const consume = async cb => {
// first, we wait for the client to connect and subscribe to the given topic
await consumer.connect()
await consumer.subscribe({ topic })
await consumer.run({
// this function is called every time the consumer gets a new message
eachMessage: ({ from, to, message }) => {
cb({ from, to, message });
},
});
}
Producer Example:
const produce = async ({ from, to, message }) => {
producer.send(topic, { from, to, message });
}
Don't forget to modify your chat.js on the client side
All of this can be optimized and is just a brief example
im pretty new into javascript and node, currently working into a node.js app,
the app use express and mongoDB, the idea is listen to some third party services via webhook, websocket and mqtt and store all data into mongoDB.
but I have a litle problem, some of the third party apps send me data too often,
for example, the mqtt stream sends about 2 message every second, i need to store only one of those message every minute.
this is the way I instance mqtt into app.js
var mqttHandler = require('./mqtt/mqtt_handler'); //mqtt
var mqttClient = new mqttHandler(); //mqtt
mqttClient.connect(); //mqtt
this is my mqttHandler.js:
onst mqtt = require('mqtt');
class MqttHandler {
constructor() {
this.mqttClient = null;
this.host = 'mqtts://host';
this.username = 'foo'; // mqtt credentials if these are needed to connect
this.password = 'mypassqword';
this.port = 8083;
this.protocol = 'MQTTS';
this.client = 'bar'
}
connect() {
// Connect mqtt with credentials (in case of needed, otherwise we can omit 2nd param)
this.mqttClient = mqtt.connect(this.host, {password : this.password, username : this.username, port: this.port});
// Mqtt error calback
this.mqttClient.on('error', (err) => {
console.log(err);
this.mqttClient.end();
});
// Connection callback
this.mqttClient.on('connect', () => {
//console.log(`mqtt client connected`);
});
// mqtt subscriptions
this.mqttClient.subscribe('/the_subscription');
// When a message arrives, console.log it
this.mqttClient.on('message', function (topic, message) {
console.log(message.toString())
});
this.mqttClient.on('close', () => {
//console.log(`mqtt client disconnected`);
});
}
// Sends a mqtt message to topic: mytopic
sendMessage(message) {
this.mqttClient.publish('mytopic', message);
}
}
module.exports = MqttHandler;
i'veing reading about setInterval and setTimeout, but I can't figure out how to implement these to force a given function to only run once every X seconds (no mather how many times it is called)
could there be a similar / generic way to implement this feature for both, mqtt, webohooks and / or websocket?
I took this example about how to implement mqtt from a tutorial, its working perfect, as I said, im prettty new to javascript.
One naive approach using setInterval is to set a flag regularly and clear it once a message is posted. The ignore any other messages until the flag is set again by the interval function.
let readyToPost = false;
setInterval(function(){ readyToPost = true; }, 1000);
In your function:
function connect() {
if (!readyToPost) return; // do nothing
readyToPost = false;
// rest of your code
}
There is also a wrapper of the module mqtt:
const mqttNow = require('mqtt-now');
const options = {
host: 'localhost',
interval: 1000,
actions: [
{
topic: 'public',
message: 'my message'
},
{
topic: 'random',
message: () => ( 'random ' + Math.random() )
}
]
}
mqttNow.publish(options);