前言说到网络层面的相关内容,难免会涉及到HTTP、TCP、WebSocket等,但相信大部分人都不是很清楚其中的一些关系和概念,尤其是需要做语言表达的时候,网上有很多优秀的资料和文章,但是这些知识还是需要自己去消化和总结,所以才有这篇文章!!!本文的核心内容是WebSocket,主要从以下几个方面进行介绍和实践:什么是WebSocket(what)为什么需要WebSocket(why)WebSocket与HTTP的关系WebSocket的使用场景(when/where)WebSocket实现一个简单的聊天室(how)我是WebSocket为了更好的解释什么是WebSocket,我们需要一个大家熟悉的常用的东西作为对比参考,它就是HTTP。HTTP在大多数项目中都是使用HTTP协议来实现前后端交互的,HTTP协议是建立在TCP协议的基础上建立连接的。它们的关系大致如下:full-duplex&half-duplex。所谓全双工是指发送端和接收端可以在任何时候(包括同时)互相发送消息进行通信,而半双工是指发送端和接收端也可以发送消息相互进行通信,但同一时间只能有一方发送动作。常用的HTTP1.1协议属于半双工,即服务端不具备向客户端主动推送数据资源的能力。当服务端需要向客户端推送数据时,客户端首先要发起请求,也称为请求-响应模型。长轮询&服务端推送由于HTTP1.1协议不支持服务端主动向客户端发送数据报文,但实际需求需要实现这样的功能,比如扫码登录:基于请求-响应model,如果我们需要服务端发送消息数据,首先要向服务端发送相应的查询请求。因此,只要每隔一段时间向服务器发送一次查询请求,就可以根据响应结果来决定是进行下一步还是继续发起查询。但是这个查询请求需要设置一个时间间隔,这个时间间隔可以结合HTTP请求超时得到。毕竟,如果你一直发送查询请求,你会得到很多无意义的查询请求和响应结果。上面提到的在用户不感知的情况下实现服务端推送功能的方案,其实就是所谓的长轮询。WebSocketHTTP&Hypertext我们知道HTTP1.1是半双工的,而HTTP是基于TCP的,而TCP是全双工的,也就是说HTTP1.1根本没有使用TCP的全双工能力。为什么?早期HTTP(HypertextTransferProtocol)的主要目的是传输超文本,因为当时网络上的大部分资源都是明文形式,很多通信协议也使用明文,所以HTTP的设计不可避免地受到了时代。限制。基于TCP的新协议由于HTTP早期设计的局限性,需要一种新的基于TCP的全双工通信协议,这个协议就是WebSocket。WebSocket中的Socket其实并不是一个协议。它是一个抽象层,方便使用TCP或UDP。它是应用层和传输控制层之间的一组接口。从上面的内容我们可以知道HTTP和WebSocket都是基于TCP的,所以在建立连接的过程中肯定少不了大家熟知的TCP三次握手。这部分在上一篇文章重新认识TCP的三向握手和四向挥手中已经介绍过,这里不再赘述。接下来,我们就来看看下面的问题吧!!!服务器怎么知道这次的协议是WebSocket呢?答案很简单,通过请求头标识:Connection:UpgradeConnection是一个连接特定的头域,最常见的指定值包括“close,keep-alive”等,不能与HTTP/2升级:websocketHTTP1.1中使用的升级标头可用于将已建立的客户端/服务器连接升级到不同的协议(通过相同的传输协议)。例如,客户端可以使用它来将连接转换为从HTTP1.1升级到HTTP2.0,或者将HTTP或HTTPS连接升级到WebSocket在HTTP/2中是明确禁止的。headerSec-WebSocket-Key是浏览器随机生成的Base64编码字符,字符串值Sec-WebSocket-Version表示客户端使用的协议版本。Sec-WebSocket-Extensions表示客户端想要表达的协议级扩展。服务器响应状态码101,客户端发送什么协议升级的相应信息?消息Upgrade:websocket,但是连接不会立即建立,因为服务器也必须支持websocket协议,否则会失败。如果服务端支持websocket协议,服务端会返回一个状态码为101的响应头Sec-WebSocket-Accept消息给客户端StatusCode:101表示服务端会根据请求头信息换成不同的协议,存放在HTTP1.1中新增的Sec-WebSocket-Accept中。获取到Sec-WebSocket-Key中具体加密算法的结果,什么时候连接建立完成?其实WebSocket连接在前两道题中就已经完成了,也就是两次握手:客户端发送请求时携带必要的信息请求头如Connection:Upgrade,Upgrade:websocket,Sec-WebSocket-Key:VE9X2xPp8teUvHEvmBL8Tw==to如果服务端支持相应的协议升级,则响应客户端状态码为101,响应头携带值为Sec-WebSocket-Accept加密算法对Sec-WebSocket-Key的值进行处理,经过两次HTTP握手,之后的通信由websocket协议接管。WebSocket使用场景WebSocket的特性非常适合高即时性的服务场景,比如:视频弹幕协同编辑媒体聊天,多人游戏,实时监控(比如运动轨迹),扫码登录..相信大家对这些没有任何疑问,但是值得思考的是,在大多数项目中,可能并不是真正值得思考的WebSocket的使用。为什么大部分可以用WebSocket的场景,却没有真正用到?打开网页上随处可见的客服聊天窗口,结合上面的介绍,你可能会认为是通过WebSocket实现的,其实不然,比如:你会发现在这样的场景中,WebSocket还是没有用,其实多半是因为historyProject的兼容性问题。虽然WebSocket的特性很容易使用,但WebSocket仍然不是HTTP。如果之前的系统链接是针对HTTP协议设计的,是否可以很好的控制改造带来的中间风险和特殊处理?这种转型的成本和收益是否合理?所以在大多数场景下,还是会采用:短连接(心跳包测试)+长连接(接收消息)的方式来实现。服务器端推送不是WebSocket。WebSocket看起来很好吃,但并不是所有场景都可以直接使用。在只需要服务器端推送的场景下,还有其他的方式可以选择,比如EventSource和ServerPush。EventSource—SSE(Server-Sent-Events)EventSource是由服务器推送的网络事件接口。EventSource实例将打开与HTTP服务的持久连接,以文本/事件流格式发送事件,并将保持打开状态直到被要求关闭。也就是说,Eve??ntSource是基于HTTP协议的单向通信,即数据信息从服务器单向分发到客户端。当不需要以消息的形式从客户端向服务端发送数据时,EventSource无疑是一种有效的解决方案。具体的用法这里就不说了,具体的用法大家可以参考这里!优点SSE是基于HTTP的轻量级协议,改造成本不高。SSE默认支持断开和重连。SSE支持发送自定义数据类型。缺点SSE不支持CORS。参数url表示服务器URL,url的协议、域名、端口需要和当前页面的URL一致,兼容性差。只适用于高级浏览器,不支持IE浏览器。IE兼容性可以通过event-source-polyfill来处理。只支持单向通信,即服务端只能向客户端推送数据。客户端无法向服务端推送数据HTTP/2(ServerPush)每个版本的HTTP都是在之前版本的基础上进行优化,就像HTTP/1.1是在HTTP/1.0的基础上进行优化一样,同样HTTP/2也是一个基于HTTP/2的优化在HTTP/1.1上。为了解决要求按正确顺序发送请求的HTTP/1.1连接,理论上可以使用一些并行连接(5到8个)的成本和复杂度。2010年初,谷歌实现了一个实验性的SPDY协议,随后明确了增加响应数量和解决复杂数据传输的问题,SPDY成为了HTTP/2协议的基础。HTTP/2相比HTTP1.1的特点HTTP/2是二进制协议而不是HTTP1.1的文本协议HTTP/2支持多路复用,即可以在同一个TCP连接中处理并行请求HTTP/2使用HPACK该算法实现了头部压缩,在客户端和服务器之间建立了一个“字典”,用索引号表示重复的字符串,用哈夫曼编码对整数和字符串进行压缩。HTTP/2提供了推送ServerPush的能力。ServerPush的缺点是只能向客户端推送静态资源,不能推送自定义数据。所谓ServerPush,举个例子就很容易理解:在HTTP/2之前访问一个站点:服务端返回对应的xxx.html文件,客户端根据预解析的结果预解析xxx.html文件link,script标签等并行加载文件资源...HTTP/2之后,访问一个站点:服务端返回对应的xxx.html文件,可以在返回对应的x.css,x.js等资源同时,也就是实现了静态资源的提前请求,所以可以加快页面的渲染和显示。Chrome将移除对服务器推送的支持。总结一下,当ServerPush响应HTML文件时,服务器会同时主动推送需要的资源文件给浏览器,资源会缓存在本地,解析HTML时要加载的资源会直接从本地缓存中读取,不需要等待网络传输。看起来没什么大问题,但是它有一个比较大的缺陷:ServerPush必然会推送浏览器已经拥有的子资源,因为很多资源在浏览器第一次请求响应的时候会被缓存起来。这种过度的推送/Pointless推送导致网络带宽的使用效率降低,因此显着降低了整体性能优势总的来说,Chrome数据显示HTTP2/Push实际上对整个网络的性能产生负面影响,因此Chrome宣布将在下一个主要版本将在(Chrome106)中删除对服务器推送的支持,请在此处了解更多信息。WebSocket在聊天室实现了那么多,毕竟还是要在代码中实现!实现功能以下实现均采用极简方式实现。很多事情都不会考虑的很全面。例如,数据库根本不用于数据存储。有兴趣的可以自行完善:用户注册昵称识别生成用户uuid加入群欢迎提示根据uuid判断用户是否是第一次加入群聊,如果是则发送和接收欢迎提示群聊消息当前用户发送消息时,通知服务器将聊天信息存储到服务器的chatList中。消息同步发送给其他连接的用户群用户每次进入聊天室,都会同步收到其他用户的聊天信息。Gif表情表情数据存储在emotions.json文件中。用户选择一个表情后,会作为对应的字符添加到消息中,当消息同步给其他用户时,会根据emotions.json中的数据匹配生成,用于切换头像。当前用户可以点击自己的头像,选择相应的图片替换默认头像。因为不涉及图片存储,所以FileReader会将接收到的File文件转换成base64格式演示源码戳这里可以直接到达源码位置:源码地址源码是通过socket.io和socket.io实现的-client,主要是因为socket.io已经做了很多基础工作,可以很好的和一些主流技术集成,开发者只需要完成一些简单的配置。前端部分可以通过npmrundev启动服务器部分可以通过npmrunserver启动局域网访问如果要支持局域网访问,需要在vite.config.ts中指定host配置选项,对应的IP地址可以在CMD终端通过ipconfig查看如果还是不支持局域网访问,可以尝试暂时关闭防火墙,参考为什么会有HTTP协议和websocket协议?EventSource数据推送解决方案节点HTTP/2服务器推送的实际应用学习放弃developer.mozilla.org
