I'm working on a chat application using angular 5 in the front end and nodeJS as back end and mongoDB to store the data. I integrated socket io to emit messages in real time
The following is the code which I'm using to develop the app. Now when a user fires a message in the text box I'm emitting the message only to the user to whom the message is sent. For example, if user1 is sending the message to user2, only user2 gets the socket event and not all the other connected users. So I found a way to emit to a particular user using the socket.id
I'm not using the same route to render all the users, I'm using different routes to different users, say "John" is the logged in user, the user can chat with other users in /message/ABC, /message/XYZ and so on.
CLIENT SIDE :
chat.component.ts
ngOnInit() {
this.loggedInUser = this.authService.getLoggedInUser();
this.route.params
.subscribe(params => {
this.username = params['username'];
this.receiveMessage();
});
}
sendMessage(message) {
const messageDetails = {
fromUsername : this.loggedInUser.username,
message : (message).trim(),
toUsername : this.username
};
this.socketService
.sendMessage(messageDetails);
}
receiveMessage() {
this.socketService
.receiveMessage()
.subscribe(
(message : Message) => {
console.log(message);
});
}
socket.service.client.ts
import * as io from 'socket.io-client';
private BASE_URL = AppConstants.ApiEndPoint.socketUrl;
private socket;
constructor() {
this.socket = io("http://localhost:3000/");
}
sendMessage(messageDetails) {
this.socket.emit('addMessage', messageDetails);
}
receiveMessage() {
return new Observable(observer => {
this.socket.on('addMessageResponse', function (message) {
observer.next(message);
});
return () => {
this.socket.disconnect();
};
});
}
SERVER SIDE :
server.js
I'm using express as middleware and passing the server instance to my socket in the server side
const http = require('http');
const app = express();
const server = http.createServer(app);
require("./server/services/socket.service.server")(server);
server.listen(port);
Socket.service.server.js
On socket event addMessage , I'm adding the message to my db and then rendering the socketId of the toUser from my user model and therefore emitting only to the intended user.
module.exports = function (server) {
var socketIo = require('socket.io');
var userModel = require("../models/user/user.model.server");
var messageModel = require("../models/message/message.model.server");
const io = socketIo(server);
io.on('connection', function(socket) {
socket.on('addMessage', function (messageDetails) {
messageModel
.createMessage(messageDetails)
.then(function (message) {
userModel
.findUserByUsername(message.toUsername)
.then(function (user) {
var socketId = user.socketId;
io.to(socketId).emit('addMessageResponse', message);
});
});
})
});
};
Now the message is emitted to the client side to the particular user. This works fine but I'm getting the message the number of times the user is connected to the client.
For example, I logged in with a user PO I'm on route /chat/ok where ok is a connected user, ok sends a message to po. PO receives the message in the console as below
screenshot 1
Now I'm navigating to chat with another user and I'm on route /chat/kk where kk is another connected user.
Now if kk sends a message to po, I'm receiving the message twice. As you can see below, I navigated to /chat/kk , I'm receiving the message twice. And similarly if I navigate to another user, I'm getting the message thrice and so on. Socket io emits a message to the user the number of times the user is getting connected to the socket.
Screenshot 2
I just want the normal workflow for the chat application and not emitting the message as many times the user is connected to the client. Is there a workaround ? I guess I'm making the mistake in the client side callback when I receive the response or I'm connecting multiple times for the same client. Can someone please help me out.
Thanks !
When you describe it as a global variable, it is incorrect at socket.io
Reason of your error is the code
constructor() {
this.socket = io("http://localhost:3000/");
}
You remove and define inside of receiveMessage function.It must work correctly.
Related
I'm currently creating a distributed chat application. Everything works fine, meaning it's possible to send messages between the clients and the server and have it broadcasted appropiately.
However, at the moment only the actual message is sent to the server. I would like to add information about the user sending the message aswell.
I could add this information whenever I send a new message, but I would prefer if I could add this information during the initial handshake and then save this information on the backend.
I've thought about sending some information in the URL, but as I only instantiate the websocket once, this does not seem like the way to go. Similarly, I thought about adding the information as the body of the request, but I read that having a body on a GET request is usually not recommended.
So my question is, am I trying to do something that I should not be going for? Should I just send information about the client on each new message that is sent to the server?
Currently, my client looks like this:
const socket = new WebSocket("ws://localhost:8080/ws");
const connect = (cb) => {
console.log("Attempting Connection...")
socket.onopen = () => {
console.log("Successfully Connected");
}
socket.onmessage = (msg) => {
console.log(msg)
cb(msg);
}
socket.onclose = (event) => {
console.log("Socket Closed Connection: ", event);
}
socket.onerror = (error) => {
console.log("Socket Error: ", error);
}
};
const sendMsg = (msg) => {
console.log("Sending msg: ", msg);
socket.send(msg);
}
And the initial connection on the backend is handled by the following:
func serveWs(pool *websocket.Pool, w http.ResponseWriter, r *http.Request) {
fmt.Println("WebSocket Endpoint Hit")
conn, err := websocket.Upgrade(w, r)
if err != nil {
fmt.Fprintf(w, "%+V\n", err)
}
client := &websocket.Client{
Name: "?????", // Obviously, I would like the actual name to be here.
Conn: conn,
Pool: pool,
}
pool.Register <- client
client.Read()
}
The normal thing to do here as selbie pointed out is to expect from the client a special first message through WebSocket. If that message is not recived or does not meet the requirements the server ends the WebSocket conn.
Using socket.onopen is very common for this task.
More on why you cant put headers:
HTTP headers in Websockets client API
If the title is not clear (I'm sorry if it's not), basically at the moment the messages that I send from the DOM to the server and from the server back to the DOM are all blue bubbles (right side). So even when I'm the receiver of the message (which should be a grey bubble on the left), I receive it on the right.
How can I differentiate between the two so that when I send a message it's blue and when I receive it it's grey (see image for example)?
I'm sorry if it's a stupid question, but I'm only getting started with NodeJs and it's still a little confusing to me sometimes.
JS SERVER SIDE
const path = require("path");
const http = require("http");
const express = require("express");
const socketio = require("socket.io");
//RUN EXPRESS SERVER
const app = express();
//RUN SERVER USING HTTP MODULE REQUIRED BY SOCKET.IO
const server = http.createServer(app);
//INIT SOCKET.IO
const io = socketio(server);
// SET STATIC FOLDER
app.use(express.static(path.join(__dirname, "public")));
//RUN WHEN CLIENT CONNECTS
io.on("connection", (socket) => {
//message only to me when I connect
socket.emit("message", "Welcome to Chat.io");
//message to other users when I connect
socket.broadcast.emit("message", "User has joined the chat");
//message to other users when I disconnect
socket.on("disconnect", () => {
io.emit("message", "User has left the chat");
});
socket.on("chatMessage", (msg) => {
io.emit("message", msg);
});
});
const PORT = 3000 || process.env.PORT;
server.listen(PORT, () => console.log(`Server running on port ${PORT}`));
JS FRONTEND
const chatForm = document.getElementById("chat-form");
//WE CAN USE THIS BECAUSE OF THE SCRIPT TAG IN chat.html
const socket = io();
//FUNCTION TO CATCH MESSAGES EMITTED FROM SERVER
socket.on("message", (message) => {
console.log(message);
outputMessage(message);
});
//MESSAGE SUBMIT
chatForm.addEventListener("submit", (e) => {
e.preventDefault();
//get chat message from DOM
const msg = e.target.elements.msg.value;
//emit chat message to server
socket.emit("chatMessage", msg);
//clear field
chatForm.reset();
});
//OUTPUT MESSAGE TO DOM FUNCTION
function outputMessage(message) {
const div = document.createElement("div");
div.classList.add("chat-row");
div.innerHTML = `
<div class="bubble-right--container">
<div class="chat-bubble chat-bubble--right">
${message}
</div>
<p class="meta">20.18</p>
</div>`;
document.querySelector(".chat-panel").appendChild(div);
}
I solved! (So happy) maybe it's not the cleanest way, so please feel to give your answer!
Basically after catching the message sent from the DOM on the server, I created two different emits:
socket.on("chatMessage", (msg) => {
socket.broadcast.emit("greyMessage", msg);
socket.emit("blueMessage", msg);
});
Then I caught them on the frontend JS using two different functions:
//CATCH GREY MESSAGES
socket.on("greyMessage", (message) => {
outputGreyMessage(message);
});
//CATCH BLUE MESSAGES
socket.on("blueMessage", (message) => {
outputBlueMessage(message);
});
Now it works!
currently i am trying to send and get the data via angular client to socket server and from socket server to angular i need to get data i able to push the data but i need to know how can i push data to the api which is there in socket server and get data from the api to socket server and emit it to client
below is my
For sending data from angular client to socket server
component code
constructor(public socketService: SocketioService){
}
ngOnInit(){
this.socketService.setupSocketConnection();
}
// For sending post request
sendMsg(){
this.socketService.sendData(this.title);
}
// For getting the request
getMsg(){
this.socketService.getMsg().subscribe(res => {
console.log(res);
})
Angular service code
import * as io from 'socket.io-client';
import { Observable } from 'rxjs';
socket;
constructor() {
}
setupSocketConnection() {
this.socket = io(environment.SOCKET_ENDPOINT);
}
// for posting data
sendData(values){
console.log(values);
this.socket.emit('my message', values);
}
//for getting data
getMsg(){
return Observable.create((observer) => {
this.socket.on('grabMsg', (message) => {
observer.next(message);
});
});
}
Node server code
const app = require('express')();
const http = require('http').createServer(app);
const io = require('socket.io')(http);
app.get('/', (req, res) => {
res.send('<h1>Hey Socket.io</h1>');
});
io.on('connection', (socket) => {
console.log('a user connected');
socket.on('disconnect', () => {
console.log('user disconnected');
});
socket.on('my message', (msg) => {
//here i want to consume api like
// localhost:3000(post) {"title":"ss"}
console.log('message: ' + msg);
});
socket.on('grabMsg', () => {
//here i want to consume api like
// localhost:3000(get)
let ms = 'max'
io.emit(ms);
});
});
http.listen(3001, () => {
console.log('listening on *:3001');
});
so here how can i send and post data in socket server
in short i will send data to from angular client to socket server then to some api
//server-side
socket.on('grabMsg', () => {
let ms = 'max'
io.emit(ms);
});
//client-side
this.socket.on('grabMsg', (message) => {
observer.next(message);
});
In the above code you are using socket.on on both client and server-side also, use one as emit as one as on according to your requirement.
And in below code you are only emitting and there is the first parameter for emitting (any text enclosed in side quote) like below code
socket.on('grabMsg', () => {
let ms = 'max'
io.emit("thatText",ms);
});
the same text(thatText) should be on client-side too, like
this.socket.on('thatText', (message) => {
console.log(message)
});
You can use the nodeJs eventEmitter API. So you can emit an event by eventEmitter when someone hits your endpoint(GET request) and listen that event inside your socket server and vice-versa.
More details:- Custom Events in Node.js with Express framework
export class LiveSocket implements OnInit {
//define a socket
public socket = new WebSocket(environment.SOCKET_ENDPOINT);
ngOnInit() {
// Add an event listener for when a connection is open
this.socket.onopen = () => {
console.log('WebSocket connection opened. Ready to send messages.');
// Send a message to the server
this.socket.send('message');
};
// Add an event listener for when a message is received from the server
this.socket.onmessage = (message) => {
//handle getting data from server
var data = JSON.parse(message.data);
console.log(data)
};
}
}
I have a Node/Vue application. I am consuming a WebSocket from Binance, a crypto exchange. I can see the quotes on the server console as I log them, I can send them to the browser for a short period of time before the client stops logging them.
Browser just using WebSocket API
Node using ws library
Node code, this I am running as it's own service as its just this.
'use strict';
const WebSocket = require('ws');
const binanceWS = new WebSocket('wss://stream.binance.com:9443/ws/btcusdt#trade')
const server = new WebSocket.Server({ port: 5002 });
//websocket connection event will return a socket you can later use
binanceWS.on("open", function() {
console.log("connected to Binance");
});
binanceWS.on('message', function(data){
console.log(data);
server.on('connection', function connection(ws){
console.log("Connected a new client");
ws.send(data);
});
server.on('closed', function (id){
console.log("connection closed");
console.log(id);
});
server.on('error', function (err){
console.log(err)
})
})
On the Client side I am using Vue and in the app.js file I have this on the created hook.
let socket = new WebSocket("ws://localhost:5002")
socket.addEventListener('message', function(event){
let quotes = JSON.parse(event.data);
console.log(quotes.p)
});
socket.addEventListener('error', function(event){
console.log("closing because " + event);
})
Right now I am only listening to the consoles in the above app.vue file.
What I see in the browser console is a lot of quotes, then they stop after a second or 2. There can be over a thousand quotes in some times. Then on occasion I see a console.log('created') that I have in a child component of app.vue. In many cases this is the last thing in the console after hundreds of quotes.
In the console.log for the server I see a lot of sessions being created with one page refresh. So much that it fills my console.
So I'm not sure I am creating the connections correcly, I am not sure if Vue is somehow stopping the console.log's?
I don't see any errors anywhere and the entire time in my server console the Binance API continues streaming.
you have to write server event listener outside binance on message handler;
then you can pass messages from binance to the server by emitting new event to the server
on receiving message from binance you can send data to all connection on the server
Or Try this code I think it will work :
'use strict';
const WebSocket = require('ws');
const binanceWS = new WebSocket('wss://stream.binance.com:9443/ws/btcusdt#trade')
const server = new WebSocket.Server({ port: 5002 });
server.on('connection', function connection(ws){
console.log("Connected a new client");
});
server.on('closed', function (id){
console.log("connection closed");
console.log(id);
});
server.on('error', function (err){
console.log(err)
})
//websocket connection event will return a socket you can later use
binanceWS.on("open", function() {
console.log("connected to Binance");
});
binanceWS.on('message', function(data){
console.log(data);
server.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
})
I have an express node.js server serving Socket.io. I would like the ability to make get requests to the express server that will automatically send a message to a channel.
var app = require('express').createServer()
, io = require('socket.io').listen(app)
app.listen(80);
app.get('/:channel/:message', function (req, res) {
//Code to create socket
socket.emit("sent from get", {channel:req.params.channel, message:req.params.message})
});
io.sockets.on('connection', function (socket) {
socket.on('sent from get', function (data) {
socket.broadcast.to(data.channel).emit('channel message', { message: data.message});
});
});
How to I create (and destroy) a socket connection in the app.get block?
(For clarity, I want to use this to send a quick message from a rails server when a particular object is saved, and have a message pushed to each appropriate user.)
io.sockets.in(req.params.channel).emit("channel message", {mes:req.params.message})
That will send a message to all users in the requested channel.
var chat = io.of('/chat').on('connection', function (socket) {
socket.emit('a message', { that: 'only', '/chat': 'will get' });
chat.emit('a message', { everyone: 'in', '/chat': 'will get' }); });
The following example defines a socket that listens on '/chat'