I have a node js server witch is for a chat page. Now I want to make a private messiging part of the server. Currently i have the following code:
Client:
rooms.addEventListener('submit', function(e){
e.preventDefault();
if(roomName.value){
socket.emit('Join-Room', roomName.value);
roomName.value = '';
}
});
Server:
socket.on('Join-Room', (roomName) => {
socket.join(roomName);
});
When I try to join one room nothing happens and i am still in the main chat.
Related
I'm using an app.post method in Express like so:
app.post('/race', function(req,res) {
let raceResponse = {
user_name: req.body.userName
}
console.log('Express has received race.');
//Socket.IO
let race = io.of('/race').on('connection', (socket) => {
console.log('A user has entered the race!');
socket.on('racePageReady', function() {
console.log('race page ready recieved');
socket.emit('racePageInfo', raceResponse);
});
socket.on('createRoom', function(roomName) {
socket.join(roomName);
let clients = io.sockets.adapter.rooms[roomName].sockets;
console.log("A room with the name " + roomName + "was created.");
console.log(clients);
socket.emit('roomCreated', clients);
});
socket.on('joinRoom', function(roomName) {
socket.join(roomName);
let clients = io.sockets.adapter.rooms[roomName].sockets;
console.log('A user joined the room with the name: ' + roomName + ". The user's name is " + raceResponse.user_name);
console.log(clients);
socket.emit('roomCreated', clients);
});
});
res.sendFile(path.join(__dirname, '/client/race/index.html'));
}
The page is being sent fine, but the console.log and all the other Socket.IO stuff just doesn't happen. I'm finding this peculiar, because I have a different app.post method that works just fine, all console.logging and Socket.IO business happens. Here's the code:
app.post('/city', function(req,res) {
let cityResponse = {
user_name: req.body.userName
}
console.log('Express has received city.');
//Socket.IO
let city = io.of('/city').on('connection', (socket) => {
socket.id = Math.random();
socket.name = cityResponse.user_name;
SOCKET_LIST[socket.id] = socket; //defined earlier
User.onConnect(socket); //defined earlier
socket.on('cityPageReady', function() {
socket.emit('cityPageInfo', cityResponse);
console.log('city page ready recieved');
});
console.log('A user has connected to the city!');
console.log("Socket: " + socket);
console.log("Socket ID: " + socket.id);
console.log("SOCKET_LIST: ");
console.log(SOCKET_LIST);
socket.on('chat message', function(msg, user) {
console.log('User ' + user + ' sent the message : ' + msg);
socket.emit('chat message', msg, user);
});
});
res.sendFile(path.join(__dirname, '/client/city/index.html'));
});
As far as I can tell, both methods look pretty much the same, except for the Socket.IO stuff in the middle. I'm fairly certain that the io.of method is correct, as it's working for the City page, but not the race.
The only other difference is the way that the two pages are accessed. The City page is accessed through a HTML form with an action attribute, whereas the Race page is accessed through a HTML link (on the City page) with a href attribute.
Both methods are shown below:
CITY
<form id="cityForm" action="http://localhost:4000/city" method="POST">
User name: <input type="text" name="userName">
button type="submit" id="formSubmit">Submit</button>
</form>
RACE
<div><a href="http://localhost:4000/race"></div>
Can anyone see why this peculiar behaviour is occuring? If any other information is needed please let me know so that I can include it.
When clicking an on HTML link, the browser does a GET HTML request. When you submit a Form (with method="POST"), the browser does a POST request.
When using app.post(), you tell express to listen for POST requests. If you want express to listen for GET requests, you should use app.get()
If this is a vague question, please let me know, so that I can specify. I really want to get around this stump.
I have read several cheat sheets regarding how to properly broadcast a message to a client room. My reference is: https://github.com/socketio/socket.io/blob/master/docs/emit.md.
To describe my problem:
The chat page comes up with the send button correctly, however as soon as I click send, nothing is ever sent.
The interesting thing, is that whenever I am not using rooms, and just use the default namespace, I can get messages.
Any idea on what is going on? Thank you!!
server.js
io.on('connection', function(socket) {
global.rooms = 'room1';
socket.on('subscribe', function(room) {
socket.join(rooms);
console.log(socket.id + 'joining room', rooms)
})
socket.on('chat', function(data) {
io.to(rooms).emit('chat', data);
})
})
chat.jade
extends layout
block content
h2.page-header Chat Page
div(id='the-chat')
div(id='chat-window')
div(id='output')
input(id='handle', type='text', value = user.name, style= 'width: 0px; visibility: hidden;')
input(id='message', type='text', placeholder='message')
button(id='send' value='Send') Send
//imports the socket.io functionality on the client side for the chat.jade application
script(src="/socket.io/socket.io.js")
script.
//variable created that mirrors connection made in the backend
//matches the connection made in the server side
var socket = io.connect('http://localhost:3000')
//Query dom
var message = document.getElementById('message')
var handle = document.getElementById('handle')
var btn = document.getElementById('send')
var output = document.getElementById('output')
//emit events
btn.addEventListener('click', function() {
socket.emit('chat', {
message: message.value,
handle: handle.value
})
})
socket.on('chat', function(data) {
output.innerHTML += '<p><strong>' + data.handle + ': </strong>' + data.message + '</p>';
document.getElementById('message').value = "";
})
Home.jade
extends layout
block content
h2.page-header(style = "text-align: center;").
Home Page
if (user.isTutor)
b(style = "text-align: center;")
form(method='post', action = '/home/available')
input.btn.btn-primary(type = 'submit',name='isAvailable', value = 'Available', id = 'button4')
form(method='post', action = '/home/unavailable')
input.btn.btn-primary(type = 'submit',name='isUnavailable', value = 'Unavailable', id = 'button5')
script(src="/socket.io/socket.io.js")
script.
var btn = document.getElementById('button4')
//var space = '#{user.room}'
var socket = io.connect()
btn.addEventListener('click', function() {
socket.emit('subscribe', 'room1')
})
div(id = 'magic')
form(method='get')
if (user.hasApplied)
input.btn.btn-primary(type = 'submit', onclick = "javascript: form.action = '/findatutor';" name='find', value = 'Find a Tutor', class = 'middle', id = 'button7')
else if (user.hasApplied == false)
input.btn.btn-primary(type = 'submit', onclick = "javascript: form.action = '/findatutor';" name='find', value = 'Find a Tutor', id = 'button1')
input.btn.btn-primary(type = 'submit', onclick = "javascript: form.action = '/apply';" name='become', value = 'Become a Tutor', id = 'button2')
Home.js
router.post('/available', ensureAuthenticated, (req,res,next) => {
var io = res.locals['socketio']
db.collection('DefaultUser').update({_id: req.user._id}, {$set: {isAvailable: true}});
res.redirect('../chat')
})
The problem is you did not specify where to render the messages. As I understood, you have no problem creating rooms, so I will explain step by step how to handle after that point.
According to your code this is the communication between server and client for sending messages
//server.js
socket.on('chat', function(data) {
io.to(rooms).emit('chat', data);
})
//chat.jade
btn.addEventListener('click', function() {
socket.emit('chat', {
message: message.value,
handle: handle.value
})
})
First client is sending the message (typing the input box) to the server and when server receives it, it has to send the same message to all other clients. once clients receive this message, clients have to figure out how to display it. But when server sends the received message, then you have to initiate a new event. let's call it display. so your code should be like this:
//server.js
//i always use arrow functions, but I will follow along your code
socket.on('chat', function(data) {
io.to(rooms).emit('display', data);
})
Now your client should be listening for this event and should be handling where to display it:
//chat.jade
socket.on('display', (data) => {
const displayedMessage = pug.render(messageTemplate, {
message: data.message,
})
$messages.insertAdjacentHTML('beforeend', displayedMessage)
})
Since Jade has been renamed to pug, i used pug here. So i will render {message: data.message} into html in a script tag then placed it into DOM ELEMENT $message.
I am not sure where you want to render handle:handle.value i will just show how to display message. similarly, you can handle it.
Now what are messageTemplate, $messages and insertAdjacentHTML()?
create a div tag and a script tag in your html
//this is where you are gonna display the message
<div id="messages" class="chat__messages"></div>
<!-- template messages -->
<script id="message-template" type="text/html">
<div>
<p>{{message}}</p>
</div>
</script>
//chat.jade
const $messages = document.getElementById("messages");
const messageTemplate = document.getElementById("message-template").innerHTML;
The insertAdjacentHTML() method inserts a text as HTML, into a specified position. you can get more explanation and examples here:
https://www.w3schools.com/jsref/met_node_insertadjacenthtml.asp
socket io code looks long and complicated but if you know the logic and move step by step it will easy to implement it.
Try
io.in(rooms).emit('chat', data);
Missing this under global.rooms = 'room1';
var rooms = global.rooms
Longer Answer: At least what I can see, you've added "rooms" as a property on the global object (idk where 'global' itself is defined, but if it's not throwing an error, I'm assuming you defined it above). While it's a property, the variable 'rooms' that you're using as a namespace isn't defined at the time you're calling it, so it doesn't know where to emit the message.
Even more answer: Also, if you're intending to add additional rooms to global.rooms, I think you might want to use a hashlist to store them, so that you can easily access them as global.rooms[room], as well as easily add new rooms to the list ie global.rooms[room.name] = room
I have tried several variations of trying to emit to all users connected to a particular /namespace, but have had no luck. I could be misunderstanding how sockets work.
But what I have right now is two browsers open on different pages. When a user connects to pageA, that user is now part of '/users' namespace. When a user connects to pageB, that user is now part of '/valets' namespace.
I have a .emit() on pageA that sends to server.js. I listen for it with .on(), and then try to run .emit() but to only the users in '/valets' namespace.
I am able to see in my terminal "listening for request valet" and the console.log(data) part.
I believe my problem is the usr_nsp.of('/valets').emit("incoming-request",{data:data}); portion. The other commented lines are what I have tried so far. They all give me an error: is not a function.
server.js
var app = require('http').createServer();
var io = require('socket.io')(app);
app.listen(3000, function(){
console.log('listening on port 3000');
});
var redis = require('socket.io/node_modules/redis');
// create custom namespace for Users
var room_number;
var usr_nsp = io.of('/users');
usr_nsp.on('connection', function(socket){
console.log('user has connected to /users namespace');
socket.on('request-valet', function(data){
console.log("listening for request valet");
console.log(data);
room_number = data.room_number;
socket.join(room_number);
// usr_nsp.broadcast.of('/valets').emit("incoming-request",{repark:data});
// usr_nsp.of('/valets').broadcast.emit("incoming-request",{repark:data});
// io.of('/valets').emit("incoming-request",{repark:data});
// socket.of('/valets').emit("incoming-request",{repark:data});
usr_nsp.of('/valets').emit("incoming-request",{repark:data});
});
});
var valet_nsp = io.of('/valets');
valet_nsp.on('connection', function(socket){
console.log('valet has connected to /valets namespace');
// var room_number;
socket.on('join-room', function(data){
// assign valet to room
room_number = data.room_number;
socket.join(room_number);
//valet_nsp.sockets.in(room_number).emit("request-accepted",{current_pos:current_pos})
});
socket.on('set-valet-starting-position', function(data){
//var valet_starting_pos = data.starting_position;
valet_nsp.sockets.in(room_number).emit('activate-directions-service', {repark:data});
})
socket.on('get-new-location', function(data){
// send the updated location only to User
// maybe use .broadcast??
valet_nsp.sockets.in(room_number).emit("update-valet-location", {current_pos:data});
});
});
pageB.html (sockets portion)
socket.on('incoming-request', function(data){
console.log("incoming request");
alert("incoming request");
// use data to display on html screen
});
The namespace handle you created is used to emit to users in that particular namespace. This should thus work:
var users = io.of('/users'),
valets = io.of('/valets');
users.on('connection', function(socket) {
socket.on('request-valet', function(data) {
valets.emit('incoming-request', { repark : data });
});
});
we have a problem regarding Websocket Communication with a Windows-Client.
As minimal setup we use the python3 autobahn websocket ping-pong example.
The server is from (taken from https://github.com/crossbario/autobahn-python/blob/master/examples/asyncio/websocket/echo/server.py). The only modification is that the server sends a message to the client when the connection is opened.
The client is also taken form the autobahn pingpong example but modified in two ways. It accepts connections from a remote server and it does not send a message to the server but it expects one.
This does work well on all browsers on my Linux Machine, but it does not work from a Windows-Client. But if I send a message from the client as soon as the connection is opened, then the client is also able to receive the messages.
Here is the pyhton3 server:
from autobahn.asyncio.websocket import WebSocketServerProtocol, \
WebSocketServerFactory
class MyServerProtocol(WebSocketServerProtocol):
def onConnect(self, req.uest):
print("Client connecting: {0}".format(request.peer))
def onOpen(self):
print("WebSocket connection open.")
self.sendMessage('server hello'.encode('utf8'))
def onMessage(self, payload, isBinary):
if isBinary:
print("Binary message received: {0} bytes".format(len(payload)))
else:
print("Text message received: {0}".format(payload.decode('utf8')))
# echo back message verbatim
self.sendMessage(payload, isBinary)
def onClose(self, wasClean, code, reason):
print("WebSocket connection closed: {0}".format(reason))
if __name__ == '__main__':
import asyncio
factory = WebSocketServerFactory(u"ws://0.0.0.0:9000", debug=False)
factory.protocol = MyServerProtocol
loop = asyncio.get_event_loop()
coro = loop.create_server(factory, '0.0.0.0', 9000)
server = loop.run_until_complete(coro)
try:
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
server.close()
loop.close()
Here is the Websocket Client:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
var socket = null;
var isopen = false;
window.onload = function() {
socket = new WebSocket("ws://" + location.hostname + ":9000");
socket.onopen = function() {
console.log("Connected!");
isopen = true;
//if I do this, then it works
//socket.send('hello from client'.encode('utf-8'))
}
socket.onmessage = function(e) {
console.log("Text message received: " + e.data);
}
socket.onclose = function(e) {
console.log("Connection closed.");
socket = null;
isopen = false;
}
};
</script>
</head>
<body>
</body>
</html>
Has anybody an idea what I am missing? I want to open a connection from server to client without sending a message from the client first.
I am using event-machine to make a chat room .
Here is the example code in their library(event-machine websocket)
require 'em-websocket'
puts "Server is listening!..."
EM.run {
#channel = EM::Channel.new
puts #channel.methods.sort
EM::WebSocket.run(:host => "0.0.0.0", :port => 8080) do |ws|
ws.onopen { |handshake|
puts "WebSocket connection open"
sid = #channel.subscribe {|msg|
ws.send msg
}
#channel.push "#{sid} connect!"
ws.onmessage {|msg|
#channel.push "<#{sid}>: #{msg}"
}
ws.onclose {
#channel.unsubscribe(sid)
}
}
end
}
How can I get the "sid" user information ? and keep connecting?
In client.html , it will become a new client after refreshing (F5)
I would like to make a one-to-one (send message between two clients) chat room,
but I don't know how to send message to specified user.(e.g.)sid1 send message to sid5.