I'm new to Socket i try to receive image from server using socket-client-io . i can able to get image but on server side it shows multiple user created but i'm only seeing it in one tab but it shows like 10 users connected(like that)
Code:
const socket = io('ws://localhost:5000')
socket.on('connnection', () => {
console.log('connected to server');
})
socket.on('disconnect', () => {
console.log('Socket disconnecting');
})
useEffect(() => {
const handler = (message) => {
setImage(message.image);
console.log(message);
};
socket.on("send_image", handler);
return () => socket.off("send_image", handler); // assuming `.off` deregisters a callback
}, []);
it works fine and i can get images but it create multiple connection for single tab kindly guide me how to avoid this
Related
I am using the following server code to retrieve data from a postgres db:
const express = require('express')
const app = express()
const server = require('http').createServer(app);
const pool = require("postgresql");
const WebSocket = require('ws');
const wss = new WebSocket.Server({ server:server });
const getTempData = async () => {
try {
const tempData = await pool.query("select country, temp from my_temp_table");
return JSON.stringify(tempData.rows)
} catch(err) {
console.error(err.messasge);
}
}
wss.on('connection', async (webSocketClient) => {
console.log('A new client Connected!');
const tempDetails = await getTempData();
webSocketClient.send(tempDetails);
webSocketClient.on('message', (message) => {
console.log('received: %s', message);
});
});
server.listen(3000, () => console.log(`Listening on port :3000`))
Now on the client side, I have created the following websocket connection to localhost 3000.
When first rendering the below client code, the data displays where I also get all the console log messages, i.e. ws opened, getting data.... and finally console logging the actual data.
isPaused is also set to false.
The problem I'm facing and unsure what the issue is, is that I expected to see my client page update the country/temp data (no page refresh), when I updated the country/temp values in my_temp_table database table, but it didn't.
The result that I expected was that via the websocket, anytime my table on the server-side updated, the client would update the tempData, via the second useEffect hook below.
I basically would like the client to pull in and display changes from the server via websocket when the data changes in the backend db table.
import React, { useState, useEffect, useRef } from 'react';
export default function Temperature() {
const [isPaused, setPause] = useState(false);
const [tempData, setTempData] = useState([]);
const [name, setName] = useState(null);
const ws = useRef(null);
useEffect(() => {
ws.current = new WebSocket("ws://localhost:3000");
ws.current.onopen = () => {
console.log("ws opened");
}
ws.current.onclose = () => console.log("ws closed");
return () => {
ws.current.close();
};
}, []);
useEffect(() => {
if (!ws.current) return;
ws.current.onmessage = e => {
if (isPaused) return;
console.log("getting temp data....");
const data = JSON.parse(e.data);
setTempData(data)
console.log("data: ",data);
};
}, [isPaused]);
return (
<div>
<button onClick={() => setPause(!isPaused)}>
{isPaused ? "Resume" : "Pause"}
</button>
{ tempData?
tempData.map((data, i) => (
<div>
<span>{data.country}</span>
<span>{data.temp}</span>
</div>
))
: null }
</div>
)
}
The code is executing only once because there are no recurrying calls to the web socket send event. When the web socket is created it gets the data from the database and sends it, and thats it.
You probably want some kind of action that triggers this event multiple times. For example, in your code:
wss.on("connection", async webSocketClient => {
console.log("A new client Connected!");
setInterval(() => {
const timeNow = Date.now();
webSocketClient.send(
JSON.stringify([
{ country: "country-a", temp: timeNow },
{ country: "country-b", temp: timeNow },
])
);
}, 1000);
webSocketClient.on("message", message => {
console.log("received: %s", message);
});
});
I see you are using some package to pool from a PostgreSQL db. Take a look at this other example.
How would your clients know if there is any change in database on server side ?
You can create an event that triggers each time a particular data changes and listen to those event on your client sockets. Like you did with onmessage event in your current code.
You can render the react component based on this event.
I am new in react js development and try to integrate WebSocket un my app.
but got an error when I send messages during connection.
my code is
const url = `${wsApi}/ws/chat/${localStorage.getItem("sID")}/${id}/`;
const ws = new WebSocket(url);
ws.onopen = (e) => {
console.log("connect");
};
ws.onmessage = (e) => {
const msgRes = JSON.parse(e.data);
setTextMessage(msgRes.type);
// if (msgRes.success === true) {
// setApiMessagesResponse(msgRes);
// }
console.log(msgRes);
};
// apiMessagesList.push(apiMessagesResponse);
// console.log("message response", apiMessagesResponse);
ws.onclose = (e) => {
console.log("disconnect");
};
ws.onerror = (e) => {
console.log("error");
};
const handleSend = () => {
console.log(message);
ws.send(message);
};
and got this error
Failed to execute 'send' on 'WebSocket': Still in CONNECTING state
Sounds like you're calling ws.send before the socket has completed the connection process. You need to wait for the open event/callback, or check the readyState per docs and queue the send after the readyState changes i.e after the open callback has fired.
Not suggesting you do this, but it might help:
const handleSend = () => {
if (ws.readyState === WebSocket.OPEN) {
ws.send()
} else {
// Queue a retry
setTimeout(() => { handleSend() }, 1000)
}
};
As Logan has mentioned my first example is lazy. I just wanted to get OP unblocked and I trusted readers were intelligent enough to understand how to take it from there. So, make sure to handle the available states appropriately, e.g if readyState is WebSocket.CONNECTING then register a listener:
const handleSend = () => {
if (ws.readyState === WebSocket.OPEN) {
ws.send()
} else if (ws.readyState == WebSocket.CONNECTING) {
// Wait for the open event, maybe do something with promises
// depending on your use case. I believe in you developer!
ws.addEventListener('open', () => handleSend())
} else {
// etc.
}
};
I guess you can only send data with ws only if it's already open, and you do not check when it's open or not.
Basically you ask for an openning but you send a message before the server said it was open (it's not instant and you do not know how many time it can take ;) )
I think you should add a variable somithing like let open = false;
and rewrite the onopen
ws.onopen = (e) => {
open = true;
console.log("connect");
};
and then in your logic you can only send a message if open is equal to true
don't forget the error handling ;)
I'm trying to use peerjs to connect an host with a client. I have two files (one for the host, one for the client). The host will generate a peerId which the client is using to connect to him. It's basically the tutorial from here.
host.html
const peer = new Peer()
peer.on('open', () => {
console.log('ID: ' + peer.id)
})
peer.on('connection', (conn) => {
conn.on('data', (data) => {
// Will print 'hi!'
console.log(data)
})
conn.on('open', () => {
conn.send('hello!')
})
})
<script src="https://unpkg.com/peerjs#1.3.1/dist/peerjs.min.js"></script>
client.html
const peer = new Peer()
const conn = peer.connect('1781a113-d095-4be5-9969-b80d9c364f6b')
conn.on('open', () => {
conn.send('hi!')
})
<script src="https://unpkg.com/peerjs#1.3.1/dist/peerjs.min.js"></script>
The client is not connecting nor sending messages. I tried to use their example to connect to my host and this was working. I was able to send messages to the host. What is the issue with my client?
It's a little vague in the documentation, but if you don't wait on the open event, messages to the server will be queued. I'm not sure if there is additional configuration needed to enable queuing, but simply waiting on the open event should work in your case
const peer = new Peer()
peer.on('open', () => {
const conn = peer.connect('1781a113-d095-4be5-9969-b80d9c364f6b')
conn.on('open', () => {
conn.send('hi!')
})
})
Keep in mind that the peer id generated in your 'host' will change every time you refresh the browser, so you might want to pick your own id instead of getting a random one
I'm creating a chat app with sockets where you first come to a landing page to enter your nickname, and then join the chat.
In the chatpage amongst the form and the message-area, I have a disconnect button that takes you back to the landing page. The idea is that you should be able to enter a nickname (same or new) and join the chat again (without seeing previous history).
I manage to send them to the landing page but for some reason they can't reconnect again.
Is there something I'm missing to make that work?
Listen on disconnect on server file
socket.on('disconnect', () => {
socket.broadcast.emit('user-disconnected', users[socket.id]);
delete users[socket.id];
console.log('bye bye');
});
chat.js emits disconnect and sends back to landing page
const feedbackBox = message => {
showFeedback.innerText = message;
showFeedback.classList.add('feedback-I-disconnect');
showFeedback.classList.remove('hide');
container.appendChild(showFeedback);
};
disconnectButton.addEventListener('click', event => {
if (event.target.classList.contains('disconnect-button')) {
socket.disconnect();
messageContainer.classList.add('hide');
messageForm.classList.add('hide');
disconnectButton.classList.add('hide');
appendForm();
feedbackBox('You disconnected from the chat');
}
});
name.js is the landing page
const getName = () => {
form.addEventListener('submit', e => {
e.preventDefault();
const name = nameInput.value;
socket.emit('new-user', name);
nameInput.value = '';
socket.off('name-taken');
socket.on('name-taken', () => {
feedbackBox('Nickname already taken');
});
socket.off('user-accepted');
socket.on('user-accepted', () => {
title.classList.add('hide');
nameContainer.classList.add('hide');
addMessageForm();
});
});
};
const appendForm = () => {
nameInput.classList.add('name_input');
form.appendChild(nameInput);
submitName.classList.add('submit_name');
form.appendChild(submitName);
nameContainer.appendChild(form);
nameContainer.classList.add('name_container');
nameContainer.classList.remove('hide');
title.classList.remove('hide');
title.classList.add('name_title');
container.appendChild(title);
container.appendChild(nameContainer);
getName();
};
I want to use RxJS inside of my socket.on('sense',function(data){});. I am stuck and confused with very few documentation available and my lack of understanding RxJS. Here is my problem.
I have a distSensor.js that has a function pingEnd()
function pingEnd(x){
socket.emit("sense", dist); //pingEnd is fired when an Interrupt is generated.
}
Inside my App.js I have
io.on('connection', function (socket) {
socket.on('sense', function (data) {
//console.log('sense from App4 was called ' + data);
});
});
The sense function gets lots of sensor data which I want to filter using RxJS and I don't know what should I do next to use RxJs here. Any pointers to right docs or sample would help.
You can use Rx.Observable.fromEvent (https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/fromevent.md).
Here's how I did a similar thing using Bacon.js, which has a very similar API: https://github.com/raimohanska/bacon-minsk-2015/blob/gh-pages/server.js#L13
So in Bacon.js it would go like
io.on('connection', function(socket){
Bacon.fromEvent(socket, "sense")
.filter(function(data) { return true })
.forEach(function(data) { dealWith(data) })
})
And in RxJs you'd replace Bacon.fromEvent with Rx.Observable.fromEvent.
I have experienced some strange issues using the fromEvent method, so I prefer just to create my own Observable:
function RxfromIO (io, eventName) {
return Rx.Observable.create(observer => {
io.on(eventName, (data) => {
observer.onNext(data)
});
return {
dispose : io.close
}
});
I can then use like this:
let $connection = RxfromIO(io, 'connection');
You can create an Observable like so:
var senses = Rx.Observable.fromEventPattern(
function add (h) {
socket.on('sense',h);
}
);
Then use senses like any other Observable.
Simply use fromEvent(). Here is a full example in Node.js but works the same in browser. Note that i use first() and takeUntil() to prevent a memory leak: first() only listens to one event and then completes. Now use takeUntil() on all other socket-events you listen to so the observables complete on disconnect:
const app = require('express')();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const Rx = require('rxjs/Rx');
connection$ = Rx.Observable.fromEvent(io, 'connection');
connection$.subscribe(socket => {
console.log(`Client connected`);
// Observables
const disconnect$ = Rx.Observable.fromEvent(socket, 'disconnect').first();
const message$ = Rx.Observable.fromEvent(socket, 'message').takeUntil(disconnect$);
// Subscriptions
message$.subscribe(data => {
console.log(`Got message from client with data: ${data}`);
io.emit('message', data); // Emit to all clients
});
disconnect$.subscribe(() => {
console.log(`Client disconnected`);
})
});
server.listen(3000);
ES6 one liner that i use, using ES7 bind syntax:
(read $ as stream)
import { Observable } from 'rxjs'
// create socket
const message$ = Observable.create($ => socket.on('message', ::$.next))
// translates to: Observable.create($ => socket.on('message', $.next.bind(this)))
// filter example
const subscription = message$
.filter(message => message.text !== 'spam')
//or .filter(({ text }) => text !== 'spam')
.subscribe(::console.log)
You can use rxjs-dom,
Rx.DOM.fromWebSocket(url, protocol, [openObserver], [closeObserver])
// an observer for when the socket is open
var openObserver = Rx.Observer.create(function(e) {
console.info('socket open');
// Now it is safe to send a message
socket.onNext('test');
});
// an observer for when the socket is about to close
var closingObserver = Rx.Observer.create(function() {
console.log('socket is about to close');
});
// create a web socket subject
socket = Rx.DOM.fromWebSocket(
'ws://echo.websocket.org',
null, // no protocol
openObserver,
closingObserver);
// subscribing creates the underlying socket and will emit a stream of incoming
// message events
socket.subscribe(
function(e) {
console.log('message: %s', e.data);
},
function(e) {
// errors and "unclean" closes land here
console.error('error: %s', e);
},
function() {
// the socket has been closed
console.info('socket closed');
}
);