WebSocket和Socket.IO快速上手

3b8319b965feb284dac275ae5f084449.png

1、WebSocket的基本原理

1.1、什么是WebSocket?

WebSocket协议在2008年诞生,2011年成为国际标准,目前所有主流浏览器都已经支持了。

浏览器是基于HTTP协议的,为什么还需要WebSocket呢?这要说起HTTP协议的一个缺陷:HTTP请求只能由浏览器发起,HTTP服务器无法主动发起HTTP请求。

随着前端应用的日益增长,浏览器不再仅仅完成简单的Web页面渲染,而是日益复杂的独立应用,出现AJAX、HTML5、RESTFul、React、Vue等技术或框架,其中有一个关键需求就是需要从HTTP服务主动向浏览器推送消息,这就是WebSocket产生根本动因,接下来为了从根本上理解WebSocket,我们分别与Socket和HTTP对照比较一下。

1.2、WebSocket vs. Socket

学过计算机网络的话Socket一词应该比较熟悉,Socket最早是UNIX类的操作系统定义的网络通信接口,我们最常用的就是通过Socket接口调用TCP协议实现网络通信。

WebSocket中socket大概就是沿用Socket接口的含义,并通过Web来说明WebSocket是基于Web的socket接口,换句话说就是在在浏览器中使用的socket接口。

基于TCP的Socket接口支持客户端和服务器双向主动发送数据,WebSocket和HTTP协议一样都是基于TCP协议。

8199d6a34613bacdce5714cfde4e4842.png

(图片来源于网络)

WebSocket使用ws表示协议标识,wss和https一样表示通信加密。

1.3、WebSocket vs. HTTP

在WebSocket出现之前,HTTP服务器主动推送消息的技术已经出现,比如HTTP客户端通过轮询/心跳不断向服务器发送HTTP请求,当HTTP服务器有消息要推送给客户端的时候就将消息放到HTTP响应中返回给HTTP客户端。这是一种迂回的方式间接实现了在业务层面HTTP服务器主动推送消息。但是频繁的HTTP请求和响应造成的资源开销很大,于是就诞生了WebSocket协议。

WebSocket协议的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话。

74fa2aaef1a9a6d56b7491746ab6aa4e.png

(图片来源于网络)

WebSocket和HTTP协议简要比较如下:

  • 都是建立在 TCP 协议之上的应用层协议

  • 与 HTTP 协议有着良好的兼容性。默认端口也是使用80端口和443端口,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

  • 数据格式比较轻量,性能开销小,通信高效。可以发送文本,也可以发送二进制数据。

  • 不像AJAX有同源限制需要较为复杂的机制实现跨域访问,WebSocket客户端天生可以与任意服务器通信。

2、WebSocket的客户端接口

2.1、创建WebSocket对象

WebSocket对象提供了用于创建和管理 WebSocket 连接,以及可以通过该连接发送和接收数据的API。使用WebSocket(url[, protocols])构造函数来构造一个WebSocket对象作为返回值,基本方法范例如下:

var ws = new WebSocket("wss://echo.websocket.org");

2.1、WebSocket对象的状态和属性

WebSocket对象的状态有四种:WebSocket.CONNECTING、WebSocket.OPEN、WebSocket.CLOSING和WebSocket.CLOSED,它们的常量值分别0、1、2和3。

WebSocket对象的属性简要列举如下:

  • WebSocket.binaryType使用二进制的数据类型连接。

  • WebSocket.bufferedAmount 只读属性,未发送至服务器的字节数。

  • WebSocket.extensions 只读属性,服务器选择的扩展。

  • WebSocket.onclose用于指定连接关闭后的回调函数。

  • WebSocket.onerror用于指定连接失败后的回调函数。

  • WebSocket.onmessage用于指定当从服务器接受到信息时的回调函数。

  • WebSocket.onopen用于指定连接成功后的回调函数。

  • WebSocket.protocol 只读属性,服务器选择的下属协议。

  • WebSocket.readyState 只读属性,当前的链接状态。

  • WebSocket.url 只读属性,WebSocket 的绝对路径。

2.3、WebSocket对象的方法和事件

WebSocket对象的方法只有两个,即close和send。

  • WebSocket.close([code[, reason]])关闭当前链接。

  • WebSocket.send(data)对要传输的数据进行排队。

WebSocket对象默认支持四个事件:即open、close、message和error。监听事件方法是使用 将一个事件的回调函数赋值给对应的WebSocket对象的属性,四个事件对应的WebSocket对象的属性为:onopen、onclose、onmessage和onerror。

  • open事件,当一个 WebSocket 连接成功时触发。可以通过 onopen 属性来设置监听事件的回调函数。

  • close事件,当一个 WebSocket 连接被关闭时触发。可以通过 onclose 属性来设置监听事件的回调函数。

  • message事件,当通过 WebSocket 收到数据时触发。可以通过 onmessage 属性来设置监听事件的回调函数。

  • error事件,当一个 WebSocket 连接因错误而关闭时触发,例如无法发送数据时。可以通过 onerror 属性来设置监听事件的回调函数。

2.4、WebSocket对象的用法范例代码

如下脚本可以打开https://jsbin.com/muqamiqimu/edit?js,console 看到运行效果。

var ws = new WebSocket("wss://echo.websocket.org");


ws.onopen = function(evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};


ws.onmessage = function(evt) {
console.log( "Received Message: " + evt.data);
ws.close();
};


