nodejs--websocket

樊宏邈
2023-12-01

websocket

一、为什么需要websocket

因为 HTTP 协议有一个缺陷:通信只能先由客户端发起,然后服务器再作出响应,并不能由服务器主动向客户端推送消息。HTTP是半双工协议
WebSocket 协议最大的特点是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息。WebSocket全双工通讯的网络技术

二、双向通信

目前实现双向通信的方式有:轮询、长轮询、iframe、Websocket

2.1、轮询

客户端和服务器之间会一直进行连接,每隔一段时间客户就会主动发送请求给服务器端询问一次。这种方式连接数会很多,而且每次发送请求都会有Http的Header头,会很耗流量。

//服务端
let express = require('express');
let app = express();
app.use(express.static('www'));
app.get('/', (req,res) => {
    res.send(Date.now());
});
app.listen(3000);
<!--客户端-->
<div id="root"></div>
<script>
setInterval(function () {
	let xhr = new XMLHttpRequest();
	xhr.open('GET', 'http://localhost:3000/', true);
	xhr.onreadystatechange = function () {
		if (xhr.readyState == 4 && xhr.status == 200) {
			document.querySelector('#root').innerHTML = xhr.responseText;
		}
	}
	xhr.send();
}, 1000);

2.2、长轮询(服务器推荐)

长轮询是对轮询的改进版,客户端发送HTTP给服务器之后,看有没有新消息,如果没有新消息,就一直等待,当有新消息的时候,才会返回给客户端。在某种程度上减小了网络带宽问题。

//服务器
let express = require('express');
let app = express();
app.use(express.static('www'));
app.get('/', (req, res) => {
  let timer = setInterval(() => {
    let seconds = new Date().getSeconds()
    if (seconds == 50) {
      res.send(Date.now())
      clearInterval(timer)
    }
  }, 1000)
})
app.listen(3000);
<!--客户端-->
<div id="root"></div>
<script>
	(function send() {
		let xhr = new XMLHttpRequest();
		xhr.open('GET', 'http://localhost:3000', true);
		xhr.onreadystatechange = function () {
			if (xhr.readyState == 4 && xhr.status == 200) {
				document.querySelector('#root').innerHTML = xhr.responseText;
				send();
			}
		}
		xhr.send();
	})();
</script>

2.3、iframe

通过在HTML页面里嵌入一个隐藏的iframe,然后将这个iframe的src属性设为对一个长连接的请求,服务器端就能源源不断地往客户推送数据。

//服务器
const express = require('express');
const app = express();
app.use(express.static('www'));
app.get('/',  (req, res) => {
	setInterval( () => {
		res.write(`
			<script type="text/javascript">
			parent.document.getElementById('root').innerHTML = "${Date.now()}";
			</script>
			`);
	}, 1000);
});
app.listen(3000);
<!--客户端-->
<div id="root"></div>
<iframe src="/" style=" display:none" />

2.4、EventSource

EventSource 是服务器推送的一个网络事件接口。一个EventSource实例会对HTTP服务开启一个持久化的连接,以text/event-stream 格式发送事件, 会一直保持开启直到被要求关闭。
与 WebSockets,不同的是,服务端推送是单向的。

//服务器
/*服务器返回一定要按照必须的格式
//头信息
res.setHeader("content-type","text/event-stream")
//响应信息
res.send("event:类型\ndata:数据\n\n")*/
//----------------------
app.get('/ajax', (req, res) => {
  res.setHeader("content-type", "text/event-stream")
  setInterval(() => {
    let dt = Date.now()
    if (new Date().getSeconds() % 2 == 0) {
      res.write("event:message\ndata:" + dt + "\n\n")
    }
  }, 1000);
})

//客户端
const es = new EventSource(url);
//接受服务器发送过来的数据
es.onmessage = ev=> ev.data

2.5、websocket

HTML5开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议,它基于TCP传输协议,并复用HTTP的握手通道。
websocket 优势在于,支持双向通信,实时性更强。较少的控制开销。连接创建后,客户端、服务端进行数据交换时,协议控制的数据包头部较小。

安装:npm i -S ws

# 服务器
const express = require('express')
const http = require('http')
const Ws = require('ws').Server
const app = express()
const server = http.createServer(app)
server.listen(3000)
app.use(express.static('www'))

let wsServer = new Ws({ server })
wsServer.on('connection', socket => {
  console.log('连接成功');
  // 监听客户端发过来的消息
  socket.on('message', msg => {
    console.log('客户端发送过来的消息:' + msg);
    // 服务器给客户端发送消息
    socket.send('服务器说:你好客户端')
  })
});
<!--客户端-->
<script>
let ws = new WebSocket('ws://127.0.0.1:3000')
ws.onopen = () => {
  ws.send('hello server')
};
ws.onmessage = ev => {
  console.log(ev.data)
}
</script>

2.6、socket.io

Socket.IO是一个WebSocket库,包括了客户端的js和服务器端的nodejs,它会自动根据浏览器从WebSocket、AJAX长轮询、Iframe流等等各种方式中选择最佳的方式来实现网络实时应用,非常方便和人性化,而且支持的浏览器最低达IE5.5。

安装:npm i -S socket.io

//服务端
const express = require('express')
const http = require('http')
const app = express()
const server = http.createServer(app)
const io = require('socket.io')(server)
server.listen(3000)
app.use(express.static('www'))

// 监听客户端的连接事件
io.on('connection', socket => {
  socket.on('message', msg => {
    console.log('接受到客户端发过来的消息:' + msg);
    socket.send('服务器说:' + msg)
  })
})
<!--客户端-->
<script src="/socket/socket.io.js"></script>
<script>
let socket = io.connect('http://localhost:3000/')
socket.on('connect', () => {
  console.log('连接成功')
  socket.emit('message','hello')
});

socket.on('message', msg => {
  console.log(msg)
});

// 断开连接
socket.on('disconnect', () => {
  console.log('断开连接')
});
</script>

// 全局广播
io.emit('message','全局广播');
// 向除了自己外的所有人广播
socket.broadcast.emit('message', msg);
 类似资料: