WebSocket协议 与 IO多路复用
最近在把 Facebook Message 接入客服系统,由于与 Facebook Message 对接的收发消息都是通过调用 http 接口来实现的,如果想实现即时通讯,还需要在中间加一个 WebSocket 来转发消息。如下图:

其中用到了 WebSocket 协议和 IO多路复用相关的知识。在这里做一个学习记录。
为什么需要 WebSocket 协议
- 因为 HTTP 协议有一个缺陷:通信只能先由客户端发起,然后服务器再作出响应,并不能由服务器主动向客户端推送消息。
- WebSocket 协议最大的特点是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息。
WebSocket 与 socket 的之间关系
- WebSocket 是一个网络通信协议,是属于网络七层模型中的应用层的协议,同样属于应用层的协议还有 HTTP 协议、FTP协议、SMTP协议等等。
- 而 socket 是操作系统提供的一套接口,利用这一套接口就可以编写程序实现进程之间的通信、网络通信等功能。
一个 WebSocket 连接是如何建立起来的
WebSocket 连接的初期是基于 HTTP 协议的,假如 WebSocket 的地址是这个:wss://www.xxx.com/websocket ,在连接 WebSocket 的初期浏览器首先会向这个地址发出一个 HTTP GET 请求,请求头信息截图如下:

红色框标出的是比较重要的请求头:
Connection: Upgrade告诉服务端这个连接需要升级。Upgrade: websocket告诉服务端需要升级到 WebSocket 协议。Sec-WebSocket-Key: d97OXZzuRlSJV/6SrX+uUA==是浏览器随机生成的一个字符串。
服务端接收到这个 HTTP 请求,会作出响应,响应头的截图如下:

红色框标出的是比较重要的响应头:
HTTP/1.1 101 Switching Protocols告诉浏览器,服务端已经成功切换了协议。Sec-WebSocket-Accept: axMY+KY1i8F9y9zyUMPhrfuYtPw=这个是服务端拿到请求头中的Sec-WebSocket-Key: d97OXZzuRlSJV/6SrX+uUA==,在d97OXZzuRlSJV/6SrX+uUA==后面拼接一个固定的字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11,对拼接后的字符串做SHA1,得到16进制表示的字符串,将每两位当作一个字节进行分隔,得到字节数组,再对这个字节数组做Base64,得到最后的结果,把最后的结果放到Sec-WebSocket-Accept响应头里返回。
浏览器也会使用同样的算法把请求头中的 Sec-WebSocket-Key 算出一个结果,将这个结果与服务端返回的 Sec-WebSocket-Accept 做对比。就像对暗号一样,两边的暗号相同,WebSocket 连接就会被建立起来。这个过程也叫做握手,握手成功后,就可以愉快的使用这个 WebSocket 连接来收发消息了。
操作系统提供的 socket 接口
WebSocket 的通信,其实是利用了操作系统给我们提供的一套 socket 编程接口。接下来,我把 Linux 系统中给我们提供的 socket 头文件找出来,看看里面有哪些接口提供给我们使用,以及每个接口的作用是什么。找到 socket.h 头文件在如下位置:

打开 socket.h 文件:



打开另一个目录下的 socket.h 文件:

socket 编程的流程如下:

