I'm trying to build a chat application using Laravel, with node js server, socketio and redis. What I have is this:
Client JS:
var socket = io('http://localhost:3005');
var room = '17';
$("#send").click(function(){
content = $("textarea").val();
id =$("#id").val();
$.ajax({
url: "{{route('send.message')}}",
method: "POST",
data: {content, id, room},
success: function(){
}
});
});
socket.on('cacad', function(message){
console.log(message); //multiple copies here
});
socket.on('connect', function(){
console.log("Connected!");
socket.emit('room', room);
});
Laravel Controller:
public function sendMessage(Request $request){
event(new EventName($request->all()));
$message = new Message;
$message->message = $request->content;
$redis = LRedis::connection();
$redis->publish('chat-channel', json_encode($request->all()));
$message->save();
}
Node Server:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var redis = require('ioredis');
var myMessage;
var redis_client = redis.createClient();
redis_client.subscribe('chat-channel');
io.on('connection', function(socket) {
redis_client.on('message', function(channel, message) {
var myData = JSON.parse(message);
socket.broadcast.to('17').emit('cacad', 'u i u a a');
});
socket.on('room', function(room){
socket.join(room);
});
socket.on('disconnect', function(){
console.log("disconnected!");
});
});
http.listen(3005, function() {
console.log('Listening on Port 3005');
});
I am trying to broadcast a message u i u a a in room 17. But when I receive it in the console, it shows multiple copies of it, 2x-4x. In the Laravel controller I publish a message using redis and I subscribe to it in node server. It is received successfully, but the problem lies with the multiple copies of the message (client side).
Please where is it wrong? Thank you :)
I'm pretty convinced I found the issue although I had to search a bit about those simple API usage because I'm not using the library lately.
Looking at the docs it's explain your issue pretty clearly.
Here you are listening to a new connection,
io.on('connection', function(socket) {
If the client asked to join to a specific room, you join him:
socket.on('room', function(room){
socket.join(room);
So far it's like the docs:
io.on('connection', function(socket){
socket.join('some room');
});
But your issue is with your emit, on each client connection, you listen to a message from your redis. Then you broadcast it to the room with an emit of the connected client.
Instead of that, you can do this:
io.on('connection', function(socket) {
socket.on('room', function(room){
socket.join(room);
});
socket.on('disconnect', function(){
console.log("disconnected!");
});
});
redis_client.on('message', function(channel, message) {
var myData = JSON.parse(message);
io.to('17').emit('cacad', 'u i u a a');
});
I think this happens on the socket-io-client side. not on the server-side. when I was using react-js for the client-side. I received the same message multiple times.
with the same server, I imported socket-io-client 4.4.1 in the vanilla js front-end project. then I didn't get multiple messages... :)
try use latest socket io client versions. i think they have fixed the issue in the latest versions..
Related
I am new to Nodejs and Socket.io, and this is first time when I am creating any chat application, so pardon me if I am asking some silly question.
In my web I have to kind of chat services, one is Live debate which is kind of chat room, and another one is private messaging like Facebook Messenger.
I have created both, in private messenger before showing the message I am checking the conversation Id, it's working quite appropriately. Live debate is also working appropriately.
But there is a issue, any message sent in private messenger displays in live debate window also. So I change the Server.js file for messenger and also changed the listening port, now the listening port for live debate is 3000 and for messenger is 8050, but still Live debate receiving the messenger messages.
Am I doing this in wrong way? Is there any other way to run two chat applications ?
I am using this server code
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var redis = require('redis');
server.listen(3000);
io.on('connection', function (socket) {
console.log("client connected");
var redisClient = redis.createClient();
redisClient.subscribe('message');
redisClient.on("message", function(channel, data) {
console.log("mew message add in queue "+ data+ " channel");
socket.emit(channel, data);
});
socket.on('disconnect', function() {
redisClient.quit();
});
});
With using Namespace
server.js
var nsp = io.of('/debate')
nsp.on('connection', function (socket) {
console.log("client connected"+socket.id);
var redisClient = redis.createClient();
redisClient.subscribe('message');
var redisClient1 = redis.createClient();
redisClient1.subscribe('debate');
redisClient.on("message", function(channel, data) {
console.log("mew message add in queue "+ data+ " channel");
nsp.emit(channel, data);
});
socket.on('disconnect', function() {
redisClient.quit();
});
});
client code
var socket = io.connect('http://localhost:3000/debate');
socket.on('message', function (data) {
data = jQuery.parseJSON(data);
console.log(data.user);
$( "#messages" ).append( "<strong>"+data.user+":</strong><p>"+data.message+"</p>" );
$('#messages').animate({
scrollTop: $('#messages').get(0).scrollHeight}, 200);
});
socket.io supports the use of different namespaces. You should use that feature instead of creating two individual servers. After that you can use socket.emit to that specific namespace. For more information see the documentation: https://socket.io/docs/rooms-and-namespaces/
It's not too difficult, I wrote a chat app, as I think everyone does when they start nodejs, but nodeJS has rooms which are quite easy to use.
io.on('connection', function(client){
client.on('room_connection', function(id){
client.join(id); // User joins room.
})
client.on('message', function(data){
io.to(data.room).emit('message', {message:data.message, client:client.conn.id});
});
})
This is pretty much all you need. This works for PM's since u simply won't allow multiple users to join this chatroom, and normal chatrooms which multiple users can join.
I'm trying to build a secure private facebook like messaging system using laravel, redis pub/sub and socket io. All the console.log functions log the messages to say everything went through on the node server and everything works fine untill I try and emit the message to the client. When I try to emit the message to the client nothing at all happens.
Although I don't think my controller method on laravel is of significance here it is anyway. You can skip the Laravel controller if it's not necessary
Laravel Message Controller:
public function store($id, Request $request)
{
//Stores message in $message
//Connects redis
$redis = Redis::connection();
$data = ['message' => $request->input('message'), 'user' => Auth::user()->name,
'room' => $message->conversation_id];
$redis->publish('message', json_encode($data));
return Redis::get('message');
response()->json([]);
}
Redis stuff being published in JSON output:
{message: 'some message', user: 'john doe', room: 1}
Node Server
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var redis = require('redis');
server.listen(3000);
io.on('connection', function (socket) {
console.log("client connected");
var redisClient = redis.createClient();
redisClient.subscribe('message');
redisClient.on("message", function(channel, data) {
data = JSON.parse(data);
socket.join(data.room, function() {
console.log('Joined room '+data.room);
});
console.log("new message add in queue "+ data.room + " channel");
socket.to(data.room).emit(channel, data);
});
socket.on('disconnect', function() {
redisClient.quit();
});
});
Client Side JS
var socket = io.connect('http://localhost:3000');
socket.on('message', function (data) {
//Just console the message and user right now.
console.log(data.message+" " + data.user);
});
I don't know if my problem is that I'm not specifying the room on the client. If so is there any way of emitting the data to the client without specifying the room in the client side code? Why can't I emit the message to the client?
I am working on realtime data visualization application using node.js, express and socket.io.
Requirement:
Have to emit the events based on the client request.
For example: If user enter the url as http://localhost:8080/pages socket.io should emit the topic pages to client and another user request for http://localhost:8080/locations socket should emit location to that particular user.
Code
var server = app.listen("8080");
var socket = require('socket.io');
var io = socket.listen(server);
var config = {};
io.on('connection', function (socket) {
config.socket = io.sockets.socket(socket.id);
socket.on('disconnect', function () {
console.log('socket.io is disconnected');
});
});
app.get('/*', function(req, res) {
var url = req.url;
var eventName = url.substring('/'.length);
//pages and locations
config.socket.volatile.emit(eventName, result);
});
Client Code:
//No problem in client code.Its working correctly.
Sample code as follows
socket.on('pages', function (result) {
console.log(result);
});
Problem:
It is emitting pages and locations to both the clients.
Any suggestion to overcome this problem.
I couldn't understand your approach on this, but because you said you're rendering different pages, It means you can serve different code, so what about doing it like this:
Server Side:
var server = app.listen("8080");
var socket = require('socket.io');
var io = socket.listen(server);
var config = {};
app.get('/pages', function(req, res) {
res.render('pages.html');
});
app.get('/locations', function(req, res) {
res.render('locations.html');
});
io.on('connection', function (socket) {
socket.on('pagesEvent', function(data){
socket.volatile.emit('pages', {your: 'data'});
});
socket.on('locationsEvent', function(data){
socket.volatile.emit('locations', {your: 'data'});
});
});
On Client side:
pages.html:
socket.on('connect', function(){
socket.emit('pagesEvent', {});
});
socket.on('pages', function(data){
// do stuff here
});
locations.html:
socket.on('connect', function(){
socket.emit('locationsEvent', {});
});
socket.on('locations', function(data){
// do stuff here
});
You are doing it wrong, WebSockets supposed to work same in both directions. Client emit event to Server, server emit back to Client/Subscribers.
The way you are doing things, seems like a way of implementing API, but for some reason you are trying to implement it with WebSockets, instead of XHR.
I'm trying to write a basic chat application with Node.js (Express), and Socket.io. Everything 'seems' to be working, but my socket server seems to be only 'sending' the message back to the original sender. Here is my socket code:
var client = io.listen(app);
client.sockets.on('connection', function (socket) {
socket.on('message', function (data) {
console.log(data);
socket.send(data);
});
});
And here is my client side code:
$(document).ready(function() {
var socket = new io.connect('http://localhost:3000');
socket.on('connect', function() {
socket.send('A client connected.');
});
socket.on('message', function(message) {
$('#messages').html('<p>' + message + '</p>' + $('#messages').html());
console.log(socket);
});
$('input').keydown(function(event) {
if(event.keyCode === 13) {
socket.send($('input').val());
$('input').val('');
}
});
});
Help is appreciated.
Use client.sockets.emit instead of socket.emit. It will emit to every connected client (broadcast), using the socket object only sends to the specific client.
Server side, I think you want:
socket.broadcast.emit(data);
instead of:
socket.send(data);
See "Broadcasting Messages" at the bottom of the "How to use" page. :)
I'm trying out Websockets/Node.js/Socket.io/Express for the first time and I'm trying to create a simple chat program. Everything runs fine and I see both clients in my node termial.
But when I try to execute my socket.send(), I get an error in Firefox (socket.send is not a function). It doesn't complain about socket.connect() so I know the socket.io.js is loaded.
Here is my server code:
var sys = require('util');
var express = require('express');
var io = require('socket.io');
var app = express.createServer();
app.listen(8080);
app.use(express.static(__dirname));
app.get('/', function (req, res) {
res.render('index.html', {
title: 'Chat'
});
});
var socket = io.listen(app);
socket.on('connection', function (client) {
client.on('message', function (message) {
console.log("Message: " + JSON.stringify(data));
socket.broadcast(message);
});
client.on('disconnect', function () {});
});
My client code:
<script src="http://localhost:8080/socket.io/socket.io.js"></script>
var socket = new io.Socket("http://localhost:8080");
socket.connect();
Then I do some code to get the chat message and send it.
socket.send(JSON.stringify(values));
Explanations
You haven't initialized Socket.io correctly on the server-side and client-side.
Client Side
new io.Socket("http://localhost:8080"); doesn't give you the object that you want, you need new io.connect("http://localhost:8080");.
You need to wait until the client is connected to the server before sending a message.
Server side
socket is the object send back by Socket.IO, you need to use socket.sockets to have access to on.
To broadcast a message, you need to use the client object like this: client.broadcast.send()
The variable data doesn't exist on your broadcast. You probably mean message.
Solution
Server
var sys = require('util'),
express = require('express'),
io = require('socket.io'),
app = express.createServer();
app.listen(8080);
app.use(express.static(__dirname));
app.get('/', function (req, res) {
res.render('index.html', {
title: 'Chat'
});
});
var io = io.listen(app);
io.sockets.on('connection', function (client) {
client.on('message', function (message) {
console.log("Message: " + JSON.stringify(message));
client.broadcast.send(message);
});
client.on('disconnect', function () {});
});
Client
<script src="http://localhost:8080/socket.io/socket.io.js"></script>
<script>
var socket = new io.connect("http://localhost:8080"),
connected = false;
socket.on('connect', function () {
connected = true;
});
// Use this in your chat function.
if (connected) {
socket.send(JSON.stringify(values));
}
</script>
socket.broadcast(message); should be io.sockets.emit('key', message);
when you use the socket object passed in threw the connect event your only emitting information to that client, to emit to all clients you have to use io.sockets.emit().
also with socket.send(JSON.stringify(values)); I think you want to do socket.emit(namespace, data);
see my connection file from one of my projects here: https://github.com/AdminSpot/HangoutCanopy/blob/master/javascripts/connection.js
You have to wait for socket.io to connect on the client side
var socket = new io.Socket("http://localhost:8080");
socket.connect();
socket.on('connect', function() {
socket.emit('event', data);
});