NodeJS
双向通讯
Socket
代码示例:
// index.js
const express = require('express')
const app = express()
app.use(express.static('./public'))
app.get('/', (req, res) => {
res.send({
ok: 1
})
})
app.listen(3000)
const WebSocket = require('ws')
const WebSocketServer = WebSocket.WebSocketServer
const wss = new WebSocketServer({port: 8080});
wss.on('connection', function connection(ws) {
ws.on('message', function message(data) { // 监听来自客户端发送的数据
console.log('received:%s', data);
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) { // 判断客户端是否保持连接状态(排除掉自己)
client.send(data, {binary: false}); // 广播信息
}
})
});
ws.send('HQSY'); // 返回给客户端的数据
});
<!-- public/chat.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Chat</title>
</head>
<body>
<script>
const ws = new WebSocket('ws://localhost:8080')
ws.onopen = ()=> {
console.log('连接成功!')
}
ws.onmessage = (msgObj) => {
console.log(msgObj.data)
}
ws.onerror = () => {
console.log('error')
}
</script>
</body>
</html>
鉴权
代码示例:
// routes/chat.js
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('chat');
});
module.exports = router;
// bin/websocketServer.js
const WebSocket = require('ws')
const WebSocketServer = WebSocket.WebSocketServer
const wss = new WebSocketServer({port: 8080});
const JWT = require('../util/JWT')
const WebSocketType = {
Error: 0,
GroupList: 1,
GroupChat: 2,
SingleChat: 3
}
function createMessage(type, user, data) {
return JSON.stringify({
type,
user,
data
})
}
wss.on('connection', function connection(ws, req) {
// console.log(req.url)
const myURL = new URL(req.url, 'http://127.0.0.1:3000')
// console.log(myURL.searchParams.get('token'))
const payload = JWT.verify(myURL.searchParams.get('token'))
if (payload) {
ws.send(createMessage(WebSocketType.GroupChat, null, '欢迎'));
} else {
ws.send(createMessage(WebSocketType.Error, null, 'token无效'))
}
ws.on('message', function message(data) {
console.log('received:%s', data);
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(data, {binary: false});
}
})
});
});
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('myapp:server');
var http = require('http');
// socket服务器
require('./websocketServer')
require('../config/db.config')
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var loginRouter = require('./routes/login');
var uploadRouter = require('./routes/upload')
var chatRouter = require('./routes/chat')
var session = require('express-session')
var MongoStore = require('connect-mongo')
var app = express();
var JWT = require('./util/JWT')
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({extended: false}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
name: 'cookieName',
secret: 'abcdefg',
cookie: {
maxAge: 1000 * 60 * 60,
secure: false
},
resave: true,
saveUninitialized: true,
store: MongoStore.create({
mongoUrl: 'mongodb://127.0.0.1:27017/test',
ttl: 1000 * 60 * 10
})
}))
app.use((req, res, next) => {
if (req.url.includes('login')) {
next()
return
}
const token = req.headers['authorization']?.split(' ')[1]
if (token) {
const payload = JWT.verify(token)
if (payload) {
const newToken = JWT.generate({
_id: payload._id,
username: payload.username
}, '1h')
res.header('Authorization', newToken)
next()
} else {
res.status(401).send({errCode: -1, errInfo: 'token失效了'})
}
} else {
next()
}
})
app.use('/', indexRouter);
app.use('/api', usersRouter);
app.use('/login', loginRouter);
app.use('/upload', uploadRouter);
app.use('/chat', chatRouter);
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
// bin/websocketServer.js
const WebSocket = require('ws')
const WebSocketServer = WebSocket.WebSocketServer
const wss = new WebSocketServer({port: 8080});
const JWT = require('../util/JWT')
const WebSocketType = {
Error: 0,
GroupList: 1,
GroupChat: 2,
SingleChat: 3
}
function createMessage(type, user, data) {
return JSON.stringify({
type,
user,
data
})
}
function sendAll() {
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(createMessage(WebSocketType.GroupList, null, JSON.stringify(Array.from(wss.clients).map(item =>
item.user
))))
}
})
}
wss.on('connection', function connection(ws, req) {
const myURL = new URL(req.url, 'http://127.0.0.1:3000')
const payload = JWT.verify(myURL.searchParams.get('token'))
if (payload) {
ws.send(createMessage(WebSocketType.GroupChat, null, '欢迎'));
ws.user = payload
// 必须群发
sendAll()
} else {
ws.send(createMessage(WebSocketType.Error, null, 'token无效'))
}
ws.on('message', function message(data) {
// console.log('received: %s', data);
// wss.clients.forEach(function each(client) {
// if (client !== ws && client.readyState === WebSocket.OPEN) {
// client.send(data, {binary: false});
// }
// })
const msgObj = JSON.parse(data)
switch (msgObj.type) {
case WebSocketType.GroupList:
console.log(Array.from(wss.clients).map(item => {
item.user
}))
ws.send(createMessage(WebSocketType.GroupList, null, JSON.stringify(Array.from(wss.clients).map(item => {
item.user
}))))
break;
case WebSocketType.GroupChat:
// console.log(msgObj.data)
console.log('received: %s', data);
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(createMessage(WebSocketType.GroupChat, ws.user, msgObj.data), {binary: false});
}
})
break;
case WebSocketType.SingleChat:
wss.clients.forEach(function each(client) {
if (client.user.username === msgObj.to && client.readyState === WebSocket.OPEN) {
client.send(createMessage(WebSocketType.SingleChat, ws.user, msgObj.data), {binary: false});
}
})
break;
}
});
ws.on('close', () => { // 当有客户端离线时
wss.clients.delete(ws.user)
sendAll()
})
});
<!-- views/chat.ejs -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Chat</title>
</head>
<body>
<script>
const WebSocketType = {
Error: 0,
GroupList: 1,
GroupChat: 2,
SingleChat: 3
}
const ws = new WebSocket(`ws://localhost:8080?token=${localStorage.getItem('token')}`)
ws.onopen = () => {
console.log('open')
}
ws.onmessage = (msgObj) => {
console.log(msgObj.data)
msgObj = JSON.parse(msgObj.data)
switch (msgObj.type) {
case WebSocketType.Error:
localStorage.removeItem('token')
location.href = '/login'
break;
case WebSocketType.GroupChat:
console.log(msgObj)
break;
}
}
</script>
</body>
</html>
<!-- views/chat.ejs -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Chat</title>
</head>
<body>
<input type="text" id="text"><button id="send">send</button>
<select id="select">
</select>
<script>
function createMessage(type, data, to = null) {
return JSON.stringify({
type,
data,
to
})
}
const select = document.querySelector('#select')
const send = document.querySelector('#send')
const text = document.querySelector('#text')
const WebSocketType = {
Error: 0,
GroupList: 1,
GroupChat: 2,
SingleChat: 3
}
const ws = new WebSocket(`ws://localhost:8080?token=${localStorage.getItem('token')}`)
ws.onopen = () => {
console.log('open')
}
ws.onmessage = (msgObj) => {
console.log(msgObj.data)
msgObj = JSON.parse(msgObj.data)
switch (msgObj.type) {
case WebSocketType.Error:
localStorage.removeItem('token')
location.href = '/login'
break;
case WebSocketType.GroupList:
console.log(JSON.parse(msgObj.data))
const onlineList = JSON.parse(msgObj.data)
select.innerHTML = ''
select.innerHTML = `<option value="all">all</option>` + onlineList.map(item => `
<option value="${item.username}">${item.username}</option>
`).join('')
break;
case WebSocketType.GroupChat:
const title = msgObj.user?msgObj.user.username:'广播'
console.log(title + ':' + msgObj.data)
break;
case WebSocketType.SingleChat:
console.log(msgObj.user.username + ':' + msgObj.data)
break;
}
}
send.onclick = () => {
if(select.value === 'all') {
ws.send(createMessage(WebSocketType.GroupChat, text.value))
} else {
ws.send(createMessage(WebSocketType.SingleChat, text.value, select.value))
}
}
</script>
</body>
</html>
socket.io
代码示例:
// bin/websocketServer.js
const io = require("socket.io");
const JWT = require('../util/JWT')
function start(server) {
const io = require('socket.io')(server)
io.on('connection', (socket) => {
// console.log('aaa', socket.handshake.query.token)
const payload = JWT.verify(socket.handshake.query.token)
if (payload) {
socket.user = payload
socket.emit(WebSocketType.GroupChat, createMessage(null, '欢迎'))
sendAll(io)
} else {
socket.emit(WebSocketType.Error, createMessage(null, 'token失效'))
}
socket.on(WebSocketType.GroupList, () => {
// console.log(Array.from(io.sockets.sockets).map(item => item[1].user)) // 不同版本使用方法各不相同
socket.emit()
})
socket.on(WebSocketType.GroupChat, (msg) => {
// console.log(msg)
io.sockets.emit( // 群发(包括自己)
WebSocketType.GroupChat,
createMessage(socket.user, msg.data)
)
// socket.broadcast.emit( // 群发(不包括自己)
// WebSocketType.GroupChat,
// createMessage(socket.user, msg.data)
// )
})
socket.on(WebSocketType.SingleChat, (msgObj) => {
Array.from(io.sockets.sockets).forEach(item => {
if (item[1].user.username === msgObj.to) {
item[1].emit(
WebSocketType.SingleChat,
createMessage(item.user, msgObj.data)
)
}
})
})
io.on('disconnect', () => { // 用户离线时
sendAll(io)
})
})
}
const WebSocketType = {
Error: 0,
GroupList: 1,
GroupChat: 2,
SingleChat: 3
}
function createMessage(user, data) {
return {
user,
data
}
}
function sendAll(io) {
io.sockets.emit(
WebSocketType.GroupList,
createMessage(null, Array.from(io.sockets.sockets)
.map(item => item[1].user).filter(item => item) // 防止undefined
)
)
}
module.exports = start
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('myapp:server');
var http = require('http');
var websocketServer = require('./websocketServer')
require('../config/db.config')
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
// 启动websocket
websocketServer(server)
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
<!-- views/chat.ejs -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Chat</title>
<script src="/javascripts/socket.io.min.js"></script>
</head>
<body>
<input type="text" id="text"><button id="send">send</button>
<select id="select">
</select>
<script>
function createMessage(data, to = null) {
return {
type,
data,
to
}
}
const select = document.querySelector('#select')
const send = document.querySelector('#send')
const text = document.querySelector('#text')
const WebSocketType = {
Error: 0,
GroupList: 1,
GroupChat: 2,
SingleChat: 3
}
const socket = io(`ws://localhost:3000?token=${localStorage.getItem('token')}`) // 未填实参默认连接localhost
socket.on(WebSocketType.GroupChat, (msg) => {
// console.log(msg)
const title = msg.user ? msg.user.username: '广播'
console.log(title + ':' + msg.data)
})
socket.on(WebSocketType.SingleChat, (msg) => {
// console.log(msg)
const title = msg.user ? msg.user.username: '广播'
console.log(title + ':' + msg.data)
})
socket.on(WebSocketType.Error, (msg) => {
localStorage.removeItem('token')
location.href = '/login'
})
socket.on(WebSocketType.GroupList, (msgObj) => {
// console.log(msg)
const onlineList = msgObj.data
select.innerHTML = ''
select.innerHTML = `<option value="all">all</option>` + onlineList.map(item => `
<option value="${item.username}">${item.username}</option>
`).join('')
})
send.onclick = () => {
if (select.value === 'all') {
socket.emit(WebSocketType.GroupList, createMessage(text.value))
} else {
socket.emit(WebSocketType.GroupList, createMessage(text.value, select.value))
}
}
</script>
</body>
</html>