在 socket 服务端除了用到上面流程图列出来的函数,还用到了 setsockopt() 函数,这个函数可以用来设置一些 socket 选项。比如:我在开发调试的过程中,改完代码后需要杀掉运行中的 socket 进程,重新运行新编译出来的 socket。这时候经常会运行失败,原因是进程是立马被杀掉了,但是原来被进程监听的那个端口会进入 TIME_WAIT 状态,而不会立即被释放出来。解决方法有两个:1、杀掉进程后等一会儿,端口被释放了就能被再次使用了。2、在绑定端口之前,利用 setsockopt() 函数,给端口设置一个 SO_REUSEPORT 选项,这样杀掉这个进程后立马重新运行这个进程,也不会运行失败。
IO 多路复用(IO Multiplexing)
在项目中还用到了IO 多路复用:
- 什么是 IO ?答:计算机的输入和输出(Input、Output)
- 什么是 IO 多路复用?答:网上看到一个例子比较有意思。假如一个班有 50 名学生,老师在黑板上布置了一道题目让学生做,
如果老师按照学号先看 1 号学生做出来没有,做出来了就检查他,还没做出来就在原地等他做出来,然后检查他,检查完 1 号学生才轮到 2 号学生......这个就是单进程/单线程。
如果老师能分身,一共分出 50 个分身,每个学生旁边站一个老师......这就是多进程/多线程。
如果老师站在讲台上,有哪位学生做完了就举手,老师下去检查他,检查完老师又回到讲台上,看有哪位同学举手,然后去检查他......这就是 IO 多路复用。
IO 多路复用有3 种:select、poll、epoll。在项目中用到的是 epoll。接下来,我把 Linux 系统中给我们提供的 epoll 头文件找出来,看看里面有哪些接口提供给我们使用,以及每个接口的作用是什么。找到 epoll.h 头文件在如下位置:

打开 epoll.h 文件:



epoll 的使用流程如下:

看到网上有文章说 redis 和 nginx 也有使用 epoll,为了验证他讲的是不是真的。我们找 redis 和 nginx 的源码看一看:


