HTTP keep-alive参考博文
socketio跨域 socketio跨域问题自己去了解
WebSocket
websocket
可以实现客户端与服务端之间的数据实时通信。(长连接)
网络通信过程中的长连接与短连接
短连接: 客户端发起连接请求,请求建立连接,服务端接受请求,完成连接的创建,而后客户端通过该连接发送数据,服务端处理请求,返回响应,最后连接断开。这种通讯模式称为短连接。
特点:这种通讯模式将会及时的释放服务端资源,所以可以为更多的客户端提供服务。但是也有缺点:无状态。(每个请求都是独立的,无法保存客户端状态)
**长连接:**客户端发起连接请求,请求建立连接,服务端接受请求,完成连接的创建,而后客户端通过该连接发送数据,服务端处理请求,返回响应;服务端也可以主动的向客户端发送数据,客户端接收。这个过程中持续连接不断开。这种通讯模式称为长连接。
特点:支持客户端与服务端之间的实时通信。服务端可以随时随地向客户端发消息。缺点就是浪费服务端资源。
什么是websocket
?
websocket
是一种在单TCP
连接上进行的支持全双工通信(通信的过程中允许两个方向上同时完成数据传输)的一种协议。是一款长连接协议。
如何在网页中建立websocket
连接?
服务端接收接收该连接?
客户端与服务端如何通过该websocket
连接进行通信呢?
socket.io
https://socketio.bootcss.com/
socket.io
是一个为浏览器与服务端之间提供实时的、双向的、基于事件通信的网络通信库。底层封装了websocket
协议。提供了一些简单的API
接口,方便的建立连接,收发消息,抹平了一些技术细节与浏览器兼容性问题。
建立连接的伪代码:
socket
(套接字)就是一个javascript
对象,对象的本质就是封装了属性与方法。socket
中就封装了用于与对方通信时所需要的资源。
// 客户端 导入socket.io后
let socket = io('ws://localhost:3000/');
// 服务端 安装socket.io后
let socketio = require('socket.io');
socketio.on('connection', (socket)=>{
连接建立成功后执行
});
建立websocket
连接
服务端代码:
- 新建项目(
socketserver
文件夹)安装
express
。安装socket.io
。cd socketserver npm init // 一路回车 生成package.json npm install --save express // 安装express npm install --save socket.io // 安装socket.io
- 基于
socket.io
提供的API
,接收客户端的连接请求,建立websocket
连接。// index.js 服务端核心js文件 const express = require('express'); const server = express(); // 调用server.get() server.post() 注册路由 接收请求 // 配置websocket // 获取express底层所使用的http模块 const http = require('http').createServer(server); // websocket需要把通信相关路由注册到该底层http模块 const socketio = require('socket.io')(http); socketio.on('connection', (socket)=>{ console.log('有客户端进来了:'+socket.id) }); // 设置静态资源托管目录 public server.use(express.static('public')); // 启动服务 不能再使用server.listen 而应该调用http.listen // 因为socketio将通信相关路由注册到了http模块,而非express // 所以http才可以接收到websocket请求,express则只用于处理http服务 http.listen(3000, function(){ console.log('server is running...') });
客户端代码:
为了避免跨域问题,设置静态资源托管目录public
,把网页写在public
中即可。
<script src="socket.io.js"></script>
<script>
btnConn.addEventListener('click', ()=>{
let socket = io('ws://localhost:3000/');
console.log(socket);
});
</script>
客户端与服务端之间进行通信
客户端发消息给服务端
客户端:
let socket = io('ws://localhost:3000/');
// 发消息 (emit 翻译为触发)
socket.emit('textmsg', '你瞅啥?!');
服务端:
socketio.on('connection', (socket)=>{
// 监听客户端发过来的消息
socket.on('textmsg', (data)=>{
console.log(data)
});
});
服务端发消息给客户端
服务端代码:
socket.on('textmsg', (data)=>{
console.log(data)
// 回复
socket.emit("textmsg", '瞅你咋地~~');
});
客户端代码:
let socket = io('ws://localhost:3000/');
socket.on('textmsg', (data)=>{
alert('xxxx');
});
服务端向所有客户端广播消息
socketio.on('connection', (socket)=>{
socket.emit(); // 针对当前客户端发消息
socketio.emit('textmsg', '兄弟们!'); // 向所有客户端都发消息
});
例子:文件夹目录为
客户端:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>websockt示例</h2>
<button id="btnConn">建立连接</button>
<button id="btnSend">向服务器端发消息</button>
<button id="btnSend3">向服务器端发消息:请服务器端准备群发</button>
<script src="socket.io.js"></script>
<script>
let socket;
// 建立连接
btnConn.addEventListener("click", ()=>{
// socket = io('ws//localhost:3000/');
socket = io('ws://localhost:3000/');
console.log(socket);
// 因为接收信息是在建立完连接之后就监听,所以接收服务器的信息是在连接之后就开启接收
socket.on("textmsg", (data)=>{
window.alert(data);
});
});
// 客户端主动发消息
btnSend.addEventListener("click", ()=>{
// textmsg 是代表在服务器端接收时要用该textmsg去接收,只跟此次发送和接收相关,
// 即每次接收和发送的要匹配即可
socket.emit("textmsg", "向服务器发送信息,单发");
});
btnSend3.addEventListener("click", ()=>{
// textmsg 是代表在服务器端接收时要用该textmsg去接收,只跟此次发送和接收相关,
// 即每次接收和发送的要匹配即可
socket.emit("textmsg", "向服务器端发消息:请服务器端准备群发");
});
</script>
</body>
</html>
服务端:
//index.js 服务端核心js文件
const express = require('express');
const server = express();
// 调用server.get() serve.post()注册路由,接收请求
// const socketio = require("socket.io");
// 建立服务端和socketio连接
// 获取express底层所使用的http模块,因为要通过express底层的http去发送请求,建立连接是用http,所以要获得http
const http = require('http').createServer(server);
// 要将express底层的http和socketio建立联系
const socketio = require('socket.io')(http);
// 监听连接事件,连接就触发回调函数
socketio.on("connection", (socket)=>{
console.log(socket.id);
// 1.服务器端主动发送消息
// 1.1针对当前客户端发送消息
// socket.emit("textmsg", "连接建立成功");
// 1.2向所有客户端发送消息,通过socktio发送
// 2.服务器端接收消息并发送消息
// 通过socketio连接的是长连接,所以接收信息要在长链接接听中接收和处理
socket.on('textmsg', (data)=>{
//接收textmsg类型的消息,即可以理解为监听textmsg的信息,有该信息则进行回调函数调用
console.log(data);
// 回复客户端
if(/向服务器发送信息,单发/.test(data)){
socket.emit("textmsg", "回复客户端信息");
}else{
socketio.emit("textmsg", "给客户端们都群发信息");
}
});
});
// 如果之后要写接口,那么是用serve.get来接收,因为serve上绑定了socketio,所以可以直接用
// 页面有自动重连的功能,如果没有连接成功
// 设置静态资源托管目录 public,即可以通过服务器地址直接访问该文件夹内的资源
server.use(express.static('public'));
// 启动服务 不能再使用server.listen,而应该调用http.listen
// 因为sokectio将通信相关的路由注册到了http模块,而非express
// 所以http才可以接收到websockt请求,express则用于处理http服务
http.listen(3000, function(){
console.log("serve is running");
})
实现群聊天室
需求如下:
-
在聊天界面中建立
websocket
连接。 -
当客户端点击发送按钮时,使用已经建立好的连接向服务端发送消息。服务端接收该消息后,将这个消息内容向所有已经连接的客户端再发一遍即可。
-
每个客户端需要监听服务端发回来的消息,控制台打印。
-
消息上屏。把接收到的消息显示在聊天记录区。
-
优化业务细节。
-
实时更新聊天室的人数。
- 在服务端实时维护一个表达在线人数的变量。
count
- 一旦有客户端连接,
count++
- 一旦有客户端断开连接,
count--
- 无论
count
是递增了还是递减了,只要count
有变化,就需要给所有客户端发消息emit('count_updated', count)
。 - 客户端监听并接收该消息类型,更新页面中的在线人数即可。
- 在服务端实时维护一个表达在线人数的变量。
-
实现登录业务。
-
在
index.html
点击登录时,需要获取昵称(文本框里输入)与头像(随机生成文件名)。带着这两个参数一起跳转到chart.html
// window.location.href = "chart.html" location = "chart.html?name=xxx&avatar=xx.jpg"
-
在
chart.html
中获取name
与avatar
,更新用户信息。 -
发消息时,不能只发送内容,而是需要发送一个对象:
{content:'xxxx', name:'xx', avatar:'xxx.jpg'}
-
服务端接收后将原封不动的把对象发给所有客户端。一旦客户端接收到返回回来的对象,需要解析对象的属性,上屏。
-
聊天室文件夹结构为:
群聊完成了,私聊该如何解决?
每当客户端与服务端建立了一个websocket
连接,客户端将会得到一个socket
对象,服务端也会得到一个socket
对象。这些socket
对象用于与对方进行数据通信。所以服务端保存了与每个客户端通信所需要的的socket
对象。
所以如何实现私聊?
当亮亮发消息时,不仅需要发送消息内容,还需要发送对方的ID
(也可以是name
,总之得传递对方的唯一标识符)。服务端接收到该消息后,需要通过ID
(也可以是name
)找到与对方建立起来的socket
连接对象,直接调用socket.emit()
方法发消息即可。
但是这里依然存在着不足,这里在用户退出之后,没有清除对应的用户socket,所以这里依然要去不断完善。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-sca