I am implementing WebSocket in chrome extension.
I wrote the code in background.js
var websocket;
function createWebSocketConnection() {
if('WebSocket' in window){
websocket = new WebSocket(host);
console.log("======== websocket ===========", websocket);
websocket.onopen = function() {
websocket.send("Hello");
};
websocket.onmessage = function (event) {
var received_msg = JSON.parse(event.data);
var notificationOptions = {
type: "basic",
title: received_msg.title,
message: received_msg.message,
iconUrl: "extension-icon.png"
}
chrome.notifications.create("", notificationOptions);
};
websocket.onclose = function() { alert("==== web socket closed
======"); };
}
When I open the popup screen (index.html), the method createWebSocketConnection() is invoked, which will create a WebSocket connection.
But as soon as the popup is closed, a message is print on the Server console that "Web socket is closed.
I have following questions-
Should I make WebSocket connection in content.js?
Do each web page opened will have different WebSocket?
Is there any way I can save the WebSocket object in the background.js?
What is the best practice of implementing web socket in chrome extensions?
Thanks in advance!
Hurray!
I solved this problem by modifying manifest.json
{
...
"background": {
...
"persistent": true
},
...
}
I implemented the websockets in the background.js.
Following is the code:
function createWebSocketConnection() {
if('WebSocket' in window){
chrome.storage.local.get("instance", function(data) {
connect('wss://' + data.instance + '/ws/demoPushNotifications');
});
}
}
//Make a websocket connection with the server.
function connect(host) {
if (websocket === undefined) {
websocket = new WebSocket(host);
}
websocket.onopen = function() {
chrome.storage.local.get(["username"], function(data) {
websocket.send(JSON.stringify({userLoginId: data.username}));
});
};
websocket.onmessage = function (event) {
var received_msg = JSON.parse(event.data);
var demoNotificationOptions = {
type: "basic",
title: received_msg.subject,
message: received_msg.message,
iconUrl: "images/demo-icon.png"
}
chrome.notifications.create("", demoNotificationOptions);
updateToolbarBadge();
};
//If the websocket is closed but the session is still active, create new connection again
websocket.onclose = function() {
websocket = undefined;
chrome.storage.local.get(['demo_session'], function(data) {
if (data.demo_session) {
createWebSocketConnection();
}
});
};
}
//Close the websocket connection
function closeWebSocketConnection(username) {
if (websocket != null || websocket != undefined) {
websocket.close();
websocket = undefined;
}
}
It depends on what and how you want to achieve your goal.
If one persistent WebSocket per extension is your aim, which is the most likely scenario, then create it in the background script. Then, you can relay the messages to popup/content using messaging.
If you need to talk from the content/popup page(s) directly to the server, then create it there. When content page or popup is closed, your WebSocket will be closed as well.
web socket will be closed because popup has its own context, each time you open popup its creates new object and on closing of popup, the state will be erased, you need to do this logic at background scripts! as the above developers provided some snippets!
Related
I'm trying to figure out how to implement a two separate websockets together. Not sure if this possible or not, but I have a websocket that works on it's own in from node.js file to angular another node.js file that uses Kraken (crypto exchange) websocket that also works in it's own file. I'm trying to consolidate them both together so that whenever a event onChange comes from Kraken websocket, I can relay that data to angular with angular socket.io websocket. Trying to do something like this
const webSocketClient = new WebSocket(connectionURL);
webSocketClient.on("open", function open() {
webSocketClient.send(webSocketSubscription);
});
webSocketClient.on("message", function incoming(wsMsg) {
const data = JSON.parse(wsMsg);
let io = require("socket.io")(server, {
cors: {
origin: "http://localhost:4200",
methods: ["GET", "POST"],
allowedHeaders: ["*"],
credentials: true,
},
});
io.on("connection", (socket) => {
const changes = parseTrades(data);
socketIo.sockets.emit(connection.change, changes);
Log whenever a user connects
console.log("user connected");
socket.emit("test event", JSON.stringify(changes));
});
console.log("DATA HERE", data[0]);
});
webSocketClient.on("close", function close() {
console.log("kraken websocket closed");
});
Although doing this doesnt relay the data to frontend and gives me a memory leak. Is there some way I can accomplish this?
I would probably split up the task a little bit. So have a service for the kraken websocket and maybe a service for your own socket, then have them communicate via observables, that you can also tap into from the front end to display data you want.
#Injectable()
export class KrakenService{
private webSocketClient : WebSocket | null;
private messages$ = new BehaviorSubject<any>(null); // give it a type
openConnection(){
// is connectionUrl from environment ??
this.webSocketClient = new WebSocket(connectionURL);
webSocketClient.on("open", function open() {
/// is webSocketSubscription from environment ??
webSocketClient.send(webSocketSubscription);
});
webSocketClient.on("message", function incoming(wsMsg) {
const data = JSON.parse(wsMsg);
this.messages$.next(data);
console.log("DATA HERE", data[0]);
});
webSocketClient.on("close", function close() {
console.log("kraken websocket closed");
this.webSocketClient = null;
});
}
getKrakenMessages(){
if(webSocketClient == null) this.openConnection();
return messages$.asObserbable();
}
}
So now when you want to either read the web socket messages or use them with the other socket you just subscribe to the krakenService.getKrakenMessages();
Then you can do something similar with you local service as well. Have something that opens connections, and one that emits messages. The example you showed it would open up a connection every time you got a message, so keep those logics separated. And reuse existing connection.
There is a plugin written for google-chrome that connects messages from the site to the user's computer. A mandatory requirement is that the plug-in through the background.js script keeps a constant connection, in fact, it keeps it through the port, but I cannot send the response received from the host to the site in any way.
That is, I send a message from the site to the plugin like this:
// script on the site
chrome.runtime.sendMessage(extensionId, {type: "SEND_FROM_WEB_SITE"}, function(response){
console.log(response)
})
Here I establish a permanent connection to the user's computer through the port, receive a message from the website, and try to send the response back to the site.
// script background.js
// establishing a connection
connect()
function onNativeMessage(message){
if(message.type == 'GET_FROM_HOST') {
// HERE I RECEIVE AN ANSWER FROM THE USER'S COMPUTER WHICH SHOULD BE TRANSFERRED TO THE SITE? QUESTION HOW
console.log(message);
}
}
function connect(){
port = chrome.runtime.connectNative(hostName);
port.onMessage.addListener(onNativeMessage);
}
function sendNativeMessage(message) {
port.postMessage(message);
}
// listening to the message from the website
chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse) {
if (request.type == "GET_FROM_WEB_SITE"){
// WE RECEIVE A MESSAGE FROM THE WEE SITE AND SEND IT TO THE USER'S COMPUTER, HOW DO I TRANSFER THROUGH "sendResponse" THE RESPONSE TO THE SITE
sendNativeMessage(request);
}
});
Just in case, the manifest file with permissions // manifest.json
{
"name": "app plugin",
"short_name": "app",
"description": "app",
"manifest_version": 2,
"permissions": ["nativeMessaging", "activeTab", "tabs", "storage"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
}
All you need is to remember sendResponse until the response from the native host is received and use return true to keep onMessageExternal channel open:
let savedSendResponse;
let port = chrome.runtime.connectNative(hostName);
port.onMessage.addListener(message => {
if (savedSendResponse) {
savedSendResponse(message);
savedSendResponse = null;
}
});
chrome.runtime.onMessageExternal.addListener((request, sender, sendResponse) => {
port.postMessage(request);
savedSendResponse = sendResponse;
return true; // keep the channel open until savedSendResponse is called
});
However, a complication will arise if a) your host app processes messages asynchronously and b) several tabs with the same site may be opened and each could send a message. In this case we need to separately remember each sendResponse. We can do it by assigning a temporary internal id to the messages between the host and the extension. Host app should read this id from the messages and write the same id in its responses.
let sendMap = new Map();
let port = chrome.runtime.connectNative(hostName);
port.onMessage.addListener(message => {
const send = sendMap.get(message.id);
if (send) {
sendMap.delete(message.id);
send(message);
}
});
chrome.runtime.onMessageExternal.addListener((request, sender, sendResponse) => {
request.id = performance.now();
sendMap.set(request.id, sendResponse);
port.postMessage(request);
return true; // keep the channel open until sendResponse is called
});
I'm trying to create a simple chat application just for the challenge and to learn javascript even more.
For this, I use the built-in package net to do my socket server and client.
To do it simply , I have a client that send a Stringify object when it connect to the server and I want my server to send a message to every client currently connected.
This is the structure of my message :
socket.write(JSON.stringify({
type: message type,
data: message data,
author: message author
}))
Sorry for my bad english and thanks for everyone who read this.
You can do it like this. This is taken from the ws github page https://github.com/websockets/ws
There are many ways to achieve this but in Node this is probably the best solution.
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
// Broadcast to all.
wss.broadcast = function broadcast(data) {
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
};
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(data) {
// Broadcast to everyone else.
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
});
});
I have a Node.js server that manages list of users. When new user is created, all the clients should display immediately the added user in the list.
I know how to send data to clients without request - using Websocket, but in this implementation, Websocket is not allowed.
Is it possible to update all the client's user-list without using Websocket, when new user is added in the server?
// Client side
const subscribe = function(callback) {
var longPoll = function() {
$.ajax({
method: 'GET',
url: '/messages',
success: function(data) {
callback(data)
},
complete: function() {
longPoll()
},
timeout: 30000
})
}
longPoll()
}
// Server Side
router.get('/messages', function(req, res) {
var addMessageListener = function(res) {
messageBus.once('message', function(data) {
res.json(data)
})
}
addMessageListener(res)
})
Long polling is where the client requests new data from the server, but the server does not respond until there is data. In the meantime, the client has an open connection to the server and is able to accept new data once the server has it ready to send.
Ref: http://hungtran.co/long-polling-and-websockets-on-nodejs/
There is a third way: Push Notifications
Your application should register in a Push Notification Server (public or proprietary) and then your server will be able to send messages asynchronously
You can use server-sent events with an implementation like sse-express:
// client
let eventSource = new EventSource('http://localhost:80/updates');
eventSource.addEventListener('connected', (e) => {
console.log(e.data.welcomeMsg);
// => Hello world!
});
// server
let sseExpress = require('./sse-express');
// ...
app.get('/updates', sseExpress, function(req, res) {
res.sse('connected', {
welcomeMsg: 'Hello world!'
});
});
am connecting xml socket to node.js websocket. Its showing connect message first. When a message send to server, its showing socket close error.
import flash.net.XMLSocket;
var client_socket: XMLSocket = new XMLSocket();
client_socket.connect("localhost",8080);
client_socket.addEventListener(DataEvent.DATA, on_serverData);
client_socket.addEventListener(Event.CONNECT, on_serverConnection);
client_socket.addEventListener(IOErrorEvent.IO_ERROR,IOerror);
client_socket.addEventListener(Event.CLOSE,socketclose);
client_socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR,socketsecurityerror);
function socketsecurityerror(event:SecurityErrorEvent)
{
trace("socketsecurityerror");
}
function IOerror(event : IOErrorEvent):void
{
trace("IOerror");
}
function socketclose(event : Event):void
{
trace("socketclose");
}
function on_serverConnection(event:Event)
{
trace("connected");
var o :Object= new Object();
o.hello = "initial_start" ;
// client_socket.send(JSON.stringify(o));
}
function on_serverData(event:DataEvent)
{
trace("errorrrrrrrrrr"+event.target.data);
}
What could be the issue, as its showing connect message and socketclose error only while sending data to websocket.
The below code is my websocket server.
var WebSocketServer = require('ws').Server
, wss = new WebSocketServer({ port: 8080 });
wss.on('connection', function connection(ws)
{
ws.on('message', function incoming(message) {
});
ws.on('close', function() {
});
ws.on('error', function() {
});
});
Will it be an issue with xmlsocket and websocket communication?
Thanks
XMLSocket cannot connect to a Websocket.
Websockets have a handshake and a protocol (see https://www.rfc-editor.org/rfc/rfc6455), whereas XMLSocket is just for sending and receiving XML data.
If you want to use websockets in AS3, try something like https://github.com/theturtle32/AS3WebSocket