Is this possible to speed up datachannel processing using webworkers - webrtc

I have this code from a well known chat bot AI.
//main thread:
const worker = new Worker('worker.js');
worker.onmessage = event => {
console.log(`Received message from worker: ${event.data}`);
};
const peerConnection = new RTCPeerConnection();
const dataChannel = peerConnection.createDataChannel('myDataChannel');
dataChannel.onmessage = event => {
console.log(`Received message from data channel: ${event.data}`);
};
worker.postMessage({ type: 'dataChannel', dataChannel });
//web worker
self.onmessage = event => {
if (event.data.type === 'dataChannel') {
const dataChannel = event.data.dataChannel;
dataChannel.onopen = () => {
dataChannel.send('Hello, main thread!');
};
}
};
Appart from signaling and other things related to the connection I was wondering if this code might work, in a sense that if I send or receive messages it won't be blocked by the main thread? I can't really see a first glance what would be the real improvement here as the endpoint is the main thread and will be treated synchronously but maybe there are some?!

Related

How to use setState with ssdp m-search discovery?

I am using SSDP search message to discover devices with connected same network but when i tried to call setState hooks inside client.on function I only get one device informations.
I initialized my state value in this way
const [deviceList, setDeviceList] = useState([]);
And create a function for the client to add deviceList as it is found
const getAllDevices = () => {
var Client = require('react-native-ssdp-remote').Client,
client = new Client();
client.search('urn:dial-multiscreen-org:service:dial:1');
client.on('response', function (headers) {
const url = new URL(headers.LOCATION);
if (url != null) {
if (!deviceList.includes(url)) {
setDeviceList([...deviceList, url]);
}
}
});
};
and called this function inside useEffect
useEffect(() => {
getAllDevices();
}, []);
There are 4 devices connected to same network and it goes into setDeviceList process 4 times but i can only get one device. Could you please support.
Thanks.
I think this is more of a race condition instead of a library issue.
Give it a try with using functional update on setDeviceList.
setDeviceList(deviceList => {
return [...deviceList, url]
}

How to export data received by event in service worker to vue components?

I am building a notification system with Pusher. Currently I have a service worker registered with Pusher and I can receive "notifications" sent from my backend, but I can only show them in console:
importScripts("https://js.pusher.com/beams/service-worker.js");
PusherPushNotifications.onNotificationReceived = ({ pushEvent, payload }) => {
pushEvent.waitUntil(
self.registration.showNotification(payload.notification.title, {
body: payload.notification.body,
icon: payload.notification.icon,
data: payload.data
})
);
let notification = `Data recieved from notification ${payload.data.message}`;
console.log(notification);
};
I want to export the variable "notifications" to my vue components to manipulate the information coming from the backend.
I have tried to export, but it didn't work.
The service worker is placed in the "public" folder.
How can I do it?
Service workers communicate with a page only through messages.
function postMsg(message) {
return self.clients.matchAll().then(function(clients) {
clients.forEach(function(client) {
client.postMessage(message)
});
});
}
And then you can listen to the message inside your page :
navigator.serviceWorker.onmessage = function (evt) {
const message = evt.data
if (message.type === 'notification') {
doSomething(message)
}
}
I used Broadcast Channel to be able to send the notifications to my vue components.
Create a new instance of BroadcastChannel. Name it (in this case, the name of the Broadcast Channel is 'sw-messages') and use the method "postMessage" to send the message:
importScripts("https://js.pusher.com/beams/service-worker.js");
const channel = new BroadcastChannel('sw-messages');
PusherPushNotifications.onNotificationReceived = ({ pushEvent, payload }) => {
pushEvent.waitUntil(
self.registration.showNotification(payload.notification.title, {
body: payload.notification.body,
icon: payload.notification.icon,
data: payload.data
})
);
channel.postMessage({ title: payload.data});
};
In the vue component, I create (again) a new BroadcastChannel instance and then put an event handler, like this:
const channel = new BroadcastChannel("sw-messages");
channel.onmessage = function (event) {
this.pushNotification = event.data;
console.log(this.pushNotification.title);
}

Using redux-observable and subscribing to a websocket

Trying to figure out how to get my epic going which will subscribe to a websocket and then dispatch some actions as the emitted events roll in from the websocket.
The sample I see are using a multiplex and not actually calling a subscribe on websocket, and I'm confused a bit on changing it up.
I have started it like this. But I believe the redux observable is wanting an
const socket$ = Observable.webSocket<DataEvent>(
"ws://thewebsocketurl"
);
const bankStreamEpic = (action$, store) =>
action$.ofType(START_BANK_STREAM).mergeMap(action => {
console.log("in epic mergeMap");
socket$
.subscribe(
e => {
console.log("dispatch event " + e);
distributeEvent(e);
},
e => {
logger.log("AmbassadorsDataService", "Unclean socket closure");
},
() => {
logger.log("AmbassadorsDataService", "Socket connection closed");
}
)
});
function distributeEvent(event: DataEvent) : void {
//this.logger.log('AmbassadorsDataService', 'Event Received: ' + event.command + ' and id: ' + event.id);
if(event.source === '/ambassadors/bank') {
if( event.command === 'REMOVE') {
removeDataEvent(event);
}
else if(event.command == 'ADD') {
loadDataEvent(event);
}
}
}
It is throwing an error:
Uncaught TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
Any help would be appreciated!
Thanks
In redux-observable, you will almost never (unless you know why I say "almost") call subscribe yourself. Instead, Observables are chained and the middleware and other operators will handle subscriptions for you.
If all you want to do is dispatch an action for every event received, that's simple:
const socket$ = Observable.webSocket<DataEvent>(
"ws://thewebsocketurl"
);
const bankStreamEpic = (action$, store) =>
action$.ofType('START_BANK_STREAM')
.mergeMap(action =>
socket$
.map(payload => ({
type: 'BANK_STREAM_MESSAGE',
payload
}))
);
You may (or may not) need to do more customization depending on what the content of the message received from the socket is, but actually you might be better served placing that other logic in your reducers since it probably isn't side effect related.
You probably will want a way to stop the stream, which is just a takeUntil:
const socket$ = Observable.webSocket<DataEvent>(
"ws://thewebsocketurl"
);
const bankStreamEpic = (action$, store) =>
action$.ofType('START_BANK_STREAM')
.mergeMap(action =>
socket$
.map(payload => ({
type: 'BANK_STREAM_MESSAGE',
payload
}))
.takeUntil(
action$.ofType('STOP_BANK_STREAM')
)
);
I used mergeMap because you did, but in this case I think switchMap is more apt, since each having multiple of these seems redundant, unless you need to have multiple and your question just omits something unique about each.

Redux Observables: Custom observer with multiple events?

Hi I'm trying to use redux-observables with react native and a websocket wrapper called Phoenix, which basically allows you to execute callbacks when certain messages are received through the websocket.
Here is the basic setup of what I'm trying to do:
import 'rxjs';
import { Observable } from 'rxjs';
import { Socket, Channel } from 'phoenix';
import * as channelActions from '../ducks/channel';
export default function connectSocket(action$, store) {
return action$.ofType(channelActions.SETUP)
.mergeMap(action => {
return new Observable(observer => {
const socket = new Socket('http://localhost:4000/socket');
const userId = store.getState().user.id;
const channel = socket.channel(`user:${userId}`);
channel
.join()
.receive('ok', response => observer.next({ type: 'join', payload: 'something' }))
.receive('error', reason => observer.next({ type: 'error', payload: 'reason' }))
channel.on('rooms:add', room => observer.next({ type: 'rooms:add', payload: '123' }))
channel.on('something:else', room => observer.next({ type: 'something:else', payload: '123' }))
});
})
.map(action => {
switch (action.type) {
case 'join':
return channelActions.join(action.payload);
case 'error':
return channelActions.error(action.payload);
case 'rooms:add':
return channelActions.add(action.payload);
case 'something:else':
return channelActions.something(action.payload);
}
});
};
As you can see, there are several concerns / issues with this approach:
Different events are being fired from one observer. I don't know how to split them up besides that switch statement in the .map() function
I don't know where to call observer.complete() since I want it to continue to listen for all those events, not just once.
If I could extract the channel constant into a separate file available to several epics that would fix these concerns. However, channel is dependent on socket, which is dependent on user, which comes from the redux state.
So I'm quite confused on how to approach this problem. I think the ability to extract a global channel object would fix it, but that also depends on the user ID from the redux state. Any help is appreciated.
P.S. If it's worth anything, my use case is very similar to this guy (https://github.com/MichalZalecki/connect-rxjs-to-react/issues/1). One of the responders recommended using Rx.Subject but I don't know where to start with that...
It seems like you are making your life extra difficult. You have already mapped the events so why are you combining and remapping them? Map each event into its own stream and merge the results together.
// A little helper function to map your nonstandard receive
// function into its own stream
const receivePattern = (channel, signal, selector) =>
Observable.fromEventPattern(
h => channel.receive(signal, h),
h => {/*However you unsubscribe*/},
selector
)
export default function connectSocket(action$, store) {
return action$.ofType(channelActions.SETUP)
// Observable.create is usually a crutch pattern
// Use defer or using here instead as it is more semantic and handles
// most of the stream lifecycle for you
.mergeMap(action => Observable.defer(() => {
const socket = new Socket('http://localhost:4000/socket');
const userId = store.getState().user.id;
const channel = socket.channel(`user:${userId}`);
const joinedChannel = channel.join();
// Convert the ok message into a channel join
const ok = receivePattern(joinedChannel, 'ok')
// Just an example of doing some arbitrary mapping since you seem to be doing it
// in your example
.mapTo('something')
.map(channelActions.join);
// Convert the error message
const error = receivePattern(joinedChannel, 'error')
.map(channelActions.error);
// Since the listener methods follow a standard event emitter interface
// You can use fromEvent to capture them
// Rather than the more verbose fromEventPattern we used above
const addRooms = Observable.fromEvent(channel, 'rooms:add')
.map(channelActions.add);
const somethingElse = Observable.fromEvent(channel, 'something:else')
.map(channelActions.somethingElse);
// Merge the resulting stream into one
return Observable.merge(ok, error, addRooms, somethingElse);
});
);
};

Node.js + socket.io + node-amqp and queue binginds when "re" connecting thru socket.io

I have one scenario which is very close to this sample:
One main screen:
this screen (client side) will connect to the socket.io server thru server:9090/scope (io.connect("http://server:9090/scope)) and will send one event "userBindOk" (socket.emit("userBindOk", message)) to the socket.io server;
the server receives the connection and the "userBindOk". At this moment, the server should get the active connection to rabbitmq server and bind the queue to the respective user that just connected to the application thru socket.io. sample:
socket.on("connection", function(client){
//client id is 1234
// bind rabbitmq exchange, queue, and:
queue.subscribe(//receive callback);
})
So far, no problem - I can send/receive messages thru socket.io without problems.
BUT, If I refresh the page, all those steps will be done again. As consequence, the binding to the queue will occur, but this time related to another session of the socket.io client. This means that if I send a message to the queue which is related to the first socket.io session (before the page refresh), that bind should (I think) receive the message and send it to a invalid socket.io client (page refresh = new client.id on the socket.io context). I can prove this behaviour because every time I refresh the page I need to send x times more messages. For instance: I`ve connected for the first time: - so, 1 message - one screen update; refresh the page: I need to send 2 messages to the queue and only the second message will be received from the "actual" socket.io client session - this behaviour will occur as many as I refresh the page (20 page refreshs, 20 messages to be sent to a queue and the server socket.io "last" client will send the message to the client socket.io to render into the screen).
The solutions I believe are:
Find a way to "unbind" the queue when disconnecting from the socket.io server - I didn`t see this option at the node-amqp api yet (waiting for it :D)
find a way to reconnect the socket.io client using the same client.id. This way I can identify the client that is coming and apply some logic to cache the socket.
Any ideas? I tried to be very clear... But, as you know, it`s not so eaey to expose your problem when trying to clarify something that is very specific to some context...
tks
I solved it like this:
I used to declare the rabbitMq queue as durable=true,autoDelete=false,exclusive=false and in my app there was 1 queue/user and 1 exchange(type=direct) with the routing_key name=queueName, my app also used the queue for other client diffent to browsers like android app or iphone app as push fallback, so i use to crear 1 queue for earch user.
The solution to this problem was to change my rabbitMQ queue and exchange declaration. Now i declare the exchange/user as fanout and autoDelete=True, and the user is going to have N queues with durable=true, autoDelete=true, exclusive=true (No. queue = No. clients) and all the queues are bind to the user-exchange(multicast).
NOTE: my app is wirten in django, and i use node+socket+amqp to be able to comunicate with the browser using web.scokets, so i use node-restler to query my app api to get the user-queue info.
thats the rabbitMQ side, for the node+amqp+socket i did this:
server-side:
onConnect: the declaration of the user exchange as fanout, autoDelete, durable. then declaration of the queue as durable, autodelete and exclusive, then the queue.bind to the user-exchange and finaly the queue.subscribe and the socket.disconnect will destroy the queue so there are going to exist queue as client connected the app and this solve the problem of the refresh and allow the user to have more than 1 window-tab with the app:
Server-side:
/*
* unCaught exception handler
*/
process.on('uncaughtException', function (err) {
sys.p('Caught exception: ' + err);
global.connection.end();
});
/*
* Requiere libraries
*/
global.sys = require('sys');
global.amqp = require('amqp');
var rest = require('restler');
var io = require('socket.io').listen(8080);
/*
* Module global variables
*/
global.amqpReady = 0;
/*
* RabbitMQ connection
*/
global.connection = global.amqp.createConnection({
host: host,
login: adminuser,
password: adminpassword,
vhost: vhost
});
global.connection.addListener('ready',
function () {
sys.p("RabbitMQ connection stablished");
global.amqpReady = 1;
}
);
/*
* Web-Socket declaration
*/
io.sockets.on('connection', function (socket) {
socket.on('message', function (data) {
sys.p(data);
try{
var message = JSON.parse(data);
}catch(error){
socket.emit("message", JSON.stringify({"error": "invalid_params", "code": 400}));
var message = {};
}
var message = JSON.parse(data);
if(message.token != undefined) {
rest.get("http://dev.kinkajougames.com/api/push",
{headers:
{
"x-geochat-auth-token": message.token
}
}).on('complete',
function(data) {
a = data;
}).on('success',
function (data){
sys.p(data);
try{
sys.p("---- creating exchange");
socket.exchange = global.connection.exchange(data.data.bind, {type: 'fanout', durable: true, autoDelete: true});
sys.p("---- declarando queue");
socket.q = global.connection.queue(data.data.queue, {durable: true, autoDelete: true, exclusive: false},
function (){
sys.p("---- bind queue to exchange");
//socket.q.bind(socket.exchange, "*");
socket.q.bind(socket.exchange, "*");
sys.p("---- subscribing queue exchange");
socket.q.subscribe(function (message) {
socket.emit("message", message.data.toString());
});
}
);
}catch(err){
sys.p("Imposible to connection to rabbitMQ-server");
}
}).on('error', function (data){
a = {
data: data,
};
}).on('400', function() {
socket.emit("message", JSON.stringify({"error": "connection_error", "code": 400}));
}).on('401', function() {
socket.emit("message", JSON.stringify({"error": "invalid_token", "code": 401}));
});
}
else {
socket.emit("message", JSON.stringify({"error": "invalid_token", "code": 401}));
}
});
socket.on('disconnect', function () {
socket.q.destroy();
sys.p("closing socket");
});
});
client-side:
The socket intance with options 'force new connection'=true and 'sync disconnect on unload'= false.
The client side use the onbeforeunload and onunload windows object events to send socket.disconnect
The client on socket.connect event send the user token to node.
proces message from socket
var socket;
function webSocket(){
//var socket = new io.Socket();
socket = io.connect("ws.dev.kinkajougames.com", {'force new connection':true, 'sync disconnect on unload': false});
//socket.connect();
onSocketConnect = function(){
alert('Connected');
socket.send(JSON.stringify({
token: Get_Cookie('liveScoopToken')
}));
};
socket.on('connect', onSocketConnect);
socket.on('message', function(data){
message = JSON.parse(data);
if (message.action == "chat") {
if (idList[message.data.sender] != undefined) {
chatboxManager.dispatch(message.data.sender, {
first_name: message.data.sender
}, message.data.message);
}
else {
var username = message.data.sender;
Data.Collections.Chats.add({
id: username,
title: username,
user: username,
desc: "Chat",
first_name: username,
last_name: ""
});
idList[message.data.sender] = message.data.sender;
chatboxManager.addBox(message.data.sender, {
title: username,
user: username,
desc: "Chat",
first_name: username,
last_name: "",
boxClosed: function(id){
alert("closing");
}
});
chatboxManager.dispatch(message.data.sender, {
first_name: message.data.sender
}, message.data.message);
}
}
});
}
webSocket();
window.onbeforeunload = function() {
return "You have made unsaved changes. Would you still like to leave this page?";
}
window.onunload = function (){
socket.disconnect();
}
And that's it, so no more round-robing of the message.