I try to send some message across rooms. I have:
server.js
var io = require('socket.io').listen(8081);
var chat = io
.of('/chat')
.on('connection', function (socket) {
socket.on('chatTestEmit1', function(d) {
console.log('>> chatTestEmit1 >> '+JSON.stringify(d));
console.log(io.sockets.manager.rooms);
socket.to('/news').emit('newsTestEmit1', d);
});
});
var news = io
.of('/news')
.on('connection', function (socket) {
socket.on('checkAlive', function(d) {
console.log('>> checkAlive >> '+JSON.stringify(d));
});
socket.on('newsTestEmit1', function(d){
console.log('>> newsTestEmit1 >> '+JSON.stringify(d));
});
});
client.html
<script src="http://localhost:8081/socket.io/socket.io.js"></script>
<script>
var chat = io.connect('http://localhost:8081/chat')
, news = io.connect('http://localhost:8081/news');
chat.on('connect', function () {
chat.emit('chatTestEmit1',{msg:'This is the message from client.'});
});
news.on('connect', function(){
news.emit('checkAlive',{msg:'News is alive'});
});
</script>
The log looks like this:
info - socket.io started
debug - client authorized
info - handshake authorized 74858017692628057
debug - setting request GET /socket.io/1/websocket/74858017692628057
debug - set heartbeat interval for client 74858017692628057
debug - client authorized for
debug - websocket writing 1::
debug - client authorized for /chat
debug - websocket writing 1::/chat
debug - client authorized for /news
debug - websocket writing 1::/news
>> chatTestEmit1 >> {"msg":"This is the message from client."}
{ '': [ '74858017692628057' ],
'/chat': [ '74858017692628057' ],
'/news': [ '74858017692628057' ] }
debug - websocket writing 5::/chat:{"name":"newsTestEmit1","args":[{"msg":"This is the message from client."}]}
>> checkAlive >> {"msg":"News is alive"}
debug - emitting heartbeat for client 74858017692628057
debug - websocket writing 2::
debug - set heartbeat timeout for client 74858017692628057
debug - got heartbeat packet
debug - cleared heartbeat timeout for client 74858017692628057
debug - set heartbeat interval for client 74858017692628057
debug - emitting heartbeat for client 74858017692628057
If this code worked correctly, it should have logged as below:
>> newsTestEmit1 >> {"msg":"This is the message from client."}
appeared in the log. Note that I also tried:
io.sockets.in('/news').emit('newsTestEmit1', d);
io.sockets.in('news').emit('newsTestEmit1', d);
io.of('/news').emit('newsTestEmit1', d);
None of them works. Do I miss something?
Thanks.
I did not look through all of your code, but the general idea is that you want to emit an event + the data from the client-side when a message is sent, then set an event on the server-side to broadcast when a happens. Some psuedo server-side code below:
io.sockets.on('connection', function (socket) {
console.log('Socket connection established.');
socket.on('message event', function(data){
socket.broadcast.emit('global chat update', data);}
);
});
On the client side you want 2 bits of code:
1) When a chat event happens, something like:
socket.emit('message event', data)
2) Something to display the newest chat when there is a global chat update.
socket.on('global chat update', function(data) { // your script to update chat data here };
Related
Bottom line up front: The Paho MQTT client sucessfully connects to my Mosquitto broker, but immediately disconnects when I try to publish a message or when it receives a message from a topic it's subscribed to. I've tried changing Mosquitto's listening port and authentication settings, and using two different versions of Paho MQTT, and I still have the same problem.
Now let's get into detail.
Intro: I'm making a dashboard for some facial recognition devices that communicate through MQTT. I set up a Mosquitto broker and I've had no problems connecting to it and communicating with the devices using the Paho MQTT client for Python (I made a kind of server to sync the device's info to a database). Now I'm making the web interface, so I added a WebSockets listener to my mosquitto.conf and wrote a script using the Paho MQTT library for Javascript to connect to it, subscribe to topic sgdrf/out, send a simple JSON message to topic sgdrf/in to get the list of online devices, and process the response of the Python server once it arrives.
Problem and attempted solutions: I ran the Django server, loaded the web page and opened the JS console to find that the MQTT client successfully connected to the broker but immediately disconnected when it tried to publish the message to topic sgdrf/in. Here's each line of console output with their explanations:
The message produced by the onSuccess function, which indicates that the client successfully connected to the Mosquitto broker:
Conexión exitosa al broker MQTT.
In the onConnected function, I added console.log(uri) to see the URI used by the client to connect to the broker. I got:
ws://localhost:61613/
After printing uri to console, I made the client subscribe to sgdrf/out and then print 'subscribed' to console:
subscribed
Then I call get_online_devices(mqtt_client), a function which creates a simple JSON string and publishes it to the topic sgdrf/in. But first, it prints the strign to the console so that I can check it (just in case):
{"operator":"GetOnlineDevices","messageId":96792535859850080000,"info":{}}
Then, when the publish method is actually executed, is when I get this error (captured by the onConnectionLost function):
Pérdida de conexión con el broker MQTT: AMQJS0005E Internal error. Error Message: message is not defined, Stack trace: No Error Stack Available (código: 5)
I checked the Mosquitto log file and it only says when a new client was connected and then when it was disconnected because of a socket error (each time for every page reload). Tail of /var/log/mosquitto/mosquitto.log:
1614796149: New connection from 127.0.0.1 on port 61612.
1614796149: New client connected from 127.0.0.1 as mqttx_53195902 (p2, c1, k60, u'admin').
1614796182: Socket error on client sgdrf_dashboard_8499, disconnecting.
1614796325: New client connected from ::ffff:127.0.0.1 as sgdrf_dashboard_1597 (p2, c1, k60, u'admin').
1614796325: Socket error on client sgdrf_dashboard_1597, disconnecting.
1614796336: New client connected from ::ffff:127.0.0.1 as sgdrf_dashboard_6565 (p2, c1, k60, u'admin').
1614796336: Socket error on client sgdrf_dashboard_6565, disconnecting.
1614796931: New client connected from ::ffff:127.0.0.1 as sgdrf_dashboard_9773 (p2, c1, k60, u'admin').
1614796931: Socket error on client sgdrf_dashboard_9773, disconnecting.
1614797168: Saving in-memory database to /var/lib/mosquitto/mosquitto.db.
I tried changing the listening port in mosquitto.conf, and enabling and disabling authentication, but it changes nothing. And obviously I've had to restart Mosquito every time I changed the config file. I don't think the problem is Mosquitto.
I have the same problem whether I use Paho MQTT version 1.1.0 or 1.0.3.
As an experiment, I commented out the call to get_online_devices in my Javascript so that it doesn't try to publish anything, reloaded the page and there was no error, as expected. Then, I used MQTTX to send a JSON message to the sgdrf/out topic to which the MQTT JS client is subscribed to, and it immediately disconnected with the same error message.
Code: At the bottom of the page (index.html) I have the following code (the original code has Django template tags to fill in some values, so this is the actual code received by the browser):
<!-- Paho MQTT -->
<script src="/static/js/paho-mqtt-min.js"></script>
<!-- Scripts -->
<script src="/static/js/dashboard.js"></script>
<script>
function get_online_devices(mqtt_client) {
cmd = {
operator: "GetOnlineDevices",
messageId: generate_random_number_n_exp(20),
info: {}
};
payload_string = JSON.stringify(cmd);
console.log(payload_string)
mqtt_client.publish("sgdrf/in", payload_string);
}
function add_device_to_list(device) {
// Omitted for brevity. It's not being used yet.
}
let mqtt_client = make_mqtt_client("localhost", 61613);
let connection_options = make_connection_options(
"admin",
"CENSORED_PASSWORD"
);
mqtt_client.onConnected = function(reconnect, uri) {
console.log(uri)
mqtt_client.subscribe("sgdrf/out");
console.log('subscribed');
get_online_devices(mqtt_client);
};
mqtt_client.onConnectionLost = mqtt_client_on_connection_lost;
mqtt_client.onMessageDelivered = mqtt_client_on_message_delivered;
mqtt_client.onMessageArrived = function (msg) {
// Omitted for brevity. Checks if the payload is a
// JSON object with the right data and calls
// add_device_to_list for each item of a list in it.
};
$(document).ready(function() {
mqtt_client.connect(connection_options);
$("#reload-device-list-btn").click(function() {
get_online_devices(mqtt_client);
});
});
</script>
The dashboard.js files mentioned above just has some functions that I think will be useful for other pages, so I separated them to a file:
// dashboard.js
function generate_random_number_n_exp(n) {
return parseInt(Math.random() * Math.pow(10, n), 10)
}
function make_mqtt_client(host, port) {
let client_id = "sgdrf_dashboard_" + generate_random_number_n_exp(4);
return new Paho.Client(host, port, '/', client_id);
}
function make_connection_options(user, password) {
let connection_options = {
userName: user,
password: password,
onSuccess: mqtt_client_on_success,
onFailure: mqtt_client_on_failure,
};
return connection_options;
}
function mqtt_client_on_success() {
console.log('Conexión exitosa al broker MQTT.');
}
function mqtt_client_on_failure(error) {
console.log(
'Fallo de conexión con el broker MQTT: ' + error.errorMessage
+ ' (código: ' + error.errorCode + ')'
);
}
function mqtt_client_on_connection_lost (error) {
console.log('Pérdida de conexión con el broker MQTT: ' + error.errorMessage
+ ' (código: ' + error.errorCode + ')'
);
}
function mqtt_client_on_message_delivered(msg) {
let topic = message.destinationName;
let payload = message.payloadString;
console.log("Mensaje enviado a " + topic + ": " + payload);
}
function mqtt_client_on_message_arrived(msg) {
let topic = message.destinationName;
let payload = message.payloadString;
console.log("Mensaje recibido de " + topic + ": " + payload);
}
Here are the contents of my mosquitto.conf file:
per_listener_settings true
listener 61612
allow_anonymous false
password_file /home/s8a/Projects/sgdrf/config/pwdfile.txt
listener 61613
protocol websockets
allow_anonymous false
password_file /home/s8a/Projects/sgdrf/config/pwdfile.txt
It just sets up a TCP listener and a WebSockets listener, both disallow anonymous connections, and authenticate using a pwdfile. As I said before, I have enabled and disabled anonymous connections, and changed the port number to 9001 and back to 61613, and I still have the same error.
Conclusion: I don't know what to do and this project's deadline is next week.
I feel kinda stupid, because it was really a trivial typing mistake. The problem is that the onMessageDelivered and onMessageArrived functions have msg as argument, but I wrote messagein the function body for some reason. That's what the "message is not defined" error meant, message is literally not defined. Anyway I fixed that and now it sends and receives messages without problems.
...
More detailed story: What was not trivial is how I figured it out.
I decided to get my hands dirty and opened the non-minified version of paho-mqtt.js. I looked for "Invalid error" and found where the error constant is defined, and two places where it's used in a catch block. In both catch blocks I noticed that there was a ternary operator checking if (error.hasOwnProperty("stack") == "undefined") but the true and false clauses were inverted, which is why I was getting "No Error Stack Available".
So I inverted the clauses, and indeed I got a stack trace in the console (maybe I should file a bug report to the Paho dev team when I can). The stack trace had my mqtt_client_on_message_delivered function right at the top, so I read it again and suddenly everything made sense. Then I felt stupid for wasting an afternoon on this.
I would like to fiddle with websockets a bit. I installed a Ruby gem called "websocket-ruby" (https://github.com/imanel/websocket-ruby) I started a pry / IRB session and typed:
require "websocket"
#handshake = WebSocket::Handshake::Server.new(:host => "localhost", :port => 8080,:secure=>true)
This starts a websocket server as far as I know. Then I opened in my browser the Javascript HTML page which attempt to connect to the server:
<!doctype html>
<html lang="en">
<head>
<title>Websocket Client</title>
</head>
<body>
<script>
var exampleSocket = new WebSocket("wss://localhost:8080");
exampleSocket.onopen = function (event) {
exampleSocket.send("Can you hear me?");
};
exampleSocket.onmessage = function (event) {
console.log(event.data);
}
</script>
</body>
</html>
But it says in the console log:
failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
I tried different ports both in server and in the client respectively: 8081, 12345, but I always get this error message.
I have some idea about websocket and javascript, but not websocket-ruby.
I hope it will helpful you.
In nodejs.. server.js file, write below code
var WebSocketServer = require("ws").Server;
var wss = new WebSocketServer({port:8100});
console.log("websocket Server is Running...");
wss.on('connection', function connection(ws) {
// Store the remote systems IP address as "remoteIp".
var remoteIp = ws.upgradeReq.connection.remoteAddress;
// Print a log with the IP of the client that connected.
console.log('Connection received: ', remoteIp);
// Add a listener which listens for the "message" event.
// When a "message" event is received, take the contents
// of the message and pass it to the broadcast() function.
ws.on('message', wss.broadcast);
});
wss.broadcast = function(msg) {
wss.clients.forEach(function each(client) {
client.send(msg);
})
};
In javascript...
var SERVER_URL = 'ws://localhost:8100';
//instead of localhost you can also use IP address of your system
var ws;
function connect() {
alert('connect');
ws = new WebSocket(SERVER_URL, []);
// Set the function to be called when a message is received.
ws.onmessage = handleMessageReceived;
// Set the function to be called when we have connected to the server.
ws.onopen = handleConnected;
// Set the function to be called when an error occurs.
ws.onerror = handleError;
}
function handleMessageReceived(data) {
// Simply call logMessage(), passing the received data.
logMessage(data.data);
}
function handleConnected(data) {
// Create a log message which explains what has happened and includes
// the url we have connected too.
var logMsg = 'Connected to server: ' + data.target.url;
// Add the message to the log.
logMessage(logMsg)
}
function handleError(err) {
// Print the error to the console so we can debug it.
console.log("Error: ", err);
}
function logMessage(msg) {
// with the new message.
console.log(msg);
}
/** This is the scope function that is called when a users hits send. */
function sendMessage{
ws.send(msg);
};
connect();
in html use one button to send message to websocket server
<button onclick="sendMessage('Hi Websocket')">send message</button>
To the best of my knowledge, the Ruby code you presented does not start a Websocket server... what it does is initiate a server-side parser.
To start a server you need to use an actual websocket server.
ActionCable (with Rails) uses the websocket-ruby library to parse websocket events and it uses nio4r to operate the actual server.
Faye have a similar solution and em-websockets use the websocket-ruby gem with EventMachine.
Other Ruby Websocket servers include Iodine, which uses the C library facil.io. Iodine is used by the framework plezi as well as independently.
Since you were trying to run an echo server, here's a quick example using the Plezi framework (you can use it as middleware in Sinatra or Rails)...
...place the following in a config.ru file:
require 'plezi'
class WebsocketSample
# HTTP index
def index
'Hello World!'
end
# called when Websocket data is recieved
#
# data is a string that contains binary or UTF8 (message dependent) data.
def on_message(data)
puts "Websocket got: #{data}"
write data
end
end
Plezi.route '/', WebsocketSample
run Plezi.app
To run the server, call (ignore the $ sign, it marks this code as terminal code):
$ iodine
notice: Iodine requires a BSD / Unix / Linux machine, such as macOS, Ubuntu, etc'. It won't work on windows.
I am using Aurora 17, Chrome 22 and Firefox 16 and I am trying to create a simple chat app. I am using Node 0.8.9.
Firefox is getting the error that it cannot connect giving the error
Firefox can't establish a connection to the server at ws://localhost/.
I also tried it with the port and it have the same message
Firefox can't establish a connection to the server at ws://localhost:4444/.
Here is my code:
Server Code:
var http = require('http');
var net = require('net');
function onRequest(req, res) {
// Does enough to render a page and javascript
}
http.createServer(onRequest).listen(4444);
var socket = new net.Socket();
socket.connect(4444, "localhost", function(){
console.log("Socket Connected");
});
socket.on("message", function(message){
console.log(message);
});
Client Code:
var WebSocket = window.WebSocket || window.MozWebSocket;
var connection = new WebSocket('ws://localhost:4444');
connection.onopen = function() {
// Never runs
alert("This never runs :(")
}
connection.onerror = function(error) {
// Always runs here
console.log(error)
}
I get an output that the Socket is connect from the log statement on the server but Firefox cannot connect to the socket.
On Chrome, there is no error but the "onopen" is never fired. Using connection.send("a message") does not send anything to the server and returns false.
You're creating an ordinary TCP client socket in your server code and connecting it to your HTTP server. That's not at all the same thing as creating a WebSocket server that a browser can connect to. Use a library designed for the purpose (socket.io is very commonly used, since it can fall back to alternate transports when a browser doesn't support WebSockets).
Question: Is it possible to cluster an application which is using Socket.io for WebSocket support? If so what would be the best method of implementation?
I've built an application which uses Express and Socket.io, built on Node.js. I'd like to incorporate clustering to increase the amount of requests that my application can process.
The following causes my application to produce a socket handshake error...
var cluster = require('cluster');
var numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('death', function(worker) {
console.log('worker ' + worker.pid + ' died');
});
} else {
app.listen(3000);
}
A console log shows that socket.io is being started up multiple times.
jack#jack:~$ node nimble/app.js
info - socket.io started
info - socket.io started
info - socket.io started
info - socket.io started
info - socket.io started
info - socket.io started
info - socket.io started
info - socket.io started
info - socket.io started
Socket.io is currently being set up in the top of my server code using:
var io = require('socket.io').listen(app);
The websocket authorization code:
//Auth the user
io.set('authorization', function (data, accept) {
// check if there's a cookie header
if (data.headers.cookie) {
data.cookie = parseCookie(data.headers.cookie);
data.sessionID = data.cookie['express.sid'];
//Save the session store to the data object
data.sessionStore = sessionStore;
sessionStore.get(data.sessionID, function(err, session){
if(err) throw err;
if(!session)
{
console.error("Error whilst authorizing websocket handshake");
accept('Error', false);
}
else
{
console.log("AUTH USERNAME: " + session.username);
if(session.username){
data.session = new Session(data, session);
accept(null, true);
}else {
accept('Invalid User', false);
}
}
})
} else {
console.error("No cookie was found whilst authorizing websocket handshake");
return accept('No cookie transmitted.', false);
}
});
Console log of the error message:
Error whilst authorizing websocket handshake
debug - authorized
warn - handshake error Error
What is your sessionStore? If it's the MemoryStore shipped with connect, then sessions won't be shared between your workers. Each worker will have it's own set of sessions, and if a client connects to another worker, you wont find their session. I suggest you take a look at e.g. connect-redis for sharing sessions between processes. Basic usage:
var connect = require('connect')
, RedisStore = require('connect-redis')(connect);
var app = connect.createServer();
app.use(connect.session({store: new RedisStore, secret: 'top secret'});
Of course, this requires that you also set up redis. Over at the Connect wiki there are modules for CouchDB, memcached, postgres and others.
This only solves part of your problem, though, because now you have sessions shared between workers, but socket.io has no way of sending messages to clients which are connected to other workers. Basically, if you issue a socket.emit in one worker, that message will only be sent to clients connected to that same worker. One solution to this is to use RedisStore for socket.io, which leverages redis to route messages between workers. Basic usage:
var sio = require('socket.io')
, RedisStore = sio.RedisStore
, io = sio.listen(app);
io.set('store', new RedisStore);
Now, all messages should be routed to clients no matter what worker they are connected to.
See also: Node.js, multi-threading and Socket.io
I manually applied this patch and everything works now. Waiting on upstream to fix this
https://github.com/LearnBoost/socket.io-client/pull/361/files
I'm just trying to follow the examples given and trying to get this to work.
Mockserver.js:
var io = require('socket.io').listen(8000);
io.sockets.on('connection', function(client) {
console.log('+ new client');
client.on('disconnect', function() {
console.log('- lost a client');
});
});
Mockclient.js:
var io = require('socket.io-client');
var socket = new io.connect('localhost', { port: 8000 });
socket.on('connect', function() {
console.log('connected');
});
socket.on('message', function(data) {
console.log(data);
});
I then run these pair with node Mockserver.js and node Mockclient.js on another terminal
info - socket.io started
debug - client authorized
info - handshake authorized 14797776461130411158
debug - setting request GET /socket.io/1/websocket/14797776461130411158
debug - set heartbeat interval for client 14797776461130411158
debug - client authorized for
debug - websocket writing 1::
+ new client
debug - set close timeout for client 14797776461130411158
***************************** error occurs here ****************
info - socket error Error: write EPIPE
at errnoException (net.js:632:11)
at Object.afterWrite [as oncomplete] (net.js:470:18)
****************************************************************
debug - setting request GET /socket.io/1/xhr-polling/14797776461130411158?t=1325912082073
debug - setting poll timeout
debug - discarding transport
debug - cleared close timeout for client 14797776461130411158
debug - cleared heartbeat interval for client 14797776461130411158
debug - clearing poll timeout
info - transport end
debug- set close timeout for client 14797776461130411158
debug - cleared close timeout for client 14797776461130411158
at this point I stopped Mockclient.js
- lost a client
debug - discarding transport
The only output for "node Mockclient.js" is
The "sys" module is now called "util". It should have a similar interface.
What's causing the socket exception? I'm probably missing something pretty obvious. Also, can somebody else try my code to see if the errors on their machine as well? The code inside socket.on('connect'... isn't triggering either. I don't exactly know why.
Apply this patch
https://github.com/LearnBoost/socket.io-client/pull/361/files