I have been trying to get the remote video stream to show up using .ontrack which is under the peer connection function in my code. Until now the .ontrack only fires on the caller side while on the callee it does not even when the function is called.
The log that checks if .ontrack fires would state "Got Remote Stream" but only on the caller side which might be the problem here but I'm not sure why would the other party not go into the IF statement that holds the .ontrack when it does not have the event.stream[0] which the statement is checking for.
I have added the console logs from both Caller and Callee below. Not shown in the images is that after a while the candidates would show null but both users are still connected.
main.js
'use strict';
var isInitiator;
var configuration = {
iceServers: [
{
urls: 'stun:stun.l.google.com:19302'
}
]
};
var pc = new RTCPeerConnection(configuration);
// Define action buttons.
const callButton = document.getElementById('callButton');
const hangupButton = document.getElementById('hangupButton');
/////////////////////////////////////////////
window.room = prompt('Enter room name:');
var socket = io.connect();
if (room !== '') {
console.log('Message from client: Asking to join room ' + room);
socket.emit('create or join', room);
}
socket.on('created', function(room) {
console.log('Created room ' + room);
isInitiator = true;
startVideo();
});
socket.on('full', function(room) {
console.log('Message from client: Room ' + room + ' is full :^(');
});
socket.on('joined', function(room) {
console.log('joined: ' + room);
startVideo();
callButton.disabled = true;
});
socket.on('log', function(array) {
console.log.apply(console, array);
});
////////////////////////////////////////////////
async function sendMessage(message) {
console.log('Client sending message: ', message);
await socket.emit('message', message);
}
// This client receives a message
socket.on('message', async function(message) {
try {
if (message.type === 'offer') {
await pc.setRemoteDescription(new RTCSessionDescription(message));
await pc
.setLocalDescription(await pc.createAnswer())
.then(function() {
sendMessage(pc.localDescription);
})
.catch(function(err) {
console.log(err.name + ': ' + err.message);
});
createPeerConnection();
} else if (message.type === 'answer') {
await pc.setRemoteDescription(new RTCSessionDescription(message));
} else if (message.type === 'candidate') {
await pc.addIceCandidate(candidate);
}
} catch (err) {
console.error(err);
}
});
////////////////////////////////////////////////////
const localVideo = document.querySelector('#localVideo');
const remoteVideo = document.querySelector('#remoteVideo');
// Set up initial action buttons status: disable call and hangup.
callButton.disabled = true;
hangupButton.disabled = true;
// Add click event handlers for buttons.
callButton.addEventListener('click', callStart);
hangupButton.addEventListener('click', hangupCall);
function startVideo() {
navigator.mediaDevices
.getUserMedia({
audio: true,
video: true
})
.then(function(stream) {
localVideo.srcObject = stream;
stream.getTracks().forEach(track => pc.addTrack(track, stream));
})
.catch(function(err) {
console.log('getUserMedia() error: ' + err.name);
});
callButton.disabled = false;
}
async function callStart() {
createPeerConnection();
callButton.disabled = true;
hangupButton.disabled = false;
if (isInitiator) {
console.log('Sending offer to peer');
await pc
.setLocalDescription(await pc.createOffer())
.then(function() {
sendMessage(pc.localDescription);
})
.catch(function(err) {
console.log(err.name + ': ' + err.message);
});
}
}
/////////////////////////////////////////////////////////
function createPeerConnection() {
try {
pc.ontrack = event => {
if (remoteVideo.srcObject !== event.streams[0]) {
remoteVideo.srcObject = event.streams[0];
console.log('Got remote stream');
}
};
pc.onicecandidate = ({ candidate }) => sendMessage({ candidate });
console.log('Created RTCPeerConnnection');
} catch (e) {
console.log('Failed to create PeerConnection, exception: ' + e.message);
alert('Cannot create RTCPeerConnection object.');
return;
}
}
function hangupCall() {
pc.close();
pc = null;
callButton.disabled = false;
hangupButton.disabled = true;
console.log('Call Ended');
}
index.js
'use strict';
var express = require('express');
var app = (module.exports.app = express());
var path = require('path');
var server = require('http').createServer(app);
var io = require('socket.io')(server);
const PORT_NO = process.env.APP_PORT || 3000;
server.listen(PORT_NO);
app.get('/', function(request, response) {
response.sendFile(path.resolve('./index.html'));
});
app.use(express.static('.'));
io.on('connection', socket => {
function log() {
const array = ['Message from server:'];
for (let i = 0; i < arguments.length; i++) {
array.push(arguments[i]);
}
socket.emit('log', array);
}
socket.on('message', message => {
log('Got message:', message);
socket.broadcast.emit('message', message);
});
socket.on('create or join', room => {
var clientsInRoom = io.sockets.adapter.rooms[room];
var numClients = clientsInRoom
? Object.keys(clientsInRoom.sockets).length
: 0;
// max two clients
if (numClients === 2) {
socket.emit('full', room);
return;
}
log('Room ' + room + ' now has ' + (numClients + 1) + ' client(s)');
if (numClients === 0) {
socket.join(room);
log('Client ID ' + socket.id + ' created room ' + room);
socket.emit('created', room, socket.id);
} else {
log('Client ID ' + socket.id + ' joined room ' + room);
io.sockets.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room, socket.id);
io.sockets.in(room).emit('ready');
}
});
});
Let the joiner be the initiator.
I'm guessing 'created' happens before 'joined'? I.e. one party creates the room before the second party joins it?
Since your startVideo() does more than start the local video—it actually begins connection negotiation—I suspect you begin negotiating before the second party is ready, a race. Instead try:
socket.on('created', function(room) {
console.log('Created room ' + room);
startVideo();
});
socket.on('joined', function(room) {
console.log('joined: ' + room);
isInitiator = true; // <-- begin negotiating once 2nd party arrives.
startVideo();
});
You're missing a call to createPeerConnection() on the answerer side, which means the answerer isn't properly set up to signal ICE candidates or fire the track event.
You only call it from startCall(), so this would only work if you hit the call button on both ends at almost exactly the same time.
createPeerConnection() is a misnomer. Instead, just initialize the pc with its ontrack and onicecandidate callbacks on page load.
Still not working?
The rest of your WebRTC-related code you're showing us looks fine—except you're calling getUserMedia twice on the answerer side, which is redundant, but shouldn't be a problem.
I suspect a bug in your server logic. E.g. you're not showing us how emitting 'create or join' turns into either a 'created' or 'joined' socket message. You're also trying to predetermine which side is which in the offer/answer exchange, which is fine, except this means you have a non-working Call button on the answerer side. Most demos just let whoever pushes the button first be the offerer, though that might create glare. Just FYI.
This is a two-way call. In which direction is remoteVideo not working?
Also, you have a two-way call here, sending video in both directions, yet you've not mentioned which remoteVideo you're not seeing.
For a working example, check out my two-way tab demo. Open it in two adjacent windows in the same browser, and click the Call button in one of them to connect. You should see (the same) video being sent both ways. It relies on a localSocket hack using localStorage.
Related
I'm trying to build a TCP server on node JS. The idea is to have multiple TCP clients connect and send / receive data(from server to client / client to server) and to have some sort of authentication (at least to enter a strong password) Also not sure if this approach is worth it. But, I've come up with something (most from online sources and docs) and crated below code.
Code runs and I can connect, but if I send data from client to server, the "password" check function fires up and each time I enter the correct password, a new (duplicate connection) is created. Seems like it keeps calling the same function on each input.
Desired behavior would be ; once client try's to connect, needs to provide the password and then start sending date. Also could someone give me a hint on how to send data back from server, or is it even possible. Or do I need to create a separate function for server.createConnection()
thanks in advance
UPDATE :I've changed the code a bit, but the main issue remains. this bit was supposed to check whether "clientAddress" exists and if so skip the auth part all together.
server.on('connection', (socket) => {
let clientAddress = `${socket.remoteAddress}:${socket.remotePort}`;
console.log(clientAddress)
if(sock.indexOf(clientAddress) !== -1){
console.log('devie found, opening communication')
newConnectionHandler(socket,clientAddress)
} else {
console.log('devie not found, need to authenticate')
userAuth(socket,clientAddress)
}
but as you can guess, it's not working :) if I manually specify the "clientAddress" it works , if I place "sock.push(clientAddress);" within the first block of code, it also works. No auth is asked. But when it's placed within
function userAuth(socket,clientAddress){
socket.write('password : ' )
socket.on('data', function (data) {
let pass = data.toString()
if (pass == password) {
sock.push(clientAddress);
console.log(sock)
newConnectionHandler(socket,clientAddress)
return;
} else {
//console.log(pass)
socket.write('Sorry, you cannot access the server \n')
console.log('acess denied for ' + socket.remoteAddress + ':' + socket.remotePort + '\n')
socket.write('connection closed')
socket.destroy()
}
})
}
code does run as expected and goes all the way till
function newConnectionHandler(socket,clientAddress){
//console.log(sock)
socket.write('Welcome \n')
socket.on('data', function(data1){
console.log("Client Sent: " + data1);
});
but as soon as I send a new message from the client, it goes back as if it was never authenticated and treats my input as the password and because it does not match with the actual password,it destroys the connection.
can someone please give me a hand...
const net = require('net');
const port = 3001;
const host = '192.168.0.165';
const server = net.createServer()
let sock = [];
let password = 123
//server.on('listening',createConnection);
server.on('connection', (socket) => {
let clientAddress = `${socket.remoteAddress}:${socket.remotePort}`;
console.log(clientAddress)
if(sock.indexOf(clientAddress) !== -1){
console.log('devie found, opening communication')
newConnectionHandler(socket,clientAddress)
} else {
console.log('devie not found, need to authenticate')
userAuth(socket,clientAddress)
}
server.on('error', errorHandler);
function errorHandler(err){
console.log(`Error occurred in ${clientAddress}: ${err.message}`);
}
function userAuth(socket,clientAddress){
socket.write('password : ' )
socket.on('data', function (data) {
let pass = data.toString()
if (pass == password) {
sock.push(clientAddress);
console.log(sock)
newConnectionHandler(socket,clientAddress)
return;
} else {
//console.log(pass)
socket.write('Sorry, you cannot access the server \n')
console.log('acess denied for ' + socket.remoteAddress + ':' + socket.remotePort + '\n')
socket.write('connection closed')
socket.destroy()
}
})
}
function newConnectionHandler(socket,clientAddress){
//console.log(sock)
socket.write('Welcome \n')
socket.on('data', function(data1){
console.log("Client Sent: " + data1);
});
socket.once('close', (data) => {
let index = sock.findIndex((o) => {
return o.remoteAddress === socket.remoteAddress && o.remotePort === socket.remotePort;
})
if (index !== -1) sock.splice(index, 1);
sock.forEach((sock) => {
socket.write(`${clientAddress} disconnected\n`);
});
console.log(`connection closed: ${clientAddress}`);
});
}
/* function createConnection(){
// Start a connection to the server
var socket = server.on('connect',function(){
// Send the initial message once connected
socket.write({question: "Hello, world?"});
});
// Whenever the server sends us an object...
socket.on('data', function(data){
// Output the answer property of the server's message to the console
console.log("Server's answer: " + data.answer);
});
} */
})
server.listen(port, host, () => {
console.log('TCP Server is running on port ' + port + '.');
});
so it appears as the only bit of code that was causing the authentication loop was the
function userAuth(socket,clientAddress){
socket.write('password : ' )
**socket.on('data', function (data) {**
let pass = data.toString()
after changing "on" with "once" it is now functioning properly. I tested with two TCP clients, both connected and was asked to enter a password. They can both actively send messages to the server and both disconnected properly in the end.
this is the code if anyone finds any use for it :) the connection it's self is still unencrypted so not good for sending/receiving sensitive data.
const net = require('net');
const port = 3001;
const host = '192.168.0.165';
const server = net.createServer()
let sock = [];
let password = 123
//server.on('listening',createConnection);
server.on('connection', (socket) => {
let clientAddress = `${socket.remoteAddress}:${socket.remotePort}`;
console.log(clientAddress)
if(sock.indexOf(clientAddress) !== -1){
console.log('devie found, opening communication')
newConnectionHandler(socket,clientAddress)
} else {
console.log('devie not found, need to authenticate')
userAuth(socket,clientAddress)
}
})
server.on('error', errorHandler);
function errorHandler(err){
console.log(`Error occurred in ${clientAddress}: ${err.message}`);
}
function userAuth(socket,clientAddress){
socket.write('password : ' )
socket.once('data', function (data) {
let pass = data.toString()
if (pass == password) {
sock.push(clientAddress);
console.log(sock)
newConnectionHandler(socket,clientAddress)
return;
} else {
//console.log(pass)
socket.write('Sorry, you cannot access the server \n')
console.log('acess denied for ' + socket.remoteAddress + ':' + socket.remotePort + '\n')
socket.write('connection closed')
socket.destroy()
}
})
}
function newConnectionHandler(socket,clientAddress){
//console.log(sock)
socket.write('Welcome \n')
socket.on('data', function(data1){
console.log("Client Sent: " + data1);
});
socket.on('close', function(data) {
let index = sock.findIndex(function(o) {
return o.remoteAddress === sock.remoteAddress && o.remotePort === sock.remotePort;
})
if (index !== -1) sock.splice(index, 1);
console.log('CLOSED: ' + socket.remoteAddress + ' ' + socket.remotePort);
});
}
/* function createConnection(){
// Start a connection to the server
var socket = server.on('connect',function(){
// Send the initial message once connected
socket.write({question: "Hello, world?"});
});
// Whenever the server sends us an object...
socket.on('data', function(data){
// Output the answer property of the server's message to the console
console.log("Server's answer: " + data.answer);
});
} */
server.listen(port, host, () => {
console.log('TCP Server is running on port ' + port + '.');
});
I am getting the error from this page. The error is client.js:166 Uncaught TypeError: Cannot read property 'addIceCandidate' of undefined. Below is the Code. How to remove that error? The video from the other browser is sending stream to the server, while adding the stream in both browser, there becomes the error. Where is the error occurs after it got stream.
var divSelectRoom = document.getElementById("selectRoom");
var divConsultingRoom = document.getElementById("consultingRoom");
var inputRoomNumber = document.getElementById("roomNumber");
var btnGoRoom = document.getElementById("goRoom");
var localVideo = document.getElementById("localVideo");
var remoteVideo = document.getElementById("remoteVideo");
// these are the global variables
var roomNumber;
var localStream;
var remotestream;
var rtcPeerConnection;
//these are the STUN servers
var iceServers = {
'iceServers': [
{
url:'stun:stun.l.google.com:19302'
},
{
url:'stun:stun.services.mozilla.com'
},
{
url: 'turn:numb.viagenie.ca',
credential: 'muazkh',
username: 'webrtc#live.com'
}
]
};
var streamConstraints = { audio: true, video: true };
var isCaller;
// Here we connect to the socket iO server. We Will create it later.
var socket = io();
// Here we Odd a click event to the button
btnGoRoom.onclick = function() {
if (inputRoomNumber.value == ""){
alert("Please type a room number");
}
else {
roomNumber = inputRoomNumber.value; //we take the value from the element
socket.emit('create or join', roomNumber); //we send a message to server
divSelectRoom.style = "display: none;"; //hide selectRoom div
divConsultingRoom.style = "display block;"; //show consultingRoom div
}
};
// when server emits created
socket.on("created", function(room){
console.log('created function');
//caller gets user media devices with defined constraints
navigator.mediaDevices.getUserMedia(streamConstraints).then(function(stream){
console.log('Created function');
const mediaStream = new MediaStream();
const video = document.getElementById('localVideo');
video.srcObject = stream;
localStream = stream; //sets local stream to variable
//localVideo.src = URL.createObjectURL(stream); //shows stream to user
isCaller = true;//sets current user as caller
}).catch(function(err){
console.log('An error occured when accessing media devices');
console.log(err.name + ": " + err.message);
});
});
// when server emits ends
socket.on("joined", function(room){
console.log('Joined function');
//caller gets user media devices with defined constraints
navigator.mediaDevices.getUserMedia(streamConstraints).then(function(stream){
localStream = stream; //sets local stream to variable
const mediaStream = new MediaStream();
const video = document.getElementById('localVideo');
video.srcObject = stream;
//localVideo.src = URL.createObjectURL(stream); //shows stream to user
socket.emit('ready',roomNumber); //sends message to the server
console.log('Joined function');
}).catch(function(err){
console.log('An error occured when accessing media devices');
console.log(err.name + ": " + err.message);
});
});
//when server emits ready
socket.on('ready', function(){
console.log('client ready function');
if(isCaller){
//creates an RTCPeerConnection object
rtcPeerConnection = new RTCPeerConnection(iceServers);
//adds event listeners to the newly created object
rtcPeerConnection.onicecandidate = onIceCandidate;
rtcPeerConnection.ontrack = onAddStream;
//add the current local stream to the object
rtcPeerConnection.addStream(localStream);
//prepares an offer
rtcPeerConnection.createOffer(setLocalAndOffer, function(e){
console.log(e);
});
}
});
//when server emits offer
socket.on('offer',function(event){
if(isCaller){
console.log('client offer function');
//creates an RTCPeerConnection object
rtcPeerConnection = new RTCPeerConnection(iceServers);
//adds event listeners to the newly created object
rtcPeerConnection.onicecandidate = onIceCandidate;
rtcPeerConnection.ontrack = onAddStream;
//adds the current local stream to the object
rtcPeerConnection.addStream(localStream);
//stores the offer as remote description
rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(event));
//Prepares an Answer
rtcPeerConnection.createAnswer(setLocalAndAnswer, function(e){
console.log(e);
});
}
});
//when server emits answer
socket.on('answer', function(event){
console.log('client answer function');
//stores it as remote description
rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(event));
});
//when server emits candidate
socket.on('candidate', function(event){
console.log('client candidate function');
var pc1 = {
addIceCandidate : function(val) {
console.log(val);
}
}
//creates a candidate object
var candidate1 = new RTCIceCandidate({
type: 'offer',
sdpMLineIndex: event.label,
candidate: event.candidate
});
addIceCandidate(candidate1);
// if(rtcPeerConnection)
// console.log('Okay Peer');
// //stores candidate
// rtcPeerConnection.addIceCandidate(candidate);
});
function addIceCandidate(message) {
if (message.candidate != null) {
rtcPeerConnection.addIceCandidate(message);
}
}
//when a user receives the other user's video and audio stream
function onAddStream(event){
console.log('On Add Stream function');
const mediaStream = new MediaStream();
const rvideo = document.getElementById('remoteVideo');
rvideo.srcObject = event.stream;
//remoteVideo.src = URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
//These are the functions referenced before as listeners for the peer connection
//sends a candidate message to server
function onIceCandidate(event){
console.log('On Ice candidate function');
if(event.candidate){
console.log('sending ice candidate');
socket.emit('candidate', {
type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate,
room: roomNumber
});
}
}
//stores offer and sends message to server
function setLocalAndOffer(sessionDescription){
console.log('LocalAndOffer function');
rtcPeerConnection.setLocalDescription(sessionDescription);
socket.emit('offer', {
type: 'offer',
sdp: sessionDescription,
room: roomNumber
});
}
//stores answer and sends message to server
function setLocalAndAnswer(sessionDescription){
console.log('LocalAndAnswer function');
rtcPeerConnection.setLocalDescription(sessionDescription);
socket.emit('answer', {
type: 'answer',
sdp: sessionDescription,
room: roomNumber
});
}
You are probably sending the candidate socket message before the rtcPeerConnection is initialized. Then you get the error in the addIceCandidate function.
You need to check if you have a webrtc peer connection object before you call addIceCandidate when candidate web socket message arrives.
Once you init it and add event andlers it can any moment find ice candidates and hence trigger the related event, onicecandidate. Same for the session descriptions and its related event onnegotiationneeded event.
So be ready on other end you send those messages over websocket to upon they trigger on one end.
socket.on('join room', function (data) {
var room = data.room;
var msg = "<span style='color:darkgreen;'>" + data.user + " has joined chat.</span>";
socket.join(room, function () {
console.log(socket.id + " now in rooms ", socket.rooms);
});
io.in(room).emit('system message', msg);
});
If I change io.in(room) to socket.broadcast the system message goes through. For whatever reason though, it will not send messages to specific rooms, it just does nothing, no errors either. Any ideas what's going wrong here?
It seems the code above is on back-end, correct?
Try the following, adapt it with your needs if necessary:
io.on('connection', function (socket) {
// You should expect ONLY one user connected,
// > otherwise, you're creating multiple sessions :)
console.log('a user connected');
socket.on('join room', function (data) {
var room = data.room;
console.log('Room: ', room);
var msg = "<span style='color:darkgreen;'>" + data.user + " has joined chat.</span>";
socket.join(room); // No callback needed
console.log(socket.id + " now in rooms ", socket.rooms);
io.to(room).emit('system message', msg);
// Change it to io.to(room)
});
});
I noticed you're asking to join on front-end using:
$(".channelChange").click( function(evt) {
var socket = io();
$("#messages").html("");
evt.stopPropagation();
if($.inArray(evt.currentTarget, $(this).children())){
console.log("user moved to " + evt.currentTarget.id);
}
setCookie("currentRoom", evt.currentTarget.id, 1);
$(".roomName").html("<span class='3Dtext'>" + evt.currentTarget.id + "</span>");
$("#enter-sound").get(0).play();
getMessages(getCookie("currentRoom"));
// Here you ask to join room
socket.emit('join room', { user: getCookie("username"), room: getCookie("currentRoom") });
})
And receiving the system message on:
$(function(){
var socket = io();
socket.on('system message', function(msg) {
alert("test");
$('#messages').append($('<div class="systemMessage">').html(msg));
var height = $("#messages")[0].scrollHeight;
$.mobile.silentScroll(height);
});
The main problem as you'll see, is that you're calling io() everytime, this created different sessions,
1. Session one joins a room
2. A different session will await for system message
Hence, you need a single io() session, a quick work arround is to make a self invoking function:
const setIo = (function (){
const _io = io();
return () => _io;
})()
And then Replace all io() declarations to: var socket = setIo();
such as:
$(".channelChange").click( function(evt) {
var socket = setIo();
$("#messages").html("");
evt.stopPropagation();
This will create a single io() session instead of different ones, and re-use it everytime you call setIo()
You'll eventually see the alert pop up:
socket.join() is asynchronous. It has not completed yet when you're trying to send to the room, so no message goes to that socket because it isn't yet in the room. So, change this:
socket.join(room, function () {
console.log(socket.id + " now in rooms ", socket.rooms);
});
io.in(room).emit('system message', msg);
to this:
socket.join(room, function () {
console.log(socket.id + " now in rooms ", socket.rooms);
io.in(room).emit('system message', msg);
});
So, in this new version you're not sending to the room until AFTER the .join() has completed rather than before.
You can see a similar example right in the socket.io doc here.
Dear friends I have a small issue while trying to keep track of a logged in users in chat. The chat is based on two separate channels that work with the help of namespaces:
chatInfra - to handle logged in users and send welcome messages.
chatCom - to handle messageing between users.
I have searched a lot but I found only theoretical explanations that the best solutions is to store users into array. Therefore I tried to keep the track of logged in users by storing them in array and then iterating through them but still the result is not good.
The problem is that after entering to the chat, only the first logged in user's name appears on screen, whereas the second user's name is not visible.
This is my server side code, I am trying to store users into clients array:
var clients = [];
var chatInfra = io.of("/chat_infra").on("connection", function(socket){
socket.on("set_name", function (data) {
clients.push(data.name);
socket.emit('name_set', data);
socket.send(JSON.stringify({
type:'serverMessage',
message:'Welcome!'
}));
socket.broadcast.emit('user_entered', data);
});
});
var chatCom = io.of("/chat_com").on("connection", function (socket) {
socket.on('message', function (message) {
message = JSON.parse(message);
for(var key in clients){
if(message.type == "userMessage"){
message.username = clients[key];
console.log('message : ', message);
socket.broadcast.send(JSON.stringify(message));
message.type = "myMessage";
socket.send(JSON.stringify(message));
}
}
});
});
Here is how it looks in browser: http://screencast.com/t/lshnfcGZ8E8
Here is the full code: https://gist.github.com/johannesMatevosyan/0b9f7e588338dbb6b7f5
I think you're creating an unnecessary overkill by using different namespaces. Here's a clearer working example achieving the same functionality:
server.js
var app = require("express")();
var server = require("http").Server(app);
var io = require("socket.io")(server);
var chat = io.of("/chat").on("connection", function(socket){
socket.on("set_name", function (data) {
socket.username = data.name;
socket.emit("name_set", data);
socket.emit("message", {
type :"serverMessage",
message :"Welcome!"
});
chat.emit("message", {
type :"serverMessage",
message : data.name + " has joined the room.!"
});
});
socket.on("message", function (message) {
message.username = socket.username;
chat.emit("message", message);
});
});
app.get("/", function (req, res) {
res.sendfile(__dirname + "/index.html");
});
server.listen(3000);
client.js
var socket = io.connect('http://localhost:3000/chat');
socket.on('name_set', function (data) {
$('#nameform').hide();
$('#messages').append('<div class="systemMessage">Hello ' + data.name + '</div>');
});
socket.on('message', function (message) {
var userNameHtml = message.username ? '<span class="name">' + message.username + ':</span>' : '';
$('#messages').append('<div class="' + message.type + '">' + userNameHtml + message.message + '</div>');
});
$(function () {
$('#setname').click(function () {
socket.emit("set_name", { name: $('#nickname').val() });
});
$('#send').click(function () {
socket.emit("message", {
message : $('#message').val(),
type : 'userMessage'
});
$('#message').val('');
});
});
I don't think you need a separate event handler for user_entered, since you are treating it as a regular message and not doing anything else with the event. Also a couple of things:
You don't need to first connect to the server and then to the namespace address, connecting to the later is just fine.
Don't set event listeners within callbacks, that will result in setting them multiple times.
I'm writing a multi-room chat app with Node.js and Socket.IO and I'm wondering how to handle data that needs to persist for the duration of a session, when the transport is xhr-polling.
Currently, on the client I'm sending a "join" event on connection, which passes a long a few variables (such as username), which I store server-side using Socket.IO's get and set methods. They are then available until the client disconnects.
This works fine if the transport is Websockets, but if it falls back to xhr-polling, the join event is emitted on each connect, which happens every 5-10 seconds. (Similarly, "such and such has left/joined the room" is sent on each xhr-poll request, which is also undesirable.)
I'm not sure what the best way forward is. I can't disable xhr-polling as it's a needed fallback for flashless IE for one.
Here is the relevant client code:
socket.on("connect", function(){
socket.emit("join", { username: username, room: room });
});
And on the server:
var io = require("socket.io").listen(8124)
, buffer = {}
, max_room_buffer = 15;
io.sockets.on("connection", function(socket) {
socket.on("join", function(data){
if(data.username && data.room) {
socket.set("data", data);
socket.join(data.room);
if(!buffer[data.room]) buffer[data.room] = [];
socket.broadcast.to(data.room).emit("message", "<em>" + data.username + " has joined room " + data.room + "</em>");
socket.emit("message", "<em>You have joined room " + data.room + "</em>");
socket.emit("message", buffer[data.room]);
}
});
socket.on("message", function(message) {
if(message) {
socket.get("data", function(err, data){
if(data) {
var msg = "<span>" + data.username + ":</span> " + message;
buffer[data.room].push(msg);
if(buffer[data.room].length > max_room_buffer) buffer[data.room].shift();
io.sockets.in(data.room).emit("message", msg);
}
});
}
});
socket.on("disconnect", function () {
socket.get("data", function(err, data){
if(data) {
socket.leave(data.room);
socket.broadcast.to(data.room).emit("message", "<em>" + data.username + " has left room " + data.room + "</em>");
}
});
});
});
Thanks in advance.
Perhaps I'm missing something, but wouldn't this work.
Client:
var firstJoin = true;
socket.on("connect", function(){
socket.emit("join", { username: username, room: room, firstJoin: firstJoin });
firstJoin = false;
});
Server:
io.sockets.on("connection", function(socket) {
socket.on("join", function(data){
if(data.username && data.room) {
socket.set("data", data);
socket.join(data.room);
if(!buffer[data.room]) buffer[data.room] = [];
if (data.firstJoin) {
socket.broadcast.to(data.room).emit("message", "<em>" + data.username + " has joined room " + data.room + "</em>");
socket.emit("message", "<em>You have joined room " + data.room + "</em>");
socket.emit("message", buffer[data.room]);
}
}
});
As for the fact that the connection event occurs on every new poll, that's an unavoidable consequence of XHR polling. Since each poll is a new HTTP request, there is no state from the previous request, so you need to set it up again.
If you want to reduce how frequently this happens, you could try increasing the polling duration from its default of 20. For example:
io.configure(function() {
io.set('polling duration', 60);
});