This issue is occurring on the client side of my application.
I am running into a issue with the socket.on event not being hit on my Node application. This usually happens when the site is being loaded for the first time by a client. When loading, the tabs usually have the data which has been dynamically generated in Javascript, however since the socket.on event is not being hit this content is not loading, leading to the tabs being blank (The base Pug/Jade file is still rendering - just no dynamic content). Refreshing the page usually fixes the issue and the sockets start receiving the data again.
There is no direct error message being output, the socket.on event is just not being hit. This also seems to solve itself once the page has been reloaded and is not occurring 100% of the time.
This is even more unusual, as when running my Node server in debug mode it is showing the socket.io-parser encoding the data and socket.io-client writing the data, meaning that it is being emitted, just not being picked up by the socket.on on my client side.
Note that this has been simplified down
io.sockets.on('connection', function (client) {
sendExampleData();
});
function sendExampleData() {
mysqlFunc.getOrderData(function(rows){
io.emit('example_data', rows);
})
}
socket.on('example_data', function (rows) {
console.log(rows);
}
I expect that on page load, the data is emitted and the socket.on event will receive this data, ready to be processed. Currently, although not happening every-time, socket.on is not being hit and the data is not being grabbed.
There is nothing out of the ordinary whilst running the server in Debugging mode and any ideas would be extremely helpful. If there's any other bits of information that would be useful in helping debug this issue please let me know.
Thanks in advance!
I finally managed to figure out what the issue was.
My socket.on event listeners were inside my $(document).ready and were not being called on first page load. I'm assuming that the event listeners were not being defined early enough and therefore when I socket.emit'd on connection the event listeners had not actually loaded.
I moved all of the logic which was inside the $(document).ready out and moved my event listeners into a connection event (Thanks for the tip Marc).
socket.on('connect', function(){
socket.on('data_1', funcOne);
socket.on('data_2', funcTwo);
socket.on('data_3', funcThree);
socket.on('data_4', funcFour);
socket.on('data_5', funcFive);
});
This fixed the issue and now socket.on is being hit every time on page load.
Thanks for the help guys and hopefully this can help someone also having this issue!
Related
I want to refresh a page in a browser tab using keyboard shortcut or CLI command when the browser window is not active (i.e. I'm working on different display). The webpage that have to be refreshed is well under my control, so I can inject any javascript there. Scrolling location should be kept after the refresh. My idea is to include simple javascript on that page that will wait for some outside event, i.e. through socket, and when this event happens, run location.reload(true). Then I can communicate with this javascript from the command line (shell) script and assign keyboard shortcut to this command line script if needed. I'm pretty sure it's possible because there are lots of tools that allow this (e.g. LiveReloadX). I cannot use such tools because I don't want to automatically refresh the window when something changes, I want to do it by explicit command. It seems that it should be really simple, but I cannot find the solution so far. So, my question is: how to make javascript running in the browser to be controlled from the command line?
There's two parts to getting your solution done - the server running on your command line and the client running on your webpage.
Client
In your browser, you want to initialize a WebSocket client. MDN has a great example to get started with this, and I added your intended functionality of refreshing the page:
// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');
// Connection opened
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
// Listen for messages
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
location.reload(true)
});
Server
There are many different ways to set up a WebSocket server and choosing the one that works best for you is probably too broad for an answer on this site. I did some quick research, and it looks like websocketd is a great way to wrap an existing command line program that uses standard in/out into a WebSocket server. You would need to implement a command line program that listens for your refresh shortcut and then writes a message to standard out. Then, you would wrap it with websocketd and run it from the command line:
websocketd --port=8080 ./your-shortcut-listener.sh
I want to programmatically close the Server-Sent-Events once a user logs out. However when the user logs back in, the browser does not execute any https-requests anymore because it has reached its limit of SSE connections.
I'm using EventSource for listening to events.
This is how I close my connections:
var eventSource;
function onChange(accountId:string, callback){
var url = "...";
eventSource = new EventSource(url);
if(eventSource){
eventSource.addEventListener("put", callback);
}
}
function close(){
this.eventSource.close()
}
When I was observing the network connections on the browser, I realized the connection still exists. The output in Timing is: Caution: request is not finished yet!, the following event-streams are stalled due to limited number of connections.
I'm not sure if EventSource is designed to behave like this, but I could not find anything regarding this issue, since many people don't have the same scenario.
Everytime I reload the page in my browser (chrome) all existing connections are closed, but I don't want to reload the page to workaround this issue.
Make sure that this.eventSource is referring to what you think it is. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this If called from an event handler it won't be the global scope.
After calling xxx.close(), set xxx to null. (I doubt that is the problem, but it might help the garbage collection, and also helps find bugs.)
As a 3rd idea of the problem, avoid giving globals the same name as built-in objects. I normally use es for my event source objects.
I have some node.js client side code like this:
socket.emit('clickAccept', { myrecid: recid });
Server side node.js code gets it fine and all is well.
If I take the server down to simulate a server side outage, then click the button that fires this socket.emit on the client side, this happens:
Nothing really, I guess it might eventually time out
When I bring the server back up, the clicks end up being sent to the server and the server acts on them (TCP-like I Guess).
What I want to happen is for those socket.emit calls to die after a short timeout and not send when the server comes back up, it causes all sorts of confusion because if they click 3 times, nothing happens, then when/if the connection or server comes back up they get 3 reactions all at once.
Also, if they click and it times out because the server is down, I would like to show an error to the client user to let them know that basically the click didn't work and to try again.
I know how to act on and show an error if the socket goes down but I don't want to do this if they aren't trying to click something at that time. No sense is firing errors at the user because the socket went down briefly if they have no need to do anything at that moment.
So, to be clear, I only want to show an error if they click on the button and the socket between the client and server is down. AND... If they get an error, I want to kill that emit, not save it all up and fire it and all the other clicks when the server comes back up a few seconds later.
Thanks in advance and I hope that was at least reasonably clear.
The root of your issue is that socket.io attempts to buffer any data that it can't currently send to the server (because the connection to the server is disconnected) and when the server comes back up and the connection is restored, it then sends that data.
You can see the technical details for how this works here: socket.io stop re-emitting event after x seconds/first failed attempt to get a response
You have several implementation options:
If socket.io already knows the client is not connected to the server, then don't buffer the data (perhaps even give you back an error to show to your user).
When socket.io reconnects and there was data buffered while the connection was down, clear that data and throw it away so old data isn't sent on a reconnect.
Implement a timeout to do one of the above after some sort of timeout.
So, to be clear, I only want to show an error if they click on the button and the socket between the client and server is down. AND... If they get an error, I want to kill that emit, not save it all up and fire it and all the other clicks when the server comes back up a few seconds later.
Probably, the simplest way to do that is to implement a version of what is shown in the above referenced answer:
Socket.prototype.emitWhenConnected = function(msg, data) {
if (this.connected) {
this.emit(msg, data);
return null;
} else {
return new Error("not connected");
}
}
Then, switch your code from using .emit() to use .emitWhenConnected() and check the return value when using it. If the return value is null, then no error was detected. If the return value is not null, then there was an error.
Thanks for the other answers and help. I ended up solving this in a super simple way. See below:
if (socket.connected){
// Do your thing here
} else {
// Throw error here that tells the user they're internet is likely down
}
Hope this helps someone out there, it was a huge improvement in our code to make sure that user's are getting proper feedback when if they have brief network/internet outages.
Heading
Im emitting games from a .json file, it all works fine
When i restart my js code, it emits the games and
socket.on('getgames', function (data) {
socket.emit('updategames', {games:games});
});
This is supposed to make a div class="game-:gameid", it works fine until either theres a connection error or i restart my js code. Then it will make duplicate divs and emit it twice, i dont want duplicate.
This problem will occur then you call on:
socket.on('getgames', function (data) {
socket.emit('updategames', {games:games});
});
multiple times. This will course socket.io to subscribe to your event 'getgames' multiple times.
My guess is that you call this code every time you restart your game. To fix it you either unsubscribe from the event or just call the code one time.
Using angular and socket.io I am getting duplicate events on the client everytime the server emits. Angular is only included once, there is only one socket.io connection, and there is only one listener per event on the client. Upon receiving an event on the server, data is logged, and this process only ever happens once. Then the data is emitted and the callback is called twice on the client, despite only being in scope once(to my knowledge).
client:
//inside a controller
var thing ='foo';
socket.emit('sentUp',thing)
socket.on('sentDown',function(thing){
console.log(thing)//this happens twice
});
server:
/*
node&express stuff here
*/
socket.on('connection',function(socket){
socket.on('sentUp',function(stuff){
console.log('this happened')//x1
socket.emit('sendDown',stuff);
});
})
Most likely your controllers are being loaded more than once. You can easily check it by logging.
Move out the socket code from the controllers and put them in a service where they're only called once.
I have found in my own socket.io client code that some of the connect events can occur each time the client reconnects. So, if the connected is lost for any reason and then the client automatically reconnects, the client may get a connect event again.
If, like me, you're adding your event handlers in the 'connect' event, then you may be accidentially adding multiple event handlers for the same event and thus you would think you were seeing duplicate data. You don't show that part of your client code so I don't know you're doing it that way, but this is an issue that hit me and it is a natural way to do things.
If that is what is happening to you, there are a couple possible work-arounds:
You can add your event handlers outside the connect event. You don't have to wait for connection to finish before adding event handlers. This way, you'd only ever do them once.
Before adding the event handlers you add upon connection, you can remove any previous event handlers that were installed upon connection to make sure you never get dups.