文章目录
源码
搭建服务器
搭建一个返回网页的端口
const express = require('express')
const app = express()
const path = require('path')
const fs = require('fs')
app.use(express.static(path.join(__dirname, 'public')))
//监听端口
app.listen(8080,()=>{
console.log('HTTP Server is running on: http://localhost:%s', 8080)
})
app.get('*',(req,res)=>{
let urlPath = req.path
if(urlPath == '/'||urlPath=='/index.html'||urlPath=='/index'||urlPath=='') {
console.log('返回index.html')
res.end(fs.readFileSync(`./index.html`))
}
})
搭建一个监听websocket的端口
安装插件
npm install ws
var WebSocketServer = require('ws').Server,
wss = new WebSocketServer({ port: 8181 });
wss.on('connection', function (ws) {
console.log('client connected');
ws.on('message', function (message) {
console.log(message);
});
});
创建一个html文件
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<head><title>websoket</title></head>
<body>
websoket
<input type="text" id='message'>
<button >发送</button>
</body>
<script>
var ws = new WebSocket("ws://localhost:8181");
ws.onopen = function (e) {
console.log('Connection to server opened');
}
document.querySelector('button').addEventListener('click',sendMessage)
function sendMessage() {
console.log(document.querySelector('#message').value)
// 发送
ws.send(document.querySelector('#message').value)
}
</script>
</html>
运行结果
数据成功发送到服务器。
websocket
参考资料阮一峰websocket
这里就摘要的讲下websocket。
HTTP请求一般只能用户发起,如果服务器有信息想要主动发送给用户的时候,单靠HTTP是不行的。
这就引出了维持通信的必要性,客户端和服务器建立了一个通信通道(websocket),双方都可以主动的发送数据给对方。
应用场景:股票市场、聊天软件
使用到的网络协议
- 和
HTTP
一样,同样是经过TCP
连接建立起来的面向连接。 - 握手阶段使用的和http协议是一样
- 数据格式比较轻量,性能开销小,通信高效。
- 可以发送文本,也可以发送
二进制数据。
- 没有同源限制,客户端可以与任意服务器通信。实现跨域
- 协议标识符是
ws
(如果加密,则为wss
),服务器网址就是 URL。有点类似HTTP
和HTTPS
的区别
服务器如何写一个websocket监听端口
从上面的一个例子非常清晰的明白,使用了ws
框架可以快速地搭建一个websocket的服务器。
var WebSocketServer = require('ws').Server,// 引入框架
wss = new WebSocketServer({ port: 8181 });// 设置端口号
wss.on('connection', function (ws) { // 注册连接事件
console.log('client connected');// 成功建立之后执行
ws.on('message', function (message) {// 注册监听事件
console.log(message);// 当收到客户端发送的信息执行
});
});
这里有官方文档
利用websocket实现一个聊天室
聊天室需要一个用户表,还有聊天框。
也需要一些按钮来控制发送和注册用户等
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<head><title>websoket</title></head>
<style>
input{
outline-style: none ;
border: 1px solid #ccc;
border-radius: 3px;
padding: 13px 14px;
width: 620px;
font-size: 14px;
font-weight: 700;
font-family: "Microsoft soft";
margin: 20px;
}
input:focus{
border-color: #66afe9;
outline: 0;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)
}
span{
font-size: 24px;
font-weight: 700;
font-family: "Microsoft soft";
}
button {
width: 120px;
padding:8px;
margin: 10px;
background-color: #428bca;
border-color: #357ebd;
color: #fff;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px; /* future proofing */
-khtml-border-radius: 10px; /* for old Konqueror browsers */
text-align: center;
vertical-align: middle;
border: 1px solid transparent;
font-weight: 900;
font-size:100%
}
</style>
<body>
<span>username</span>
<input type="text" id='username' placeholder="请输入名称">
<button id='confirm'>确认</button>
<br>
<div id='talk' style='margin:20px;width:600px;height:400px;border:1px solid black;'>
</div>
<br>
<input type="text" id='message'><br>
<button id='sendBtn'>发送</button><br>
<button id='closeWs'>退出聊天室</button>
<div id='users' style="position: fixed;right:50px;top:20px;width:200px;height:200px;background: #00CCFF;">
<li>123</li>
</div>
</body>
</html>
逻辑处理
建立连接
- 客户端
// 建立一个WS连接
var ws = new WebSocket("ws://localhost:8181");
// 打开连接
ws.onopen = function (e) {
console.log('Connection to server opened');
}
// 关闭连接 删除用户
ws.onclose = function (e) {
console.log('Connection to server closed');
}
- 服务端
var WebSocketServer = require('ws').Server
var wss = new WebSocketServer({ port: 8181 });
wss.on('connection', function (ws) {
console.log('client connected');
ws.on('message', function (message) {
console.log(JSON.parse(message));
});
信息约定
由于http是无状态的,无法自动判断信息的类型,所以我们这里约定一个信息约定。
- 每次客户端发送信息都会携带一个状态码:
- 1代表注册信息,
- 2代表修改用户信息,
- 3代表发送聊天信息,
- 4代表注销信息。
用户端同理,都会携带一个状态码提供用户进行判断信息类型。
添加注册用户事件
- 客户端
// 接受服务器信息 ws.onmessage= function(message) { console.log('服务器返回的信息') let state = message.data.slice(0,1) let data = JSON.parse(message.data.slice(1)) if(state==1) {// 表示用户信息更新 updateUserName(data) } console.log(state,data) } // 更新用户表 function updateUserName(data) { var parent = document.querySelector('#users') parent.innerHTML ='' data.forEach((item)=>{ var list = document.createElement('li') list.innerHTML = item.name parent.appendChild(list) }) } // 添加用户 document.querySelector('#confirm').addEventListener('click',()=>{ username = document.querySelector('#username').value if(flag) return // 修改信息,并且发送信息到服务器 let mess = { id:userId, name:username, state:1 } flag = true ws.send(JSON.stringify(mess)) })
- 服务端
// 保存用户id
var users = []
var wss = new WebSocketServer({ port: 8181 });
wss.on('connection', function (ws) {
console.log('client connected');
ws.on('message', function (message) {
message = JSON.parse(message)
// 判断状态
if(message.state==1) {
// 注册用户时间
users.push({
id:message.id,
name:message.name
})
// 将更新的用户信息发送给所有用户
updateAllUsername()
}
});
});
function updateAllUsername () {
for(let item of wss.clients){
console.log(item.domain)
item.send(`1${users}`)
}
}
我们知道,wss
是一个websocket的server实例,注册了一个监听端口,当一个用户连接这个端口的时候,就会触发里面的监听事件。
connection
表示连接建立完成,后面回调函数中的参数则是请求头
nodejs的ws框架
有一个数据保存着所有的请求头,它就是wss
server实例的clients
,我们可以遍历它,就可以完成当一个用户进入聊天室时候,让所有用户更新信息。
注销用户信息
当用户退出聊天室,也需要更新用户信息表。
- 客户端
// 关闭连接事件
document.querySelector('#closeWs').addEventListener('click',()=>{
// 取消用户信息
// 修改信息,并且发送信息到服务器
let mess = {
id:userId,
state:4
}
ws.close(3000,JSON.stringify(mess);
})
- 服务器端
wss.on('connection', function (ws) {
console.log('client connected');
/***/
// 给每一个用户注册一个关闭事件
ws.on('close',(state,msg)=>{
// 通过id删除用户表
let id = JSON.parse(msg).id
users= users.filter((item)=>{
return item.id!==id
})
// 给所有还在用的用户更新列表
updateAllUsername()
})
});
我们来测试下
用户1:
用户2:
可以看到用户2 成功添加到用户列表,并且切会到用户1发现用户列表也更新了。
- 测试退出情况
用户1退出,查看用户2情况。
聊天内容
有了前面的基础,这部分已经非常简单了。
- 客户端
// 发送聊天信息
document.querySelector('#sendBtn').addEventListener('click',() => {// 发送
if(!flag){
alert('请注册用户')
return
}
if(document.querySelector('#message').value!=''&&flag) {
// 拼接数据
let mess = {
id:userId,
name:username,
state:3,
time:new Date().toLocaleDateString(),
value:document.querySelector('#message').value
}
ws.send(JSON.stringify(mess))
document.querySelector('#message').value=''
}
})
// 接受聊天信息
ws.onmessage= function(message) {
console.log('服务器返回的信息',message.data)
let state = message.data.slice(0,1)
let data = JSON.parse(message.data.slice(1))
if(state==1||state==4) {
updateUserName(data)
}else if (state == 3) {
updateTalkMessage(data)
}
}
// 更新聊天框信息
function updateTalkMessage(data) {
console.log(data)
var parent = document.querySelector('#talk')
var list = document.createElement('li')
list.innerHTML = `${data.name} 说: ${data.value} -----${data.time}`
parent.appendChild(list)
}
- 服务器
wss.on('connection', function (ws) {
console.log('client connected');
ws.on('message', function (message) {
message = JSON.parse(message)
console.log(message)
// 判断状态
if(message.state==1) {
// 注册用户时间
users.push({
id:message.id,
name:message.name
})
}else if(message.state==3) {
talkMessage = {
name:message.name,
time:message.time,
value: message.value
}
}
// 将更新的信息发送给所有用户
updateAllUsername(message.state)
});
})
function updateAllUsername (state) {
let result = ''
if(state== 1||state==4) {
result=users
} else if (state == 3 ) {
result = talkMessage
}
for(let item of wss.clients){
item.send(`${state}`+JSON.stringify(result))
}
}
查看演示
- 还剩下一个更新用户信息,相信看到现在应该可以独立完成了,和前面的功能差不多,就是获取数据、处理数据和返回数据。
总结
有了websocket这个接口方便了用户端和服务器持续的交流
,比起使用http
轮询地访问大大提高了性能。
websocket这个api使用起来非常简单,相信你如果能够自己搭一个聊天室,一定就掌握了七七八八了。