WebSocket和Socket.io介绍以及聊天室功能实现


WebSocket

在介绍 Socket.io 之前,首先需要说一说什么是 WebSocket。

详细了解参考:

MDN上的介绍

知乎上的介绍

WebSocket 与 Socket.io 介绍
为什么需要WebSocket

我们知道,在 HTML5 之前,客户端和服务器通过 HTTP 协议交换数据,但是,HTTP 协议具有两个特点:

  • HTTP 协议是一种单向的网络协议。在建立连接后,它只允许客户端 Browser/UA (User Agent) 向服务器 WebServer 发送请求后,WebServer 才能返回相应的数据。而 WebServer 不能主动推送数据给 Browser/UA。

  • HTTP 协议是无状态的。客户端向服务器发送连接请求中会包含 identity info(鉴别信息),每次当一个连接结束时,服务器就会将这些鉴别信息丢掉,客户端再次发送 HTTP 请求时,就需要重新发送这些信息。

现在,假设我们需要开发一个基于 Web 的应用程序,需要获取服务器的实时数据,比如股票的实时行情、聊天室的聊天内容等,这就需要客户端和服务器之间反复进行 HTTP 通信,客户端不断发送请求,去获取当前的实时数据。下面介绍两种常见的方式:

  • ajax 轮询

ajax 轮询的原理非常简单,就是让浏览器定时(隔几秒)向服务器发送一次请求,询问是否有新的数据,如果有就返回最新数据,浏览器接收到后将最新数据显示出来,然后重复这一过程。

  • Long Polling

Long Polling 的原理与 ajax 轮询的原理差不多,都是采用轮询的方式,它是 Polling 的一种改进。客户端发送请求到服务器后,服务器并不立即响应客户端,而是保持住这次连接,当有新的数据时,才返回给客户端,客户端接收到数据,进行展示,再立即发送一个新的请求给服务器,并重复这个过程。如果服务器的数据长期没有更新,一段时间后,这个请求就会超时,客户端收到超时消息后,再立即发送一个新的请求给服务器。

从上面可以看出,这两种方式都需要不断的建立 HTTP 连接,然后等待服务器处理。

在这样的情况下,假如客户端能有一种新的网络协议,可以支持客户端和服务器的双向通信的就好了。于是,WebSocket 应运而生。

Websocket是什么样的协议,具体有什么优点

Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。

首先Websocket是基于HTTP协议的,或者说借用了HTTP的协议来完成一部分握手。在握手阶段是一样的

WebSocket 协议

WebSocket 是 HTML5 新增的一种通信协议。WebSocket 协议是一种持久化的双向通信协议,它建立在TCP之上,同 HTTP 一样通过 TCP 来传输数据,但是它和 HTTP 最大的不同有两点:

WebSocket 是一种双向通信协议,在建立连接后,WebSocket 服务器和 Browser/UA(浏览器) 都能主动的向对方发送或接收数据,就像 Socket 一样,不同的是 WebSocket 是一种建立在 Web 基础上的一种简单模拟 Socket 的协议。

WebSocket 需要通过握手连接,类似于 TCP 它也需要客户端和服务器端进行握手连接,连接成功后才能相互通信。

WebSocket 工作流程

浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。因为 WebSocket 连接本质上就是一个 TCP 连接,所以在数据传输的稳定性和数据传输量的大小方面,和传统轮询以技术比较,具有很大的性能优势。

为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息 “Upgrade: WebSocket” 表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。

基于 nodeJs 的 webSocket 框架 socket.io

socket.io 是这篇文章的主角,因为它对 webSocket 做了一个非常完善的封装, 并且提出了 多房间 多命名空间的 概念,让多聊天室同时存在不再是一个问题,所以,下面就会详细的来介绍下 socket.io 这个框架

Socket.io 是一个完全由 JavaScript 实现、基于 Node.js、支持 WebSocket 协议的用于实时通信、跨平台的开源框架,它包括了客户端的 JavaScript 和服务器端的 Node.js。

Socket.io 设计的目标是支持任何的浏览器,任何 Mobile 设备。支持主流的 PC 浏览器 (IE,Safari,Chrome,Firefox,Opera等),Mobile 浏览器(iphone Safari/ipad Safari/Android WebKit/WebOS WebKit等)。

