So this is weird, when i try to connect to websocket (this is only a Microsoft edge issue) it makes so on every second page refresh webworker will not accept messages onMessage wont trigger at all:
consider the following:
main.js
var worker = new Worker("webworker.js");
worker.postMessage({ type: 'INIT_SOCKET' });
worker.addEventListener('message', (event) => {
let data = event.data;
if (typeof data === 'string') {
data = JSON.parse(data);
}
if (data.type === 'SOCKET_INITIALIZED') {
console.log('inititalized');
}
});
webworker.js
var io = require('socket.io-client');
var socket;
onmessage = function(event) {
var data = event.data;
console.log('got a event');
if (typeof data === 'string') {
data = JSON.parse(data);
}
switch (data.type) {
case 'INIT_SOCKET':
try {
socket = io('xxxx', { transports: [ 'websocket' ], secure: true }); // this line causes the error
socket.on('connect', function () {
postMessage({
type: Consts.SOCKET_INITIALIZED
});
});
} catch(e) {
console.log('some error ', e);
}
break;
};
};
require does not appear to be defined at Worker context. Use importScripts() to import external scripts into DedicatedWorkerGlobalScope. For example
importScripts("socket.io.js");
Could not determine how to stop io() call from polling and getting error, probably due to 404 error
socket.io.js:7370 WebSocket connection to
'ws://echo.websocket.org/socket.io/?EIO=3&transport=websocket' failed:
Error during WebSocket handshake: Unexpected response code: 404
probably due to being unfamiliar, here, as to how io() is implemented. Though was able to define Socket object within Worker scope.
Approach using WebSocket returns expected result
const worker = new Worker("webworker.js");
worker.addEventListener('message', (event) => {
let data = event.data;
if (typeof data === 'string') {
console.log(data)
}
if (data.type === 'SOCKET_INITIALIZED') {
console.log('inititalized');
}
});
worker.postMessage({
type: 'INIT_SOCKET'
});
importScripts("socket.io.js");
let sock = io();
console.log(sock); // to demonstrate `Socket` is defined
sock.close(); // closing socket here to prevent `404` polling errors
self.socket = void 0;
self.onmessage = function(event) {
var data = event.data;
console.log('got a event');
if (typeof data === 'string') {
data = JSON.parse(data);
}
switch (data.type) {
case 'INIT_SOCKET':
if (!self.socket) {
try {
self.socket = new WebSocket("ws://echo.websocket.org/");
self.socket.onopen = function(e) {
socket.send("WebSocket rocks");
console.log("self.socket event.type:", e.type);
self.postMessage({
type: 'SOCKET_INITIALIZED'
});
};
self.socket.onmessage = function(e) {
console.log(e.data);
self.socket.close()
};
self.socket.onerror = function(e) {
console.log("self.socket error", e);
};
self.socket.onclose = function(e) {
console.log("self.socket event.type", e.type);
};
} catch (e) {
console.log('some error ', e);
}
break;
};
}
};
plnkr http://plnkr.co/edit/zVnLE6qG7Kf4yVSb0aJt?p=preview
Related
I'm working on app which send message via websockets (managed by django channels) and in return it receives json from django db as a message and renders frontend based on that json.
I have Invalid State Error when I try to send message by websocket, why? Messages send are usually Json. I works properly all the time but commented part doesn't and I don't know why please explain me.
function main() {
configGame();
}
function configGame() {
const socket = "ws://" + window.location.host + window.location.pathname;
const websocket = new WebSocket(socket);
const playerName = document.querySelector(".playerName_header").textContent;
function asignEvents() {
const ready_btn = document.querySelector(".--ready_btn");
const start_btn = document.querySelector(".--start_btn");
ready_btn.addEventListener("click", () => {
let mess = JSON.stringify({
player: playerName,
action: "ready",
});
sendMess(mess);
});
start_btn.addEventListener("click", () => {
let mess = JSON.stringify({
player: playerName,
action: "start",
});
sendMess(mess);
});
}
function openWebsocket() {
console.log("Establishing Websocket Connection...");
websocket.onopen = () => {
console.log("Websocket Connection Established!");
};
}
function setWebsocket() {
websocket.onmessage = (mess) => {
console.log(`Message: ${mess.data}`);
dataJson = JSON.parse(mess.data);
dataJson = JSON.parse(dataJson.message);
//Player Ready (jeszcze z max_players zrobic kontrolke)
if (dataJson.action === "player_ready") {
const playersReadyText = document.querySelector(".players_ready_text");
playersReadyText.textContent = `Players ready: ${dataJson.players_ready}`;
}
};
websocket.onclose = () => {
console.log("Websocket Connection Terminated!");
};
}
/*
function checkState() {
let mess = JSON.stringify({
player: playerName,
action: "game state",
});
sendMess(mess);
}
*/
function sendMess(messText) {
websocket.send(messText);
}
openWebsocket();
checkState(); //This one doesn't work
asignEvents();
setWebsocket();
}
// Asigning Event Listneres to DOM ELEMENTS
function asignEvents() {
const ready_btn = document.querySelector(".--ready_btn");
const start_btn = document.querySelector(".--start_btn");
ready_btn.addEventListener("click", () => {
console.log("Ready");
});
start_btn.addEventListener("click", () => {
console.log("Start");
});
}
main();
Error:
Console (Safari) returns InvalidState error and points to
method checkState and sendMess.
InvalidStateError: The object is in an invalid state.
Is the websocket connected?
sendMess(messText) {
if (websocket.readyState === WebSocket.OPEN) {
websocket.send(messText);
} else {
console.warn("websocket is not connected");
}
}
I'm trying to debug the existent system, where calls are made via Asteriks.
When accepting incoming calls, everything works fine, but on making outgoing call there is apparently no sound (but I accept 'addstream' event and attach stream to audio). Production code takes 500 lines, but this code does pretty much the same, but doesn't work as well
const socket = new JsSIP.WebSocketInterface('wss://callwss.agdevelopments.net');
socket.via_transport = 'wss';
const configuration = {
password: "SIP4003!",
realm: "callws,s.agdevelopments.net",
register: true,
session_timers: false,
uri: "sip:4003#callwss.agdevelopments.net",
sockets: [socket]
}
const ua = new JsSIP.UA(configuration)
// Setup events
ua.on('connected', function () {
console.log('Connected')
})
ua.on('disconnected', function () {
console.log('Connected')
})
// Make a call
const eventHandlers = {
'progress': function (e) {
console.log('call is in progress');
},
'failed': function (e) {
console.log('call failed with cause: ' + (e.data ? e.data.cause : 'no cause'), e);
},
'ended': function (e) {
console.log('call ended with cause: ' + (e.data ? e.data.cause : 'no cause'), e);
},
'confirmed': function (e) {
console.log('call confirmed');
},
'addstream': (e) => {
console.log('Add stream (event handlers)')
audio.srcObject = e.stream
audio.play()
}
};
const options = {
'eventHandlers': eventHandlers,
'mediaConstraints': {'audio': true, 'video': false}
};
const audio = new window.Audio()
ua.on('registered', function () {
const session = ua.call('0513887341', options)
if (session.connection) {
console.log('Connection is valid')
session.connection.addEventListener('addstream', e => {
console.log('Add stream')
audio.srcObject = e.stream
audio.play()
})
session.on('addstream', function(e){
// set remote audio stream (to listen to remote audio)
// remoteAudio is <audio> element on page
const remoteAudio = audio
remoteAudio.src = window.URL.createObjectURL(e.stream);
remoteAudio.play();
});
session.connection.addEventListener('peerconnection', e => {
console.log('Peer connection')
audio.srcObject = e.stream
audio.play()
})
} else {
console.log('Connection is null')
}
})
ua.on('newRTCSession', (data) => {
console.log('New RTC Session')
const session = data.session
session.on('addstream', function(e){
// set remote audio stream (to listen to remote audio)
// remoteAudio is <audio> element on page
const remoteAudio = audio
remoteAudio.src = window.URL.createObjectURL(e.stream);
remoteAudio.play();
});
})
ua.start()
Also attaching screenshots from Asterisk. The first one is outgoing call with no sound, the second is incoming with sound
The issue was solved from IT side. There was no problems in JsSIP or code
I try to use Deno ws to reload the document, but it will throw an error after second reload
Uncaught ConnectionReset: Socket has already been closed throw new Deno.errors.ConnectionReset("Socket has already been closed");
var ws = new WebSocket("ws://127.0.0.1:8080/ws")
ws.onopen = function () {
ws.send('ws open')
console.log('ws open');
}
ws.addEventListener("message", (e) => {
if (e.data === 'fileUpdate') {
// ws.send('close')
location.replace(location.href);
}
})
seem location.replace(location.href) raise an error
any solution?
The error is happening because you're sending a message after the socket is closed.
When you do: location.replace(location.href); the page is refreshed and the current socket is closed.
You can either catch the error, or check for ws.isClosed before sending the message.
for await (const e of ws) {
if (e === 'close') {
ob.remove("fileUpdate")
continue
}
ob.on("fileUpdate", () => {
console.log('sending')
if(!ws.isClosed)
ws.send("fileUpdate")
})
}
While that will fix the error, it won't fix the cause. Your ob.on('fileUpdate') event is firing after the socket is closed. You should clear that listener on the WebSocket close event, you can do that using ws.isWebSocketCloseEvent
import { acceptWebSocket, isWebSocketCloseEvent } from "https://deno.land/std#0.51.0/ws/mod.ts";
/* ... */
for await (const e of ws) {
if(isWebSocketCloseEvent(e) || e === 'close') {
// clear listeners here
ob.remove("fileUpdate")
// if e === 'close' you may want to close the socket
}
}
here is Deno code :
import { Application } from "https://deno.land/x/abc/mod.ts";
import { acceptWebSocket } from "https://deno.land/std#0.51.0/ws/mod.ts";
new Application()
.file("/", "./index.html")
.file("/module.js", "./module.js")
// .file("sw.js", "ServiceWorker.js")
.get('/ws', async (c: any) => {
const { conn, headers, r: bufReader, w: bufWriter } = c.request;
const ws = await acceptWebSocket({
conn,
headers,
bufReader,
bufWriter,
});
for await (const e of ws) {
if (e === 'close') {
ob.remove("fileUpdate")
continue
}
ob.on("fileUpdate", () => {
ws.send("fileUpdate")
})
}
})
.start({ port: 8080 })
ob like this :
class Ob {
private list: ObList[] = []
send(event: string) {
this.list.forEach((e: ObList) => {
if (e.event === event) {
e.cb && e.cb()
}
})
}
on(event: string, cb: Function) {
this.list.push({ event, cb })
}
remove(event:string){
this.list=this.list.filter((e:ObList)=>{
return e.event!==event
})
}
}
the framework is abc
I am making a Screen sharing app with WebRTC + Socket.io and stuck at a place.
Connected with two browser using WebRTC + Socket.io and can send text
I am taking support from codelab but it is not for stream.(If solution is based on this link then highly helpful)
How can I send getUserMedia() stream:
dataChannel.send(stream);
And receive same stream on channel.onmessage():
I am getting event.data as '[object MediaStream]' not stream.
channel.onmessage = function(event){
// unable to get correct stream
// event.data is "[object MediaStream]" in string
}
function createPeerConnection(isInitiator, config) {
console.log('Creating Peer connection as initiator?', isInitiator, 'config:', config);
peerConn = new RTCPeerConnection(config);
// send any ice candidates to the other peer
peerConn.onicecandidate = function (event) {
console.log('onIceCandidate event:', event);
if (event.candidate) {
sendMessage({
type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate
});
} else {
console.log('End of candidates.');
}
};
if (isInitiator) {
console.log('Creating Data Channel');
dataChannel = peerConn.createDataChannel("screen");
onDataChannelCreated(dataChannel);
console.log('Creating an offer');
peerConn.createOffer(onLocalSessionCreated, logError);
} else {
peerConn.ondatachannel = function (event) {
console.log('ondatachannel:', event.channel);
dataChannel = event.channel;
onDataChannelCreated(dataChannel);
};
}
}
It is working fine for string or json i.e. dataChannel.send('Hello');
I have created a wiki page for same: wiki
Please help.
Please try something like this: (explanation at the end of the code)
var btnShareYourCamera = document.querySelector('#share-your-camera');
var localVideo = document.querySelector('#local-video');
var remoteVideo = document.querySelector('#remote-video');
var websocket = new WebSocket('wss://path-to-server:port/');
websocket.onmessage = function(event) {
var data = JSON.parse(event.data);
if (data.sdp) {
if (data.sdp.type === 'offer') {
getUserMedia(function(video_stream) {
localVideo.srcObject = video_stream;
answererPeer(new RTCSessionDescription(data.sdp), video_stream);
});
}
if (data.sdp.type === 'answer') {
offerer.setRemoteDescription(new RTCSessionDescription(data.sdp));
}
}
if (data.candidate) {
addIceCandidate((offerer || answerer), new RTCIceCandidate(data.candidate));
}
};
var iceTransportPolicy = 'all';
var iceTransportLimitation = 'udp';
function addIceCandidate(peer, candidate) {
if (iceTransportLimitation === 'tcp') {
if (candidate.candidate.toLowerCase().indexOf('tcp') === -1) {
return; // ignore UDP
}
}
peer.addIceCandidate(candidate);
}
var offerer, answerer;
var iceServers = {
iceServers: [{
'urls': [
'stun:stun.l.google.com:19302',
'stun:stun1.l.google.com:19302',
'stun:stun2.l.google.com:19302',
'stun:stun.l.google.com:19302?transport=udp',
]
}],
iceTransportPolicy: iceTransportPolicy,
rtcpMuxPolicy: 'require',
bundlePolicy: 'max-bundle'
};
// https://https;//cdn.webrtc-experiment.com/IceServersHandler.js
if (typeof IceServersHandler !== 'undefined') {
iceServers.iceServers = IceServersHandler.getIceServers();
}
var mediaConstraints = {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
};
/* offerer */
function offererPeer(video_stream) {
offerer = new RTCPeerConnection(iceServers);
offerer.idx = 1;
video_stream.getTracks().forEach(function(track) {
offerer.addTrack(track, video_stream);
});
offerer.ontrack = function(event) {
remoteVideo.srcObject = event.streams[0];
};
offerer.onicecandidate = function(event) {
if (!event || !event.candidate) return;
websocket.send(JSON.stringify({
candidate: event.candidate
}));
};
offerer.createOffer(mediaConstraints).then(function(offer) {
offerer.setLocalDescription(offer).then(function() {
websocket.send(JSON.stringify({
sdp: offer
}));
});
});
}
/* answerer */
function answererPeer(offer, video_stream) {
answerer = new RTCPeerConnection(iceServers);
answerer.idx = 2;
video_stream.getTracks().forEach(function(track) {
answerer.addTrack(track, video_stream);
});
answerer.ontrack = function(event) {
remoteVideo.srcObject = event.streams[0];
};
answerer.onicecandidate = function(event) {
if (!event || !event.candidate) return;
websocket.send(JSON.stringify({
candidate: event.candidate
}));
};
answerer.setRemoteDescription(offer).then(function() {
answerer.createAnswer(mediaConstraints).then(function(answer) {
answerer.setLocalDescription(answer).then(function() {
websocket.send(JSON.stringify({
sdp: answer
}));
});
});
});
}
var video_constraints = {
mandatory: {},
optional: []
};
function getUserMedia(successCallback) {
function errorCallback(e) {
alert(JSON.stringify(e, null, '\t'));
}
var mediaConstraints = {
video: true,
audio: true
};
navigator.mediaDevices.getUserMedia(mediaConstraints).then(successCallback).catch(errorCallback);
}
btnShareYourCamera.onclick = function() {
getUserMedia(function(video_stream) {
localVideo.srcObject = video_stream;
offererPeer(video_stream);
});
};
You must attach stream using peer.addTrack as you can see in the above example
You must receive remote stream using peer.ontrack as you can see in the above example
i.e. use addTrack to attach your camera and use ontrack to receive remote camera.
You must never send your stream using dataChannel.send. Both are totally different protocols. A MediaStream must be shared using RTP; not SCTP. RTP is used only if you call peer.addTrack method to attach your camera stream.
This process happens before you open or join a room.
See single-page demo here: https://www.webrtc-experiment.com/getStats/
HTML for above code snippet:
<button id="share-your-camera"></button>
<video id="local-video" controls autoplay playsinline></video>
<video id="remote-video" controls autoplay playsinline></video>
When my page loads, I try to send a message to the server to initiate a connection, but it's not working. This script block is near the top of my file:
var connection = new WrapperWS();
connection.ident();
// var autoIdent = window.addEventListener('load', connection.ident(), false);
Most of the time, I see the error in the title:
Uncaught InvalidStateError: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state
So I tried to catch the exception, as you can see below, but now it seems InvalidStateError is not defined and that produces a ReferenceError.
Here's the wrapper object for my websocket connection:
// Define WrapperWS
function WrapperWS() {
if ("WebSocket" in window) {
var ws = new WebSocket("ws://server:8000/");
var self = this;
ws.onopen = function () {
console.log("Opening a connection...");
window.identified = false;
};
ws.onclose = function (evt) {
console.log("I'm sorry. Bye!");
};
ws.onmessage = function (evt) {
// handle messages here
};
ws.onerror = function (evt) {
console.log("ERR: " + evt.data);
};
this.write = function () {
if (!window.identified) {
connection.ident();
console.debug("Wasn't identified earlier. It is now.");
}
ws.send(theText.value);
};
this.ident = function () {
var session = "Test";
try {
ws.send(session);
} catch (error) {
if (error instanceof InvalidStateError) {
// possibly still 'CONNECTING'
if (ws.readyState !== 1) {
var waitSend = setInterval(ws.send(session), 1000);
}
}
}
window.identified = true;
theText.value = "Hello!";
say.click();
theText.disabled = false;
};
};
}
I am testing using Chromium on Ubuntu.
You could send messages via a proxy function that waits for the readyState to be 1.
this.send = function (message, callback) {
this.waitForConnection(function () {
ws.send(message);
if (typeof callback !== 'undefined') {
callback();
}
}, 1000);
};
this.waitForConnection = function (callback, interval) {
if (ws.readyState === 1) {
callback();
} else {
var that = this;
// optional: implement backoff for interval here
setTimeout(function () {
that.waitForConnection(callback, interval);
}, interval);
}
};
Then use this.send in place of ws.send, and put the code that should be run afterwards in a callback:
this.ident = function () {
var session = "Test";
this.send(session, function () {
window.identified = true;
theText.value = "Hello!";
say.click();
theText.disabled = false;
});
};
For something more streamlined you could look into promises.
This error is raised because you are sending your message before the WebSocket connection is established.
You can solve it by doing this simply:
conn.onopen = () => conn.send("Message");
This onopen function waits for your WebSocket connection to establish before sending your message.
if you use one websocket client object and connect from random app places then object can be in connecting mode (concurent access).
if you want to exchange through only one websoket then
create class with promise and keep it in property
class Ws {
get newClientPromise() {
return new Promise((resolve, reject) => {
let wsClient = new WebSocket("ws://demos.kaazing.com/echo");
console.log(wsClient)
wsClient.onopen = () => {
console.log("connected");
resolve(wsClient);
};
wsClient.onerror = error => reject(error);
})
}
get clientPromise() {
if (!this.promise) {
this.promise = this.newClientPromise
}
return this.promise;
}
}
create singleton
window.wsSingleton = new Ws()
use clientPromise property in any place of app
window.wsSingleton.clientPromise
.then( wsClient =>{wsClient.send('data'); console.log('sended')})
.catch( error => alert(error) )
http://jsfiddle.net/adqu7q58/11/
Method 1: Check connection
You can resolve a promise when socket is connected:
async function send(data) {
await checkConnection();
ws.send(data);
}
Implementation
This trick is implemented using an array of resolvers.
let ws = new WebSocket(url);
let connection_resolvers = [];
let checkConnection = () => {
return new Promise((resolve, reject) => {
if (ws.readyState === WebSocket.OPEN) {
resolve();
}
else {
connection_resolvers.push({resolve, reject});
}
});
}
ws.addEventListener('open', () => {
connection_resolvers.forEach(r => r.resolve())
});
Method 2: Wait for connection
You can resolve a promise when socket is not connected:
const MAX_RETRIES = 4;
async function send(data, retries = 0) {
try {
ws.send(data);
}
catch (error) {
if (retries < MAX_RETRIES error.name === "InvalidStateError") {
await waitForConnection();
send(data, retries + 1);
}
else {
throw error;
}
}
}
Implementation
This trick is implemented using an array of resolvers.
let ws = new WebSocket(url);
let connection_resolvers = [];
let waitForConnection = () => {
return new Promise((resolve, reject) => {
connection_resolvers.push({resolve, reject});
});
}
ws.addEventListener('open', () => {
connection_resolvers.forEach(r => r.resolve())
});
My opinion is that the second method has a little bit good performance!
It is possible to use functions and readyState with setTimeout.
function openSocket()
{
webSocket = new WebSocket("");
}
function sendData()
{
if(webSocket.readyState)
{
webSocket.send(JSON.stringify(
{
"event" : "",
"message" : ""
}));
}
else
{
setTimeout(sendData, 1000);
}
}
function eventHandler()
{
webSocket.onmessage = function(e)
{
data = JSON.parse(e.data);
event = data.event;
switch (event)
{...}
}
}
openSocket();
sendData();
enentHandler();