I am trying to build a chat app. In the first step the user sends username and if the name is not already existing is logged in. Then can send a message to another user, here comes the error, after the first messages the server sends to the other clients (except the one the message came from) this message but it gets duplicated and the more messages I send the number of duplicates seems to multiply.
I tried so much changes, including sending back the message from server to all client including the one it originated from, but cannot find a solution for these duplicates --- please help me!
(built with bootstrap & jQuery)
Tried sending a message from user to server, UI shows the message to user and server sends message to all other connected users except the one it came from, all other users should see the message too then on their UI
Here is the HTML & CSS UI:
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
<link rel="stylesheet" href="styles.css">
<style>
.formular {
width: 90%;
position: fixed;
bottom: 0;
}
li {list-style: none;}
#header {position: fixed;
top: 0}
#msgDisplay {padding-top: 2em;}
</style>
<title>Document</title>
</head>
<body>
<div class="container" id="header">
WEBSOCKETS
</div>
<div class="container mt-3" id="msgDisplay">
<ul class="messages">
</ul>
<form action="?" class="formular">
<input type="text" class="input" id="input">
<button type="button" class="btn btn-primary" >Login</button>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4"
crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.6.1.min.js"
integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"></script>
<script src="app.js">
</script>
</body>
</html>
Here is the user JavaScript:
let scrollBottom = () => {
window.scrollTo(0, 999999);
let heightForm = document.querySelector('.formular').offsetHeight;
document.querySelector('.messages').style.paddingBottom = heightForm + 'px';
};
let showMessage = (msg) => {
console.log('MSG DISPLAY: ', msg)
$('.messages').append($('<li>').html(msg))
scrollBottom()
}
let sendmessageF = () => {
ws.send($('#input').val());
$('.input').val(''); // Eingabe leeren
}
window.onresize = scrollBottom;
let username = '';
let ws;
let wsOK = false;
let submitF = (e) => {
e.preventDefault();
if (username == '') {
username = $('.input').val();
if (wsOK == false) {
// Create WebSocket connection.
ws = new WebSocket('ws://localhost:5041');
ws.binaryType = 'blob';
// Connection opened
ws.addEventListener('open', (e) => {
e.preventDefault()
console.log('NEW SOCKED OPENED', e)
console.log('NEW USER: ', username)
ws.send(JSON.stringify({ req: '/login', user: username }))
wsOK = true;
});
}
}
else if (username != ''){
ws.send(JSON.stringify({ req: '/message', message: $('.input').val() }))
showMessage($('.input').val())
}
// Listen for messages
ws.addEventListener('message', (event) => {
console.log('Message from server => ', event.data);
if (event.data instanceof Blob) {
let reader = new FileReader(); // UM BLOB AUSZULESEN
reader.readAsText(event.data)
reader.onload = () => {
console.log("Result BLOB: " + reader.result);
showMessage('BLOB: ', reader.result)
};
}
else if (JSON.parse(event.data) instanceof Object){
let msg = JSON.parse(event.data);
console.log('SERVER MSG: ', msg)
if(msg.itype == 'loginSuccess'){
showMessage(msg.res)
$('.btn').html('Senden')
username = msg.res.split(':')[1].trim()
console.log('USERNAME: ', username)
}
else if (msg.itype == 'loginFail'){
showMessage(msg.res)
username = '';
wsOK = false;
ws.destroy()
}
else if (msg.itype == 'valUserMsg'){
console.log('HIER ???')
showMessage(msg.res)
}
}
});
}
$('.btn').click(submitF)`
Here is the node.js Server:
const express = require('express'); // NPM INSTALLEREN
const app = express();
const PORT = process.env.PORT || 5041;
const server = require('http').createServer(app)
const fs = require('fs');
const bodyParser = require('body-parser'); // NPM INSTALLEREN
const WebSocket = require('ws') // NPM INSTALLEREN
const wss = new WebSocket.Server({ server: server })
// const Random = require('./modules/random.js');
server.listen(PORT, () => {
console.log('SERVER PORT: ', PORT)
})
app.use(express.static('www'));
app.use(bodyParser.text({ type: 'text/xml' }));
app.use(bodyParser.json({ type: '*/*' }));
let wsConns = [];
let allUser = [];
wss.on('connection', (wsConn) => { // wsConn ORIGIN
console.log('NEUER CLIENT CONNECTED')
let username = '';
wsConn.on('message', (message) => {
let msgReceived = JSON.parse(message)
console.log('MESSAGE RECEIVED: ', msgReceived)
if (msgReceived.req == '/login') {
username = msgReceived.user
if (allUser.indexOf(username) == -1) {
allUser.push(username)
wsConns.push(wsConn)
console.log('ALLUSER: ',allUser)
console.log('WSCONNS: ',wsConns.length)
wsConn.send(JSON.stringify({itype:'loginSuccess',res:`Username akzeptiert: ${username}`}))
}
else {
wsConn.send(JSON.stringify({itype:'loginFail',res:'Username schon vergeben!'}))
}
}
else if (msgReceived.req == '/message'){
console.log('HIER???')
wss.clients.forEach((client)=>{ // anstatt wss.clients würde auch wsConns gehen (Arr mit allen Conn)
if(client !== wsConn && client.readyState === WebSocket.OPEN){
client.send(JSON.stringify({itype:'valUserMsg',res: msgReceived.message }))
}
})
console.log('WSCONNS: ',wsConns.length)
}
})
})
use ws.onmessage = ()... instead of ws.addEventListener('message', ...
Related
I am creating a project where I can read messages of gmail. I am having problem to display list of messages. I've just quickstart sample code of getting labels.
https://developers.google.com/gmail/api/quickstart/js#step_1_set_up_the_sample
<!DOCTYPE html>
<html>
<head>
<title>Gmail API Quickstart</title>
<meta charset="utf-8" />
</head>
<body>
<p>Gmail API Quickstart</p>
<button id="authorize_button" onclick="handleAuthClick()">Authorize</button>
<button id="signout_button" onclick="handleSignoutClick()">Sign Out</button>
<pre id="content" style="white-space: pre-wrap;"></pre>
<script type="text/javascript">
const CLIENT_ID = 'XXXXX-XXXXXXXXXXXXX.apps.googleusercontent.com';
const API_KEY = 'XXXXXXXXXXXXXXXX';
const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest';
const SCOPES = 'https://www.googleapis.com/auth/gmail.readonly';
let tokenClient;
let gapiInited = false;
let gisInited = false;
document.getElementById('authorize_button').style.visibility = 'hidden';
document.getElementById('signout_button').style.visibility = 'hidden';
function gapiLoaded() {
gapi.load('client', intializeGapiClient);
}
async function intializeGapiClient() {
await gapi.client.init({
apiKey: API_KEY,
discoveryDocs: [DISCOVERY_DOC],
});
gapiInited = true;
maybeEnableButtons();
}
function gisLoaded() {
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: CLIENT_ID,
scope: SCOPES,
callback: '', // defined later
});
gisInited = true;
maybeEnableButtons();
}
function maybeEnableButtons() {
if (gapiInited && gisInited) {
document.getElementById('authorize_button').style.visibility = 'visible';
}
}
function handleAuthClick() {
tokenClient.callback = async (resp) => {
if (resp.error !== undefined) {
throw (resp);
}
document.getElementById('signout_button').style.visibility = 'visible';
document.getElementById('authorize_button').innerText = 'Refresh';
await listLabels();
};
if (gapi.client.getToken() === null) {
tokenClient.requestAccessToken({prompt: 'consent'});
} else {
tokenClient.requestAccessToken({prompt: ''});
}
}
function handleSignoutClick() {
const token = gapi.client.getToken();
if (token !== null) {
google.accounts.oauth2.revoke(token.access_token);
gapi.client.setToken('');
document.getElementById('content').innerText = '';
document.getElementById('authorize_button').innerText = 'Authorize';
document.getElementById('signout_button').style.visibility = 'hidden';
}
}
async function listLabels() {
let response;
try {
response = await gapi.client.gmail.users.labels.list({
'userId': 'me',
});
} catch (err) {
document.getElementById('content').innerText = err.message;
return;
}
const labels = response.result.labels;
if (!labels || labels.length == 0) {
document.getElementById('content').innerText = 'No labels found.';
return;
}
const output = labels.reduce(
(str, label) => `${str}${label.name}\n`,
'Labels:\n');
document.getElementById('content').innerText = output;
}
</script>
<script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script>
<script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script>
</body>
</html>
This code is working fine without any errors.
I want to modify this code and get list of messages. But I am just unable to find right method to get list of messages.
Can someone please help me?
This is the documentation for labels. Compare that with the sample code you have for labels.
Then try to follow the same principle for messages. This is the documentation for messages. Following bullet 1, the code to get the list of messages might be something like
gapi.client.gmail.users.messages.list({'userId': 'me'});
You can work out how to parse the response from the linked documentation
Update:
The documentation says
each message resource contains only an id and a threadId. Additional message details can be fetched using the messages.get method.
This means you have to do something like
gapi.client.gmail.users.messages.get({
'userId': 'me',
'id' : <message_id>
});
I have done a video call POC with peerjs. It works perfectly with web to web (both video, audio & text) but when I try to connect with mobile. It asks permission for audio and video. When I accepts ,I could see the mic icon and video icon on mobile which means audio and video is enabled in mobile. But I couldn't see the video on mobile and mobile's video on web. NOTE: Text chat between mobile to web is working fine only video not rendering.
What I see on web
What I see on mobile
Code
server.js
const express = require("express");
const app = express();
const dotenv = require('dotenv');
dotenv.config();
var random = Math.floor(1000 + Math.random() * 9000);
const server = require("http").Server(app);
const { v4: uuidv4 } = require("uuid");
const io = require("socket.io")(server, {
cors: {
origin: "*",
},
});
// Peer
const { ExpressPeerServer } = require("peer");
const peerServer = ExpressPeerServer(server, {
debug: true,
port: 443
});
const PORT = 3000;
app.set("view engine", "ejs");
app.use(express.static("public"));
app.use("/peerjs", peerServer);
app.get("/", (req, rsp) => {
rsp.redirect(`/${random}`);
});
app.get("/:room", (req, res) => {
res.render("room", { roomId: req.params.room });
});
io.on("connection", (socket) => {
socket.on("join-room", (roomId, userId) => {
socket.join(roomId);
socket.to(roomId).broadcast.emit("user-connected", userId);
socket.on("message", (message) => {
io.to(roomId).emit("createMessage", message);
});
});
});
server.listen(PORT,()=>{
console.log("Started")
});
script.js
const socket = io('/')
const chatInputBox = document.getElementById("chat_message");
const all_messages = document.getElementById("all_messages");
const main__chat__window = document.getElementById("main__chat__window");
const videoGrid = document.getElementById("video-grid");
const myVideo = document.createElement("video");
myVideo.muted = true;
let peer = new Peer(undefined, {
path: "/peerjs",
host: "XXXXXXXXXXXXXXXXX.XXX.XXXX",
port: 443,
});
let myVideoStream;
var getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
navigator.mediaDevices
.getUserMedia({
video: true,
audio: true,
})
.then((stream) => {
myVideoStream = stream;
addVideoStream(myVideo, stream);
peer.on("call", (call) => {
call.answer(stream);
const video = document.createElement("video");
call.on("stream", (userVideoStream) => {
addVideoStream(video, userVideoStream);
});
});
socket.on('user-connected', userId => {
// user is joining`
setTimeout(() => {
// user joined
connectToNewUser(userId, stream)
}, 1000)
})
document.addEventListener("keydown", (e) => {
if (e.which === 13 && chatInputBox.value != "") {
socket.emit("message", chatInputBox.value);
chatInputBox.value = "";
}
});
socket.on("createMessage", (msg) => {
console.log(msg);
let li = document.createElement("li");
li.innerHTML = msg;
all_messages.append(li);
main__chat__window.scrollTop = main__chat__window.scrollHeight;
});
});
peer.on("call", function (call) {
getUserMedia(
{ video: true, audio: true },
function (stream) {
call.answer(stream); // Answer the call with an A/V stream.
const video = document.createElement("video");
call.on("stream", function (remoteStream) {
addVideoStream(video, remoteStream);
});
},
function (err) {
console.log("Failed to get local stream", err);
}
);
});
peer.on("open", (id) => {
socket.emit("join-room", ROOM_ID, id);
});
// CHAT
const connectToNewUser = (userId, streams) => {
var call = peer.call(userId, streams);
console.log(call);
var video = document.createElement("video");
call.on("stream", (userVideoStream) => {
console.log(userVideoStream);
addVideoStream(video, userVideoStream);
});
};
const addVideoStream = (videoEl, stream) => {
videoEl.srcObject = stream;
videoEl.addEventListener("loadedmetadata", () => {
videoEl.play();
});
videoGrid.append(videoEl);
let totalUsers = document.getElementsByTagName("video").length;
if (totalUsers > 1) {
for (let index = 0; index < totalUsers; index++) {
document.getElementsByTagName("video")[index].style.width =
100 / totalUsers + "%";
}
}
};
const playStop = () => {
let enabled = myVideoStream.getVideoTracks()[0].enabled;
if (enabled) {
myVideoStream.getVideoTracks()[0].enabled = false;
setPlayVideo();
} else {
setStopVideo();
myVideoStream.getVideoTracks()[0].enabled = true;
}
};
const muteUnmute = () => {
const enabled = myVideoStream.getAudioTracks()[0].enabled;
if (enabled) {
myVideoStream.getAudioTracks()[0].enabled = false;
setUnmuteButton();
} else {
setMuteButton();
myVideoStream.getAudioTracks()[0].enabled = true;
}
};
const setPlayVideo = () => {
const html = `<i class="unmute fa fa-pause-circle"></i>
<span class="unmute">Resume Video</span>`;
document.getElementById("playPauseVideo").innerHTML = html;
};
const setStopVideo = () => {
const html = `<i class=" fa fa-video-camera"></i>
<span class="">Pause Video</span>`;
document.getElementById("playPauseVideo").innerHTML = html;
};
const setUnmuteButton = () => {
const html = `<i class="unmute fa fa-microphone-slash"></i>
<span class="unmute">Unmute</span>`;
document.getElementById("muteButton").innerHTML = html;
};
const setMuteButton = () => {
const html = `<i class="fa fa-microphone"></i>
<span>Mute</span>`;
document.getElementById("muteButton").innerHTML = html;
};
ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<link rel="stylesheet" href="style.css" />
<script src="https://unpkg.com/peerjs#1.3.1/dist/peerjs.min.js"></script>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/peerjs/1.3.1/peerjs.js.map"></script> -->
<script src="/socket.io/socket.io.js"></script>
<script>
const ROOM_ID = "<%= roomId %>";
</script>
</head>
<body>
<div class="main">
<div class="main__left">
<div class="main__videos">
<div id="video-grid"></div>
</div>
<div class="main__controls">
<div class="main__controls_block">
<div
class="main__controls_button"
id="muteButton"
onclick="muteUnmute()"
>
<i class="fa fa-microphone"></i>
<span>Mute</span>
</div>
<div
class="main__controls_button"
id="playPauseVideo"
onclick="playStop()"
>
<i class="fa fa-video-camera"></i>
<span>Pause Video</span>
</div>
</div>
<!-- <div class="main__controls_block">
<div class="main__controls_button">
<i class="fa fa-shield"></i>
<span>Security</span>
</div>
<div class="main__controls_button">
<i class="fa fa-users"></i>
<span>Participants</span>
</div>
<div class="main__controls_button">
<i class="fa fa-comment"></i>
<span>Chat</span>
</div>
</div> -->
<div class="main__controls_block">
<div class="main__controls_button leaveMeeting" id="leave-meeting">
<i class="fa fa-times"></i>
<span class="">Leave Meeting</span>
</div>
</div>
</div>
</div>
<div class="main__right">
<div class="main__header">
<h6>Chat</h6>
</div>
<div class="main__chat__window" id="main__chat__window">
<ul class="messages" id="all_messages"></ul>
</div>
<div class="main__message_container">
<input
type="text"
id="chat_message"
placeholder="Type message here.."
/>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
I'm working on a simple chat app which was originally forked from a GitHub repo.
It works, but there's there's an error that randomly occurs and stops the app from running.
Server side code
const path = require('path');
const http = require('http');
const express = require('express');
const socketio = require('socket.io');
const formatMessage = require('./utils/messages');
const {
userJoin,
getCurrentUser,
userLeave,
getRoomUsers
} = require('./utils/users');
const app = express();
const server = http.createServer(app);
const io = socketio(server);
// Set static folder
app.use(express.static(path.join(__dirname, 'public')));
const botName = 'CCBot';
v = process.env;
// Run when client connects
io.on('connection', socket => {
socket.on('joinRoom', ({ token, username, room }) => {
console.log(token, username, room)
if (token == v.TOKEN_1){username = v.N_1}
else if (token == v.TOKEN_2){username = v.N_2}
else if (token == v.TOKEN_3){username = v.N_3}
else{
socket.emit("redirect"); return
}
const user = userJoin(socket.id, username, room, token, false);
socket.join(user.room);
// Welcome current user
socket.emit('message', formatMessage(botName, 'Welcome!'));
// Broadcast when a user connects
socket.broadcast
.to(user.room)
.emit(
'message',
formatMessage(botName, `${user.username} has joined the chat!`, 'green')
);
// Send users and room info
io.to(user.room).emit('roomUsers', {
room: user.room,
users: getRoomUsers(user.room)
});
});
// Listen for chatMessage
socket.on('chatMessage', (msg) => {
const user = getCurrentUser(socket.id)
console.log(user)
//The error originates on the below line
io.to(user.room).emit('message', formatMessage(user.username, msg, )); //The error occurs here
});
socket.on('userLeave', (id) => {
const user = getCurrentUser(id)
console.log(user)
socket.emit("sendMessages", user)
})
// Runs when client disconnects
socket.on('disconnect', () => {
const user = userLeave(socket.id);
if (user) {
io.to(user.room).emit(
'message',
formatMessage(botName, `${user.username} has left the chat.`, "red")
);
// Send users and room info
io.to(user.room).emit('roomUsers', {
room: user.room,
users: getRoomUsers(user.room)
});
}
});
});
const PORT = process.env.PORT || 3000;
server.listen(300, () => console.log(`Server running on port 3000`));
Util functions
const users = [];
// Join user to chat
function userJoin(id, username, room, auth, admin ) {
const user = { id, username, room, admin };
users.push(user);
return user;
}
// Get current user
function getCurrentUser(id) {
return users.find(user => user.id === id);
}
// User leaves chat
function userLeave(id) {
const index = users.findIndex(user => user.id === id)
if (index !== -1) {
return users.splice(index, 1)[0];
}
}
// Get room users
function getRoomUsers(room) {
return users.filter(user => user.room === room);
}
module.exports = {
userJoin,
getCurrentUser,
userLeave,
getRoomUsers
};
function formatMessage(username, text, color) {
return {
username,
text,
time: moment().tz("My Timezone here.").format('h:mm a'),
color
};
}
module.exports = formatMessage;
Client Side
let notif = undefined;
// Get username and room from URL
const { username, room, token, t2 } = Qs.parse(location.search, {
ignoreQueryPrefix: true,
});
listUsers = [];
const socket = io();
// Join chatroom
socket.emit('joinRoom', { username, room, token });
// Get room and users
socket.on('roomUsers', ({ room, users }) => {
outputRoomName(room);
outputUsers(users);
});
socket.on("redirect", () => {
try { window.location = "wrongpassword.html" }
catch (e) {
window.location = "about:blank"
}
})
// Message from server
socket.on('message', (message) => {
console.log(message);
outputMessage(message);
// Scroll down
chatMessages.scrollTop = chatMessages.scrollHeight;
});
socket.on("sendMessages", user => {
banned.push(user.username)
socket.emit("BAN_ALERT")
})
// Message submit
chatForm.addEventListener('submit', (e) => {
e.preventDefault();
// Get message text
let msg = e.target.elements.msg.value;
msg = msg.trim();
if (!msg) {
return false;
}
// Emit message to server
socket.emit('chatMessage', msg);
// Clear input
e.target.elements.msg.value = '';
e.target.elements.msg.focus();
});
function enableNotif(){
notif = true;
}
function disableNotif(){
notif = false;
}
// Output message to DOM
function outputMessage(message) {
if (message.text == ".clear" || message.text ==
".c") {
try {
document.querySelector(".chat-messages").innerHTML = ""
return
}
catch (e) {
window.location.reload(true)
return
}
}
else if (message.text == ".exit" || message.text == ".e"){
location = "about:blank"
}
else if (message.text == ".users"){
console.log(userList)
div = document.createElement('div');
div.classList.add('message');
const p = document.createElement('p');
p.classList.add('meta');
p.innerText = "CCBot";
p.innerHTML += ` <span>${message.time}</span>`;
div.appendChild(p);
const para = document.createElement('p');
para.classList.add('text');
para.innerText = listUsers;
color = "black";
if(color){para.style.color = color}
else{para.style.color = "black"}
div.appendChild(para);
document.querySelector('.chat-messages').appendChild(div);
}
else {
const div = document.createElement('div');
div.classList.add('message');
const p = document.createElement('p');
p.classList.add('meta');
p.innerText = message.username;
p.innerHTML += ` <span>${message.time}</span>`;
div.appendChild(p);
const para = document.createElement('p');
para.classList.add('text');
para.innerText = message.text;
color = message.color;
if(color){para.style.color = color}
else{para.style.color = "black"}
div.appendChild(para);
document.querySelector('.chat-messages').appendChild(div);
if (notif) {
var audio = new Audio('./media/notif.mp3');
console.log(audio)
audio.play();
}
}
}
// Add room name to DOM
function outputRoomName(room) {
roomName.innerText = room;
}
// Add users to DOM
function outputUsers(users) {
userList.innerHTML = '';
users.forEach((user) => {
const li = document.createElement('li');
li.innerHTML = `${user.username} <br><hr>`;
li.style = "cursor:pointer;"
userList.appendChild(li);
listUsers.push(li.innerText)
});
}
//Prompt the user before leave chat room
document.getElementById('leave-btn').addEventListener('click', () => {
const leaveRoom = confirm('Are you sure you want to leave the chatroom?');
if (leaveRoom) {
window.location = '../index.html';
} else {
}
});
Error
TypeError: Cannot read property 'room' of undefined
at Socket.socket.on.msg (/home/duck/server.js:51:16)
at Socket.emit (events.js:198:13)
at /home/duck/node_modules/socket.io/lib/socket.js:528:12
at process._tickCallback (internal/process/next_tick.js:61:11)
/home/duck/server.js:51
io.to(user.room).emit('message', formatMessage(user.username, msg));
^
The repo already has this issue opened, but there's no response to it.
I'm not sure why the error occurs.
Thanks in advance.
P.S, Please ignore anything related to banning/removing users.
I refer the below document to build using perfect negotiation pattern in my Peer Communicate web app.:
This one from MDN
This one from github
My web page content as below:
<html>
<head>
<meta charset="UTF-8">
<title>WebRTC Caller</title>
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
var peer=null;
var socket = io.connect();
var dc,pc,polite=false;
var localVideo,remoteVideo;
var makingOffer = false, ignoreOffer = false;
var configuration = {iceServers:
[{urls: "stun:stun.stunprotocol.org"},
{urls: "stun:stun.l.google.com:19302"},
{urls: "turn:numb.viagenie.ca", credential: "***", username: "***"}
]};
$( document ).ready(function() {
localVideo=document.getElementById("selfView");
remoteVideo=document.getElementById("remoteView");
});
function call() {
polite=true;
createConnection();
}
function createConnection(){
pc=new RTCPeerConnection(configuration);
pc.onnegotiationneeded=negotiationEventHandler;
pc.onicecandidate=iceCandidateEventHandler;
dc= pc.createDataChannel('chat');
}
function hangUp() {
dc.close();
pc.close();
}
function iceCandidateEventHandler(event){
if (event.candidate==null){
writeLog("All ICE Candidates are sent");
} else {
writeLog("Send ICE Candidate");
socket.emit('send',{candidate:event.candidate});
}
}
async function negotiationEventHandler(){
writeLog('Handle Negotitation');
try {
makingOffer = true;
await pc.setLocalDescription();
socket.emit("send",{ description: pc.localDescription });
} catch(err) {
writeLog(err);
} finally {
makingOffer = false;
}
}
function clearLog() {
chatelement = document.getElementById('logger');
chatelement.innerHTML='';
}
function writeLog(message) {
var logger=document.getElementById("logger");
logger.innerHTML=message+"<br>"+logger.innerHTML;
}
socket.on("receive",async (req)=>{
let ignoreOffer = false;
clearLog();
createConnection();
if (req.description){
const offerCollision = (req.description.type == "offer") &&
(makingOffer || pc.signalingState != "stable");
ignoreOffer = !polite && offerCollision;
if (ignoreOffer) {
return;
}
await pc.setRemoteDescription(req.description);
if (req.description.type =="offer") {
await pc.setLocalDescription();
socket.emit("send",{description: pc.localDescription});
}
} else {
if (req.candidate){
try {
if (pc.currentRemoteDescription){
await pc.addIceCandidate(req.candidate);
}
} catch(err) {
if (!ignoreOffer) {
throw err;
}
}
}
}
});
</script>
</head>
<body>
<table border=1>
<tr>
<td>
<h1> Self View </h1>
<video id="selfView" width="320" height="240" autoplay muted></video>
</td>
<td>
<h1> Remote View </h1>
<video id="remoteView" width="320" height="240" autoplay muted></video>
</td>
</tr>
<tr>
<td><button onclick="call()">Make A Call</button><button onclick="addMedia()">Add Media</button></td>
<td><button onclick="hangUp()">Hang Up</button></td>
</tr>
</table>
<div id="logger" style="border:1px solid black;width:100%;height:30%;overflow:scroll">
</div>
</body>
</html>
My node.js server side code as below:
var fs = require('fs');
var https = require('https');
var express = require('express');
var app = express();
var options = {
key: fs.readFileSync('private.key'),
ca: [fs.readFileSync('ca_bundle.crt')],
cert: fs.readFileSync('certificate.crt')
};
var serverPort = 443;
var server = https.createServer(options, app);
var io = require('socket.io')(server);
var userList=[];
app.use(express.static('public'));
io.on('connection', function(socket) {
console.log('new connection');
userList.push(socket.id);
socket.emit('message', 'This is a message from the dark side.');
});
io.on('connection', (socket) => {
console.log('a user connected#'+(new Date()));
socket.on("send",(req)=>{
console.log("Receive send request");
socket.broadcast.emit("receive", req);
});
socket.on('disconnect', () => {
console.log('user disconnected#'+(new Date()));
});
});
server.listen(serverPort, function() {
console.log('server up and running at %s port', serverPort);
});
I open the web page with 2 browsers (1 is chrome,1 firefox).
When I click on the "Make a call" button in chrome browser, the chrome prompt the following error message:
Uncaught (in promise) DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote answer sdp: Called in wrong state: kStable
When I click on the "Make a call" button in firefox browser, the firefox prompt the following error message:
InvalidStateError: Cannot set remote answer in state stable
What is the problem?How can I fix it?
I have my server and client on different projects.
When I'm using Live Server on vsCode then everything works. But not if I try to go to localhost, then I get 404.
How come?
server.js
const logger = require('./logger');
const PORT = 3000;
const io = require('socket.io')(PORT);
const express = require('express');
const app = express();
const socketListeners = require('./sockets/socketListeners');
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
io.on('connection', socket => socketListeners(socket, io, logger));
index.js (on client)
const socket = io('http://localhost:3000');
window.addEventListener('DOMContentLoaded', appendForm());
With debugger on client I get "io is not defined".
When I use "import io from "socket.io-client" on line 1 I get unexpected identifier.
EDIT
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Chat</title>
<link rel="stylesheet" href="style.css" />
<script src="http://localhost:3000/socket.io/socket.io.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.5/socket.io.min.js"></script>
<script src="name.js"></script>
<script src="index.js"></script>
<script src="chat.js"></script>
</head>
<body>
<section class="container"></section>
</body>
</html>
index.js
//const socket = io('http://localhost:3000');
const socket = io(':3000');
window.addEventListener('DOMContentLoaded', appendForm());
name.js
const container = document.querySelector('.container');
const nameContainer = document.createElement('section');
const title = document.createElement('h1');
const form = document.createElement('form');
const nameInput = document.createElement('input');
const submitName = document.createElement('button');
title.innerText = 'Enter Your Name';
submitName.innerText = 'Connect';
const getName = () => {
form.addEventListener('submit', e => {
e.preventDefault();
const name = nameInput.value;
socket.emit('new-user', name);
nameInput.value = '';
socket.off('invalid-name');
socket.on('invalid-name', () => {
feedbackBox(
"Make sure you don't have space or other funky characters in your name \n \n Must be between 3-13 characters"
);
});
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();
};
chat.js
//const socket = io('http://localhost:3000');
const showFeedback = document.createElement('section');
const messageContainer = document.createElement('section');
const messageForm = document.createElement('form');
const messageInput = document.createElement('input');
const submitMessage = document.createElement('button');
const disconnectButton = document.createElement('button');
submitMessage.innerText = 'Send';
disconnectButton.innerText = 'X';
messageContainer.classList.add('message-container');
messageForm.classList.add('send-container');
messageInput.classList.add('message-input');
submitMessage.classList.add('send-button');
disconnectButton.classList.add('disconnect-button');
const addMessageForm = () => {
showFeedback.classList.add('hide');
messageContainer.classList.remove('hide');
messageForm.classList.remove('hide');
disconnectButton.classList.remove('hide');
messageForm.appendChild(messageInput);
messageForm.appendChild(submitMessage);
container.appendChild(disconnectButton);
container.appendChild(messageContainer);
container.appendChild(messageForm);
appendMessage('You joined');
};
socket.on('chat-message', data => {
appendMessage(`${data.name}: ${data.message}`);
});
socket.on('user-connected', name => {
appendMessage(`${name} connected`);
});
socket.on('user-disconnected', name => {
appendMessage(`${name} left the chat`);
});
socket.on('user-inactivity-disconnected', name => {
appendMessage(`${name} was disconnected due to inactivity`);
});
messageForm.addEventListener('submit', e => {
e.preventDefault();
const message = messageInput.value;
socket.emit('send-chat-message', message);
if (message !== '') {
appendMyMessage(`You: ${message}`);
}
messageInput.value = '';
});
function appendMessage(message) {
const messageElement = document.createElement('section');
messageElement.innerText = message;
messageElement.classList.add('messages');
messageContainer.append(messageElement);
}
function appendMyMessage(message) {
const myMessageElement = document.createElement('section');
myMessageElement.innerText = message;
myMessageElement.classList.add('myMessage');
messageContainer.append(myMessageElement);
}
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');
socket.connect();
}
});
socket.on('inactive', () => {
socket.emit('disconnected');
messageContainer.classList.add('hide');
messageForm.classList.add('hide');
disconnectButton.classList.add('hide');
appendForm();
feedbackBox('Disconnected by the server due to inactivity');
});
First, fix your link to the Socket.io library in index.html. try cdn, like this <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.5/socket.io.min.js"></script>
I had a similar problem. Try this for connection to localhost:
const socket = io(':3000'); // pay attention on that it should be a string and starts with a colon symbol
EDIT
First you need to remove defer attribute from <script defer src="...> loading. This tells browser to keep loading the page while this defer script loading in background and run this script after it became loaded.
So, in your case, the script with Socket.io library will be run AFTER it required in the line const socket = io('...
Try using <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.5/socket.io.min.js"></script> in your client. I think it should work.