但是,WebSocket 协议是 HTML5 新推出的协议,浏览器对它的支持并不完善,由此可以看出,Socket.io 不可能仅仅是对 WebSocket 的实现,它还支持其他的通信方式,如上面介绍过的 ajax 轮询和 Long Polling。根据浏览器的支持程度,自主选择使用哪种方式进行通讯。

Socket.io 支持的通信方式:

1
2
3
4
5
6
WebSocket
Adobe Flash Socket
AJAX long-polling
AJAX multipart streaming
Forever IFrame
JSONP polling

Socket.io 的使用

node 端使用 express 框架

引入

服务器端:

1
npm install --save socket.io

浏览器端(引入本地文件):

1
<script src="/socket.io/socket.io.js"></script>

浏览器端(CDN 加速):

1
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"> </script>

创建 io 服务器

1
2
3
4
5
6
7
8
9
10
11
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);

app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});

server.listen(3000, function() {
console.log('App listening on port 3000!');
});

服务端

Socket.IO 提供了默认事件(如:connect, message, disconnect)。另外,Socket.IO允许发送并接收自定义事件。

监听客户端连接,回调函数会传递本次连接的socket

1
io.on(‘connection’,function(socket){ });

给所有客户端广播消息

1
io.sockets.emit(‘String’,data);

给指定的客户端发送自定义事件

1
2
socket.emit(‘String’, data);
io.sockets.socket(socketid).emit(‘String’, data);

接收客户端发送的自定义事件

1
socket.on(‘String’,function(data));

给除了自己以外的客户端广播消息

1
socket.broadcast.emit(“msg”, data);

房间

房间是 Socket.IO 提供的一个非常好用的功能。房间相当于为指定的一些客户端提供了一个命名空间,所有在房间里的广播和通信都不会影响到房间以外的客户端。

使用 join() 方法将 socket 加入房间:

1
2
3
4
5
6
7
8
io.on('connection', function(socket){
socket.on('group1', function (data) {
socket.join('group1');
});
socket.on('group2',function(data){
socket.join('group2');
});
});

使用 leave() 方法离开房间:

1
socket.leave(‘some room’);

向房间中除了当前 socket 的其他 socket 发送消息

1
socket.broadcast.to(‘group1’).emit(‘event_name’, data);

broadcast方法允许当前socket client不在该分组内

向房间中所有的 socket 发送消息

1
io.sockets.in(‘group1’).emit(‘event_name’, data);

获取连接的客户端 socket

1
2
3
io.sockets.clients().forEach(function (socket) {
//.....
})

获取所有房间(分组)信息

1
io.sockets.manager.rooms

来获取此socketid进入的房间信息

1
io.sockets.manager.roomClients[socket.id]

获取particular room中的客户端,返回所有在此房间的socket实例

1
io.sockets.clients(‘particular room’)

客户端

1
2
3
4
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
</script>

这样就加载了 socket.io-client。 socket.io-client 暴露了一个 io 全局变量,然后连接服务器。

请注意我们在调用 io() 时没有指定任何 URL,因为它默认将尝试连接到提供当前页面的主机。

监听服务器消息

1
2
3
socket.on('msg',function(data){
console.log(data);
});

socket.on(“String”,function(data){}) 监听服务端发送的消息, String 参数与服务器端 socket.emit(‘String’, data) 第一个参数 String 相同。

向服务器发送消息

1
socket.emit(‘msg’, data);

监听 socket 断开与重连

1
2
3
4
5
6
7
socket.on('disconnect', function() {
console.log("与服务器断开");
});

socket.on('reconnect', function() {
console.log("重新连接到服务器");
});

客户端 socket.on() 监听的事件

  • connect:连接成功
  • connecting:正在连接
  • disconnect:断开连接
  • connect_failed:连接失败
  • error:错误发生,并且无法被其他事件类型所处理
  • message:同服务器端message事件
  • anything:同服务器端anything事件
  • reconnect_failed:重连失败
  • reconnect:成功重连
  • reconnecting:正在重连

聊天室

流程:

  1. 创建 socket 服务器
  2. 浏览器建立 socket 连接
  3. 页面输入聊天内容,点击 “发送” 按钮,向自定义 socket 事件 “chat” 发送聊天信息
  4. 服务器监听浏览器 “chat” 事件,当接收到浏览器发来的聊天信息时,将信息发送给所有连接了 socket 的浏览器
  5. 浏览器监听服务器发来的 “chat” 事件,接收到聊天信息时,在页面上显示

参考连接

WebSocket和Socket.io介绍以及聊天室功能实现
聊天室