Websocket 介绍
Websocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许
服务器主动发送信息给客户端。
Websocket是—种持久协议,http是非持久协议
现在很多网站都有实时推送的需求,比如聊天,客服咨询等
早期没有websocket时,通过ajax轮询,由于http请求,服务器无法给浏览器主动发送数据,因此需要浏览器定时的给服务器发送请求(比
如1s一次),服务器把最新的数据响应给浏览器。这种模式的缺点就是浪费性能和资源。
websocket是一种网络协议,允许客户端和服务端全双工的进行网络通讯,服务器可以给客户端发消息,客户端也可以给服务器发消息。
Websocket基本使用
在HTML5中,浏览器已经实现了websocket的API,直接使用即可。
WebSocket-MDN
创建Websocket对象
1 2 3 4
|
var Socket = new Websocket(url, [protocol] );
|
WebSocket事件
事件 | 事件处理程序 | 描述 |
---|
open | Socket.onopen | 连接建立时触发 |
message | Socket.onmessage | 客户端接收服务端数据时触发 |
error | Socket.onerror | 通信发生错误时触发 |
close | Socket.onclose | 连接关闭时触发 |
Websocket方法
方法 | 描述 |
---|
Socket.send() | 使用连接发送数据 |
Socket.close() | 关闭连接 |
示例
一个简单的示例
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebSocket学习</title> </head> <body> <input type="text"> <button>点我发送</button> <div style="width: 100px;height: 100px;border:1px solid red"></div> <script> const input = document.querySelector("input") const button = document.querySelector("button") const div = document.querySelector("div")
const socket = new WebSocket("ws://echo.websocket.org") socket.addEventListener("open",()=>{ div.innerText = "连接成功" })
button.addEventListener("click",()=>{ var value = input.value socket.send(value) })
socket.addEventListener("message",(e)=>{ console.log(e.data); div.innerHTML = e.data })
socket.addEventListener("close",()=>{ div.innerHTML = "断开服务" }) </script> </body> </html>
|
使用nodejs开发websocket服务
我们刚刚使用了官网提供的echo服务,I接下来我们自己通过nodejs实现一个简单的websocket服务。
使用nodejs开发websocket需要依赖一个第三方包。Nodejs Websocket
项目搭建
安装 nodejs-websocket
开发服务程序
使用 app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| const ws = require("nodejs-websocket")
const port = 3000
var server = ws.createServer((conn) => { console.log("有用户连接了。。。");
conn.on("text", (data) => { console.log("接收到数据 " + data); conn.send(data) })
conn.on("close", () => { console.log("用户断开连接"); }) conn.on("error", () => { console.log("发生错误"); }) })
server.listen(port, () => { console.log("连接端口" + port); })
|
编辑 index.html
1 2 3 4
|
const socket = new WebSocket("ws://localhost:3000")
|
示例
一个简单的聊天室
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebSocket学习</title> </head> <body> <input type="text"> <button>点我发送</button> <div></div>
<script> const input = document.querySelector("input") const button = document.querySelector("button") const div = document.querySelector("div") const TYPE_ENTER = 0 const TYPE_LEAVE = 1 const TYPE_MSG = 2
const socket = new WebSocket("ws://localhost:3000")
socket.addEventListener("open", () => { div.innerText = "连接成功" })
button.addEventListener("click", () => { var value = input.value socket.send(value) input.value = "" })
socket.addEventListener("message", (e) => { var data = JSON.parse(e.data) var dv = document.createElement("div") dv.innerText = data.msg + "--------" + data.time if (data.type == TYPE_ENTER) { dv.style.color = "green" } else if (data.type == TYPE_LEAVE) { dv.style.color = "red" } else { dv.style.color = "blue" } div.appendChild(dv) })
socket.addEventListener("close", () => { div.innerHTML = "断开服务" }) </script> </body> </html>
|
app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| const ws = require("nodejs-websocket");
const TYPE_ENTER = 0 const TYPE_LEAVE = 1 const TYPE_MSG = 2
const port = 3000
let count = 0
var server = ws.createServer((conn) => { console.log("有用户连接了。。。"); count++ conn.userName = `用户${count}`
bordercast({ type: TYPE_ENTER, msg: `${conn.userName}进入了聊天室`, time: new Date().toLocaleTimeString() })
conn.on("text", (data) => { bordercast({ type: TYPE_MSG, msg: `${conn.userName}: ` +data, time: new Date().toLocaleTimeString() }) })
conn.on("close", () => { console.log("用户断开连接"); count-- bordercast({ type: TYPE_LEAVE, msg: `${conn.userName}离开了聊天室`, time: new Date().toLocaleTimeString() }) }) conn.on("error", () => { console.log("发生错误"); }) })
var bordercast = (msg)=>{ server.connections.forEach(item=>{ item.send(JSON.stringify(msg)) }) }
server.listen(port, () => { console.log("连接端口" + port); })
|
如果使用原生的websocket进行开发,会比较麻烦,比如支持的事件太少,发送的数据只能是字符串格式的,提供的api也很少,类似于广播这种方法都没有,需要自己封装。
socket.io基本使用
socket.io
安装 socket.io
socket.emit
: 表示触发某个事件 如果需要给浏览器发数据.需要触发浏览器注册某个事件
socket.on
: 表示的注册某个事件 如果需要获取浏览器的数据,需要注册一个事件,等待浏览器触发
服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| const http = require('http') const fs = require('fs') const app = http.createServer() app.on("request", (req, res) => { fs.readFile(__dirname + '/index.html', function (err, data) { if (err) { res.writeHead(500) return res.end('Error loading index.html') } res.writeHead(200) res.end(data) })
})
app.listen(3000, () => { console.log("服务器启动成功"); }); const io = require("socket.io")(app)
io.on("connection",socket=>{ console.log("新用户连接"); socket.on("hehe",data=>{ console.log(data); socket.emit("send",data) }) })
|
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script src="/socket.io/socket.io.js"></script> <script> const socket = io("http://localhost:3000"); socket.emit("hehe",{name:"zykj",age:18}) socket.on('send',data=>{ console.log(data); }) </script> </body> </html>
|
基于socket.io开发完整的聊天室
部分内容讲解
FileReader
MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
在上面的项目中就是在本地上传图片时用到了 FileReader 将图片转换成 Base64 编码
1 2
| FileReader.readAsDataURL()
|
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
$('#file').on("change", function () { var file = this.files[0] var fr = new FileReader() fr.readAsDataURL(file) fr.onload = function () { socket.emit("sendImage", { username: username, avatar: avatar, img: fr.result }) } })
|
参考:H5 FileReader对象
scrollIntoView
项目中、当我们像每次发完消息时、希望定位到最新的一条、也就是滚动到最底部、我们就需要使用 scrollIntoView
示例:
1 2 3 4
| function scroll() { $('.box-bd').childnet(':last').get(0).scrollIntoView(false) }
|
参考 scrollIntoView()的用法