果然 redis 和 nginx 的源码里面都有使用 epoll。
WebSocket 编程,还有其他方案
Swoole 扩展:
- 需要 php-7.1 或更高版本
- 用法如下:
//创建WebSocket Server对象,监听0.0.0.0:9502端口
$ws = new Swoole\WebSocket\Server('0.0.0.0', 9502);
//监听WebSocket连接打开事件
$ws->on('open', function ($ws, $request) {
var_dump($request->fd, $request->server);
$ws->push($request->fd, "hello, welcome\n");
});
//监听WebSocket消息事件
$ws->on('message', function ($ws, $frame) {
echo "Message: {$frame->data}\n";
$ws->push($frame->fd, "server: {$frame->data}");
});
//监听WebSocket连接关闭事件
$ws->on('close', function ($ws, $fd) {
echo "client-{$fd} is closed\n";
});
$ws->start();
想了解更多,请参考 Swoole 官方文档:https://wiki.swoole.com/#/
Workerman:
在学习 WebSocket 的过程中,还发现了一个纯 PHP 实现的框架:Workerman
- 需要 PHP 5.3.3 或更高版本
- 用法如下:
<?php
use Workerman\Worker;
require_once __DIR__ . '/Workerman/Autoloader.php';
// 注意:这里与上个例子不同,使用的是websocket协议
$ws_worker = new Worker("websocket://0.0.0.0:2000");
// 启动4个进程对外提供服务
$ws_worker->count = 4;
// 当收到客户端发来的数据后返回hello $data给客户端
$ws_worker->onMessage = function($connection, $data)
{
// 向客户端发送hello $data
$connection->send('hello ' . $data);
};
// 运行worker
Worker::runAll();
想了解更多,请参考 Workerman 官方文档:http://doc.workerman.net/
WebSocket协议 与 IO多路复用的更多相关文章
- Python网络编程(http协议,IO多路复用、select内核监听)
前言: 什么是IO? 分为IO设备和IO接口两个部分 如Linux系统,I/O操作可以有多种方式 比如DIO(DirectI/O) AIO(AsynchronousI/O异步I/O) Memory-M ...
- MQTT协议笔记之mqtt.io项目Websocket协议支持
前言 MQTT协议专注于网络.资源受限环境,建立之初不曾考虑WEB环境,倒也正常.虽然如此,但不代表它不适合HTML5环境. HTML5 Websocket是建立在TCP基础上的双通道通信,和TCP通 ...
- WebSocket协议:5分钟从入门到精通
一.内容概览 WebSocket的出现,使得浏览器具备了实时双向通信的能力.本文由浅入深,介绍了WebSocket如何建立连接.交换数据的细节,以及数据帧的格式.此外,还简要介绍了针对WebSocke ...
- 八问WebSocket协议:为你快速解答WebSocket热门疑问
一.引言 WebSocket是一种比较新的协议,它是伴随着html5规范而生的,虽然还比较年轻,但大多主流浏览器都已经支持.它使用方面.应用广泛,已经渗透到前后端开发的各种场景中. 对http一问一答 ...
- flask之gevent-websocket的IO多路复用长连接通信
本节目录: (一)笔记总结: (二)gevent-websocket+flask+javascript实现WS即时通信 (1)无昵称群聊 (2)有昵称群聊 (3)私聊 三种通信模型简述: (1)轮询: ...
- 轮询以及webSocket与socket.io原理
概述: 首先,我们知道,起初的http协议只是为了能够进行通信而被创造出来(也就是请求-响应的过程).并没有双向通信这一说,后面随着历史业务的需求,人们使用轮询http来解决双向通信也就是使用xhr或 ...
- Python(七)Socket编程、IO多路复用、SocketServer
本章内容: Socket IO多路复用(select) SocketServer 模块(ThreadingTCPServer源码剖析) Socket socket通常也称作"套接字" ...
- websocket与socket.io
什么是Websocket? Websocket是一个独立于http的实时通信协议,最初是在HTML5中被引用进来的,在HTML5规范中作为浏览器与服务器的核心通信技术被嵌入到浏览器中.WebSocke ...
- IO多路复用之select总结
1.基本概念 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程.IO多路复用适用如下场合: (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/ ...
随机推荐
- 因为一个Docker问题,我顺手整理从安装到常用命令操作手册
今天,自己写了一部分业务代码,是常规代码的另外一种方式,不能在公司的服务器上测试,就自己在PC端搭建了一套和公司集群一样的模板,因为公司的业务模块的测试有单独的服务器(这一块还是我很稀罕的),但是,第 ...
- 老猿Python博文汇总目录--按标题排序
☞ ░ 前往老猿Python博文目录 ░ 本部分为老猿CSDN全部博文的汇总(含转载部分),所有文章在此未进行归类,仅按文章标题排序,方便关键字查找.本部分内容将至少以周为单位定期更新,可能不包含发布 ...
- 老猿学5G随笔:5G网元功能体NF以及NF之间的两种接口--服务化接口和参考点
一.5G功能体之间的接口类型 5G不同功能体之间提供了两种接口: 服务化接口:Service-basedinterface,这个是类似微服务化架构的服务注册和服务发现来实现的功能体对外暴露的接口,这种 ...
- 第三十一章、containers容器类部件QDockWidget停靠窗功能介绍
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 一.概述 QDockWidget类提供了一个可以停靠在QMainWin ...
- PyQt(Python+Qt)学习随笔:QTabWidget选项卡部件移除选项卡的removeTab和clear方法
老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 要从一个QTabWidget中去除选项卡,可用使用removeTab和clear方法. 1.移除选项 ...
- PyQt(Python+Qt)学习随笔:模式窗口的windowModality属性与modal属性
windowModality属性 windowModality属性只对窗口对象有效,保存的是哪些类型的窗口被模式窗口阻塞. 模式窗口防止其他窗口中的部件获取输入.此属性的值控制对应窗口可见时阻塞哪些类 ...
- Scrum冲刺_Day01
一.团队展示: 1.项目:light_note备忘录 2.队名:删库跑路队 3.团队成员 队员(不分先后) 项目角色 黄敦鸿 后端工程师.测试 黄华 后端工程师.测试 黄骏鹏 后端工程师.测试 黄源钦 ...
- 第 3 篇 Scrum 冲刺博客
每天举行会议 会议照片: 昨天已完成的工作与今天计划完成的工作及工作中遇到的困难: 成员姓名 昨天完成工作 今天计划完成的工作 工作中遇到的困难 蔡双浩 了解任务,并做相关学习和思考,创建基本的收藏夹 ...
- 【SDOI2017】相关分析(线段树)
Description 你需要维护一个长度为 \(n\) 的实数对的序列,第 \(i\) 个元素为 \((x_i, y_i)\).现有 \(m\) 次操作: \(\texttt{1 L R}\):设区 ...
- STL——容器(List)list 的反序排列
list.reverse(); //反转链表,比如list包含1, 2, 3, 4, 5五个元素,运行此方法后,list就包含5, 4, 3, 2, 1元素. 1 #include <iostr ...