I'm working on SocketIO with ReactJS vie a chat app.
When emitting message to my server my client doesn't receive the response of my server. The console.log controlling the mechanism is never displayed.
I can't figure out why since I follow exactly the SocketIO blueprint.
here my client.js :
send= (e) => {
e.preventDefault();
const socket= io.connect(this.state.endpoint);
socket.emit("message", () => {
message: "hey !"
})
console.log("send ended")
}
componentDidMount(){
const socket= io.connect(this.state.endpoint);
socket.on("new_message", (message) => {
console.log("new message ", message)
})
socket.on("user_connected", (message) => {
console.log(message)
})
}
here my server.js :
client.on("message", (message) => {
client.emit("new_message", message)
})
Any hint would be great,
Thanks
The reason for your problem is that you essentially have multiple instances of socket connections created over the life span of your client component.
From the server's perspective, the "new_message" is being emitted to the socket that you created in your components send arrow function. Because that socket instance does not listen to "new_message", you're therefore not going to see the expected log messages in the console.
Perhaps you could consider refactoring your client component code like this, to connect a single socket, and use that as a single means of sending and listening to messages from the server?
class YourComponent extends Component {
// Add socket field to component class
socket : ''
// Note that the send method is not an arrow function here, so
// care should be taken to consider how you invoke send() if
// your current implementation relies on this being an arrow function
function send(e) {
e.preventDefault();
const socket = this.state.socket // UPDATE: Access socket via state
// Send messages to server via the same socket instance of this class
if(socket) {
socket.emit("message", () => {
message: "hey !"
})
console.log("send ended")
}
}
function componentDidMount(){
const socket = io.connect(this.state.endpoint)
socket.on("new_message", (message) => {
console.log("new message ", message)
})
socket.on("user_connected", (message) => {
console.log(message)
})
// UPDATE: Connect the socket, and hold a reference for reuse by the component class
// instance via the component's state (seeing you can't add a class field for this)
this.setState({ socket : socket })
}
}
Related
The socket have to listen to the server when i send a new message.
The messages arrive because when i refresh the page i can see them.
Server side socket i know is working find because in other frontend app the same client socket is working find.
This socket is called every time the user select a new chat in the web.
Here is the socket service in my app:
import openSocket from "socket.io-client";
function connectToSocket() {
return openSocket("http://localhost:8080");
}
export default connectToSocket;
And here is the code executed when user select a chat:
// how i am importing the socket
import openSocket from "../../Services/socket-io"
async function fetchMessages(ticketId) {
try {
const { data } = await api.get("/messages/" + ticketId, {
params: { pageNumber },
});
if (ticketId === data.ticket.id) {
await loadMessages(data, ticketId);
}
listenMessages(ticketId);
} catch (err) {
Toast.ToastError("Error trying to load messages");
}
};
function listenMessages(ticketId) {
const socket = openSocket();
socket.on("connect", () => socket.emit("joinChatBox", ticketId));
socket.on("appMessage", (data) => {
if (data.action === "create") {
console.log(data);
}
if (data.action === "update") {
console.log(data);
}
});
}
What i already tried:
Use the same socket version both in client and server (3.0.5).
Calling listenMessages every time a message is sent.
socket.io-client version 4.
I have GraphQL Subscriptions on my Apollo server that I want to close after the user logs out. The initial question is whether we should close this (socket) connections on the client side or in the backend.
On the front-end, I am using Angular with Apollo Client and I handle GraphQL subscriptions by extending the Subscription class from apollo-angular. I am able to close the subscription channels with a typical takeUntil rxjs implementation:
this.userSubscription
.subscribe()
.pipe(takeUntil(this.subscriptionDestroyed$))
.subscribe(
({ data }) => {
// logic goes here
},
(error) => {
// error handling
}
);
However, this does not close the websocket on the server, which If I'm right, will result in a subscription memory leak.
The way the Apollo Server (and express) is set up for subscriptions is as follows:
const server = new ApolloServer({
typeDefs,
resolvers,
subscriptions: {
onConnect: (connectionParams, webSocket, context) => {
console.log('on connect');
const payload = getAuthPayload(connectionParams.accessToken);
if (payload instanceof Error) {
webSocket.close();
}
return { user: payload };
},
onDisconnect: (webSocket, context) => {
console.log('on Disconnect');
}
},
context: ({ req, res, connection }) => {
if (connection) {
// set up context for subscriptions...
} else {
// set up context for Queries, Mutations...
}
When the client registers a new GraphQL subscription, I always get to see console.log('on connect'); on the server logs, but I never see console.log('on Disconnect'); unless I close the front-end application.
I haven't seen any example on how to close the websocket for subscriptions with Apollo. I mainly want to do this to complete a Logout implementation.
Am I missing something here? Thanks in advance!
I based my solution based on this post
Essentially, the way we created the Subscription with sockets was using subscriptions-transport-ws
export const webSocketClient: SubscriptionClient = new
SubscriptionClient(
`${environment.WS_BASE_URL}/graphql`,
{
reconnect: true,
lazy: true,
inactivityTimeout: 3000,
connectionParams: () => ({
params: getParams()
})
}
);
As specified in the question, I wanted to unsubscribe all channels and close the subscription socket connection before logout of the user. We do this by using the webSocketClient SubscriptionClient in the logout function and call:
webSocketClient.unsubscribeAll();
webSocketClient.close();
Am still learning vuex, i have a 3 export method connect to mqtt broker with call backs to console out.
The thing after page refresh the client disconnect and client.on("end/disconnect") is not fired.
was thinking if there was a way from software design perspective of view, on how to implement it with vuex to track client connection status and act accordingly.
EDIT:
here is my current code.
export let client = null
export var status = 'initial'
export var startSub = ({username, password}) => {
client = mqtt.connect("ws://192.168.1.30:9001/mqtt",{
clientId: `mqttjs_ + ${Math.random().toString(16).substr(2, 8)}`,
username: username,
password: password
})
client.on("connect", () => {
status = client.connected
client.subscribe('hello/#')
console.log("startSub,Subsecribed")
}).on('error', err => {
status = client.connected
console.log("error", err)
client.end()
}).on("message", (topic, message) => {
status = client.connected
console.log(`topic, ${topic}, msg:: ${JSON.parse(message)}`);
})
//on disconnect call bacl
client.on("disconnect", (packet)=>{
status = client.connected
console.log('client of discconect call back', packet)
})
//on disconnect call bacl
client.on("end", function () {
console.log('client of end call back')
})
}
now when the page refresh the client automatically disconnect.
on my vuex store, if i import status, as state, its useless to track because after refresh its always initial. to be more clear my aim is to after refresh to keep the connection active without it being disconnected.
Here's my methods to open/close socket connection:
methods: {
connect () {
this.$socket.onopen = () => {
this.status = 'connected'
this.$socket.onmessage = ({data}) => {
this.$socket.send(this.message)
console.log({ event: "Recieved message", data })
}
}
this.$socket.onclose = (e) => {
console.log('Socket is closed. Reconnect will be attempted in 1 second.')
setTimeout(() => {
this.connect()
}, 1000)
}
},
disconnect () {
this.$socket.close()
this.status = 'disconnected'
}
}
I'm not using socket.io etc, just built in Websocket object.
When i manually call disconnect () method - it closes the connection as expected, but when i send message again - it says that connection is closed. Since i call connect () in a mounted, then it won't reconnect if i don't refresh the page.
I've tried using watcher:
watch: {
'$socket': 'connect'
}
But no effect. Is there a way to watch for websocket connection status? If it's closed - call connect (), if it's error'ed - call connect () to reconnect it.
Your connect() method does nothing to "reconnect" to the WebSocket server. So calling this.connect() simply rewrites your onopen handler.
You have to take the steps necessary to reconnect to the WebSocket server. The most excellent answer to this question does a great job of explaining a great structure for your code:
vue: emitting global events from websocket listener
Unfortunately, it doesn't answer your specific question. So I've forked the sandbox from that answer and added the modified code below that allows you to achieve your goal.
import Vue from "vue";
const url = "wss://echo.websocket.org";
let socket;
const emitter = new Vue({
methods: {
send(message) {
if (1 === socket.readyState) socket.send(message);
},
close() {
if (1 === socket.readyState) {
emitter.$emit("message", "Closing Socket.");
socket.close();
socket = null; // prevent memory leak
}
},
connect() {
socket = new WebSocket(url);
socket.onmessage = function(msg) {
emitter.$emit("message", msg.data);
};
socket.onerror = function(err) {
emitter.$emit("error", err);
};
emitter.$emit("message", "Openning Socket.");
}
}
});
emitter.connect();
export default emitter;
To see how this service is used, check out index.js in the running sample is here:
https://codesandbox.io/s/ry4993q654
You should check the close status code before reconnecting.
e.code === 1e3 || e.code === 1001 || e.code === 1005
In my frontend I'm using the sails.io.js wrapper. I'm trying to connect to my backend and listen for notifications. This works fine when I'm using blast to send out to all connected sockets, but broadcasting does nothing.
However, joining a room seems to work as the callback from the join function gets executed without any errors.
The frontend part:
import * as socketIoClient from 'socket.io-client'
import * as sailsIo from 'sails.io.js'
const io = sailsIo(socketIoClient)
io.sails.url = 'localhost:1337'
io.sails.environment = process.env.NODE_ENV || 'development'
io.sails.useCORSRouteToGetCookie = false
io.socket.get('/notification/join', (data, jwRes) => {
console.log('Server responded with status code ' + jwRes.statusCode + ' and data: ', data);
})
io.socket.on('notification', data => {
console.log('Server says: ', data);
})
My Sails Controller:
export const join = async (req, res) => {
if (!req.isSocket) {
return res.badRequest();
}
// Join a user specific notification room
sails.sockets.join(req, 'notification', error => {
if (!error) console.log('Everything went fine')
return res.send('Connected') // this works so far
})
// Send events each second
setInterval(() => {
sails.sockets.broadcast('notification', { data: 'Real notification' }) // This never works
sails.sockets.blast('notification', {data: 'BLAST'}) // This works perfectly
}, 1000)
}
Any suggestion on what goes wrong? Like I said, the callback of joining the room gets executed without an error, also the blast call works fine and the frontend is able to receive the message.