SocketIO dont fire first client events - node.js

I am building a chat app. The issue is that, socket.io does not emitthe keypress event on the first client, when fired.
I built this with Ionic 3 & Angular 5
Here's the "bug"
My view.html
<p *ngIf="typing == true">... is typing</p>
<ion-item no-lines>
<ion-input
type="text"
placeholder="Message"
[(ngModel)]="message"
(keypress)="userIsTyping()">
</ion-input>
</ion-item>
My view.ts
userIsTyping() {
this.socket.emit('typing');
this.socket.on("updateTyping", (data) => {
console.log('typingInSocket = ' + data.isTyping);
this.typing = data.isTyping;
});
console.log('typing = ' + this.typing);
My server.js
io.on('connection', (socket) => {
socket.on('typing', () => {
socket.broadcast.emit('updateTyping', {
isTyping: true
});
setTimeout(() => {
socket.broadcast.emit('updateTyping', {
isTyping: false
});
}, 2000);
});
Do you have any solution ?
Thanks you guys !

Okay, so the issue is how you're going about defining your socket listener. You're setting the event listener for this.socket.on("updateTyping") inside the block of code that only runs once you start typing. So every time you start typing, you create a new duplicate event listener. However, because it's only defined when you start typing, the "is typing..." won't show up until you've typed at least one character, at which point it'll start listening.
The answer is basically to move that socket listener outside of the userIsTyping() function.
this.socket.on("updateTyping", (data) => {
console.log('typingInSocket = ' + data.isTyping);
this.typing = data.isTyping;
});
userIsTyping() {
this.socket.emit('typing');
}

Related

Socket.emit doesn't trigger for all clients

I am new to react with socket.io, and I am stuck on the retrieving data permanently on frontend. What I'm trying to do is to create a little game and first step is to have a lobby page where multiple users can join.
Here is the part where I have problems because, if example, if I create a game with user Test
Lobby is now just with user Test
Tab1:
| --- Test --- |
But if I open a new tab and join to the current tab with the user Test1, my new tab is
Tab2:
| --- Test Test1 --- |
The problem that I ecounter is that the first tab only have the first user joined in the game
Tab1:
| --- Test --- |
The event didn't trigger for this tab. The same scenario happends for the next tabs and so on.
Node.js code
socket.on("getUsers", async (data) => {
if (!data.gameCode) {
universalError("GameCode doesn't exist");
return;
}
const existentGame = await pubClient.get(data.gameCode);
if (!existentGame) {
universalError("The Current Game Does Not Exist");
return;
}
currentGame = JSON.parse(existentGame);
socket.emit("gameUsers", {
gameUsers: currentGame,
});
});
React code
const equals = (a, b) => JSON.stringify(a) === JSON.stringify(b);
function() {
const socket = useContext(SocketContext);
const [host, setHost] = useState("");
const [users, setUsers] = useState([]);
useEffect(() => {
if (!currentGameCode) return;
socket.emit("getUsers", { gameCode: currentGameCode });
}, []);
socket.on("gameUsers", (data) => {
setHost(data.gameUsers.host);
console.log(users, data.gameUsers.users);
if (!equals(users, data.gameUsers.users)) {
setUsers(data.gameUsers.users);
}
});
return(
...
)
}
I tried to put the socket.on before useEffect function, also insert the socket.on inside a useEffect with multiple dependency but I had no success with that. From what I saw, the user enter in socket.on("gameUsers") just once per tab and that is when he join the game.. The call on the backend is made everytime a new user join to the game, but my socket.on doesn't trigger in that case. Any ideas of what I'm doing wrong? Anticipated thanks!
Ok, I figured it out, the problem was not on the client side but on server side.. So I edited the title from
Socket.io doesn't trigger event for previous tabs - React
to
Socket.emit doesn't trigger for all clients.
I was trying to emit the event only to the particular socket which triggered the event.
socket.emit < - was triggered JUST by - > socket.on at which was connected to.
SOLUTON: Use io.emit to send to all client
io.on("connection", (socket) => { ... });

Emitting multiple times

I've read through the site and googled, and can't seem to find an answer that will work for me.
I've set up a super super basic example of using Socket.IO until I can get my head around, all it does is passes a number to the back end, adds +1 and send it back to front end.
It does work however, each interval round, it emits more and more (which I can see using console.log on the server side.) I'm only connected using one computer to test.
Can someone help me please. I understand its probably because the emit is inside the connection but I just can't quite click in my head on how to overcome this.
I've tried moving bits around, moving the function out of the connection. I've tried multiple ideas from google, but nothing seems to solve it.
const io = require('socket.io')();
io.on('connection', (socket) => {
socket.on('subscribeToAddition', (additon,interval) => {
console.log('current number... ', additon);
setInterval(() => {
socket.emit('addition', additon+1);
}, interval);
});
});
const port = 8000;
io.listen(port);
console.log('listening on port ', port);
the interval variable is set as 5 seconds in my react component. I just want it to log/update once every five seconds instead of log once, then twice, then 4 times, then 8 times, etc
Given the symptoms, it's likely that you're sending the subscribeToAddition event more than once on the same socket and thus starting more than one timer for the same socket and thus you get duplicate messages.
Proper indentation of your code makes things a little clearer:
const io = require('socket.io')();
io.on('connection', (socket) => {
socket.on('subscribeToAddition', (additon, interval) => {
console.log('current number... ', additon);
setInterval(() => {
socket.emit('addition', additon + 1);
}, interval);
});
});
const port = 8000;
io.listen(port);
console.log('listening on port ', port);
I want to make sure you understand that setInterval() starts a repeating timer that goes forever until you call clearInterval() on the timerId that it returns.
Problems I see:
In every subscribeToAddition message, you're adding a new setInterval() timer. So, if you send the subscribeToAddition twice, you will have two setInterval() timers each going on the same socket.
Those setInterval() timers will accumulate and never go away because you have no way of the client stopping them and they don't go away even when the socket closes. They will just cause errors because socket.emit() won't work on a closed socket, but they will probably even prevent garbage collection of the old socket.
It is not clear exactly how you want this to work, but here's a cleaned up version:
const io = require('socket.io')();
io.on('connection', (socket) => {
function clearTimer() {
if (socket.myTimer) {
clearInterval(socket.myTimer);
delete socket.myTimer;
}
}
socket.on('subscribeToAddition', (additon, interval) => {
console.log('current number... ', additon);
// don't start a new interval timer if one is already running
if (!socket.mytimer) {
socket.mytimer = setInterval(() => {
socket.emit('addition', ++additon);
}, interval);
}
});
socket.on('unSubscribeToAddition', () => {
clearTimer();
});
socket.on('disconnect', () => {
clearTimer();
});
});
const port = 8000;
io.listen(port);
console.log('listening on port ', port);
This version makes the following modifications:
Keeps track of the timerID from setInterval() so we can stop it in the future. For convenience, we store it as a custom property on the socket object so that each connected socket has its own timer.
Adds an unsubscribeToAddition message so the client can stop the timer.
Adds a disconnect message handler so you can stop the timer when the socket disconnects.
Increments the additon variable on each tick of the timer.

Enter twice in socket.on event

I'm new to Angular and Node.js.
My code executes a socket.emit to a server side function and receives a response in the socket.on event ('listartists').
Unfortunately, he enters twice in this event and I do not understand why.
On the server side (app.js) I put logs and the function is called correctly once, but then it goes through the socket.on twice ('listartists').
Thank you all
var appAng = angular.module('appAng', []).controller('myCtrl', function($scope) {
socket.on('listartists', function(msg) {
// !!! enter twice this part of the code
var ClientMessage = JSON.parse(msg);
if (ClientMessage.ClientID == GetClientUniqueId()) {
var artistslist = JSON.parse(ClientMessage.Message);
$scope.$apply(function() {
var artistslist=JSON.parse(ClientMessage.Message);
$scope.artists = artistslist;
});
}
});
});
i modified my code as :
socket.on('listartists', function(msg){
var ClientMessage=JSON.parse(msg);
if (ClientMessage.ClientID== GetClientUniqueId())
{
console.log("Arriva Lista Artisti " + ClientMessage.ClientID);
var artistslist =JSON.parse(ClientMessage.Message);
$scope.artists = artistslist;
$scope.$apply( function(){});
}
});
Anyway if i remove $scope.$apply( function(){}); the socket.on event is called only once,
otherwise it passes twice from there.
I think the problem is the management of the SCOPE some idea?

socket.io emit in loop until client responds?

I would like to emit a message to the client every X seconds until the client clicks a button and acknowledges receipt.
Any ideas on how I might accomplish this? New to Node but I am hoping I can get this done with Node and Sockets.io.
Thanks in advance for any insight or help to get me in the right direction.
I think the best way to accomplish this would be to create a new class that extends the "vanilla" Node.js event class. You will need to require it first like so:
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
https://nodejs.org/api/events.html
Once you've got your class, what you will need to do is creating a child process, to do so, you can refer to the official documentation here:
https://nodejs.org/api/child_process.html
Inside this process you can create your loop and send a message through the socket. The goal of extending the events class is to be able to create an Inside event emission which you can listen to detect specific events (like socket closed, or anything you'd like to track)
I hope this answers your question.
This is a simple setup just to give you an idea of how to do it:
node server (very basic, only socket.io, nothing else):
const io = require('socket.io')(3000);
const interval = 1000;
io.on('connection', function (socket) {
console.log(socket.id + ' has connected');
var foo = setInterval (function () {
socket.emit('foo');
}, interval);
socket.on('confirmed', function () {
console.log('confirmation received from ' + socket.id);
clearInterval(foo);
});
});
console.log('socket.io server started at port 3000');
index.html (open it in your browser and see what happens):
<!doctype html>
<html>
<head>
<title>Testing socket.io</title>
</head>
<body>
<button id="button">Confirm</button>
<p id="socket">waiting...</p>
<p id="alert">foos counter: </p>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.3/socket.io.min.js"></script>
<script>
var socket = io("http://localhost:3000");
var counter;
socket.on('connect', function() {
counter = 0;
document.getElementById("socket").innerHTML = "connected";
document.getElementById("button").addEventListener("click", function () {
socket.emit("confirmed");
});
});
socket.on('foo', function() {
counter++;
document.getElementById("alert").innerHTML = "foos counter: " + counter;
});
</script>
</body>
</html>

play chess with socket.io and uci

I want to create a webapp where you can play against a UCI-Chessengine. I found https://github.com/imor/uci which works nice on commandline. So I "only" need a websocket to evaluate the moves.
But I'm not able to get it running... I tried (based on the uci-example):
io.sockets.on('connection',function(socket){
socket.on('start', function(data) {
var uci = new UCI();
var game = new Chess();
uci.on('ready', function () {
//Start a new 10 minute game with engine as black, use the first found
//engine and the first found polyglot book
uci.startNewGame(uci.getAvailableEngines()[0], 'black', 10,
uci.getAvailableBooks()[0]);
}).on('newgame', function () {
console.log("A new 10 minute game has started.");
console.log("Enter your moves in algebraic notation. E.g. e2e4<Enter>");
console.log(game.ascii());
}).on('moved', function (move) {
game.move(move);
console.log(move.from + move.to + (move.promotion ? move.promotion : ''));
console.log(game.ascii());
}).on('error', function (message) {
console.log('Error:' + message);
}).on('exit', function (message) {
console.log('Exiting:' + message);
}).on('gameends', function (result, reason) {
console.log('Game ends with result ' + result + ' because ' + reason);
uci.shutdown();
process.exit();
});
})
socket.on('m',function(data){
uci.move(data.move);
});
});
Starting the game is working: socket.emit('start',{"bar":"foo"})
but when I try to make a move with socket.emit('m':"e2e4") it does not know the uci.move
That's okay cause it's defined within socket.on('start')... so he can't know it, but I'm not able to get it running. I've tried some stupid ideas, for example putting the socket.on('m') into the socket.on('start')...
Can somebody help me with this? How can I send moves to the created uci-connection? Or is this not possible?
Thank you very much.
Try moving the var uci up in the scope hierarchy.
io.sockets.on('connection',function(socket){
var uci;
socket.on('start', function(data) {
uci = new UCI();
var game = new Chess();
...
})
socket.on('m',function(data){
uci.move(data.move);
});
});

Resources