ws.onclose = function(evt) {
console.log("Connection closed.");

3、Socket.IO快速上手

WebSocket的服务端实现框架众多,比如uWebSockets、WebSocket-Node和Socket.IO等,我们选择MIT许可证发布的Socket.IO为例来学习WebSocket服务端接口。

3.1、配置基于Node.JS的Web框架express

下载安装Node.JS参见https://nodejs.org

创建一个项目目录,比如创建了一个名称为“socket.io”的目录

在项目目录下创建一个项目配置文件package.json,文件内容如下:

{
"name": "socket-chat-example",
"version": "0.0.1",
"description": "my first socket.io app",
"dependencies": {}
}

安装Web框架express

npm install express@4.15.2

创建一个源代码文件index.js

var app = require('express')();
var http = require('http').createServer(app);


app.get('/', (req, res) => {
res.send('<h1>Hello world</h1>');
});


http.listen(3000, () => {
console.log('listening on *:3000');
})

执行执行程序index.js

$ node index.js
listening on *:3000

打开http://localhost:3000 查看程序运行效果

338f910fb4ce2656d2ae9781ebb1986f.png

3.2、服务端安装集成Socket.IO

安装Socket.IO

npm install socket.io

安装过程中会将对socket.io的依赖添加到package.json文件中。

在服务端集成Socket.IO

修改index.js文件如下:

var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);


app.get('/', (req, res) => {
res.send('<h1>Hello world</h1>');
//res.sendFile(__dirname + '/index.html');
});


io.on('connection', (socket) => {
console.log('a user connected');
});


http.listen(3000, () => {
console.log('listening on *:3000');
});

这时打开http://localhost:3000 查看程序运行效果,与之前完全相同,但是服务器端提供的服务却有所不同,我们启用了Socket.IO能够接收客户端的WebSocket连接请求。

3.3、使用Socket.IO客户端代码连接服务器

Socket.IO不仅仅是WebSocket服务端接口框架,还支持许多种轮询机制以及其他实时通信方式,这些方式包含 Adobe Flash Socket、Ajax 长轮询、Ajax multipart streaming 、持久 Iframe、JSONP 轮询等。

当 Socket.IO 检测到当前环境不支持 WebSocket 时,能够自动地选择最佳的方式来实现网络的实时通信。不同的方式客户端的接口也是不同,Socket.IO将客户端也封装成为了通用的接口。

因此,使用WebSocket客户端代码连接服务器既可以使用在客户端创建WebSocket对象的方式,也可以使用Socket.IO客户端接口。

使用创建WebSocket对象的方式连接服务器需要遵守Socket.IO封装的协议规格,会复杂一些,而且兼容性也没有Socket.IO客户端接口好,因此我们使用Socket.IO的客户端接口连接服务器。

将如下文件内容保存为index.html文件放在项目目录下。

<!doctype html>
<html>
  <head>
    <title>Socket.IO example</title>
    <script src="/socket.io/socket.io.js"></script>
    <script>
    var socket = io();
</script>
  </head>
  <body>
    <h1>Hello world</h1>
    <ul id="messages"></ul>
    <form action="">
      <input id="m" autocomplete="off" /><button>Send</button>
    </form>
  </body>
</html>

修改index.js部分内容如下:

app.get('/', (req, res) => {
//res.send('<h1>Hello world</h1>');
res.sendFile(__dirname + '/index.html');
})

也就是在客户端启用var socket = io();与服务器建立连接。这时每打开一次http://localhost:3000 ,服务器都会与之建立一个WebSocket连接。

$ node index.js
listening on *:3000
a user connected
a user connected
a user connected
a user connected

3.4、Socket.IO建立连接的接口

Socket.IO服务端创建Socket.IO对象及监听连接请求

var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);
//...
io.on('connection', (socket) => {
console.log('a user connected');
}

Socket.IO客户端创建Socket.IO对象并发起连接请求

<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
</script>

3.5、Socket.IO的双向通信接口

  • 自定义服务路径和通信方式。默认路径为/socket.io,我们自定义为myownpath

//服务端代码
var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http, {
    path: '/myownpath'
});
//...
io.on('connection', (socket) => {
    console.log('a user connected');
});
//...
//客户端代码
    <script src="myownpath/socket.io.js"></script>
    <script>
        const socket = io('http://localhost:3000/', {
            path: '/myownpath',
            transports: ['websocket', 'polling']
        });




        socket.on('connect', () => {
            console.log(socket.id); // 'G5p5...'
        });
    </script>

transports的默认值为['polling', 'websocket'],也就是Socket.IO默认首先尝试轮询polling方式,然后尝试websocket方式,为了提高效率我们在客户端配置为优先尝试websocket方式。

  • 客户端和服务端双向通信

//客户端代码
        socket.on('server', (data, fn) => {
            console.log(data);
            fn('callback a server function');
        });
        socket.emit('client', 'data to server', (data) => {
            console.log(data);
        });
//服务端代码
io.on('connection', (socket) => {
    console.log(socket.id);
    socket.on('client', (data, fn) => {
        console.log(data);
        fn('callback a client function');
    });
    socket.emit('server', 'data to client', (data) => {
        console.log(data);
    });
});

VS Code也能用来画图?不信来试试

自己动手写一个操作系统内核【内含视频】

在浏览器中输入网址按回车后发生了什么?

自由软件江湖里的码头和规矩

熵减定律 | 疫情与老子智慧

天才的秘密 | 智商是天生的 or 后天培养的?

参考资料

  • http://www.ruanyifeng.com/blog/2017/05/websocket.html

  • https://socket.io/get-started/chat/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农孟宁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值