当前位置: 首页 > news >正文

南通专业网站建设舆情信息报告范文

南通专业网站建设,舆情信息报告范文,河北网站建设,网站建设交流推广目录 引言 从 http 到 websocekt 的切换 Sec-WebSocket-Key 与 Sec-WebSocket-Accept 全新的二进制协议 自己实现一个 websocket 服务器 按照协议格式解析收到的Buffer 取出opcode 取出MASK与payload长度 根据mask key读取数据 根据类型处理数据 frame 帧 数据的发…目录 引言 从 http 到 websocekt 的切换 Sec-WebSocket-Key 与 Sec-WebSocket-Accept 全新的二进制协议 自己实现一个 websocket 服务器 按照协议格式解析收到的Buffer 取出opcode 取出MASK与payload长度 根据mask key读取数据 根据类型处理数据 frame 帧 数据的发送 完整代码 总结 引言 我们知道http 是一问一答的模式客户端向服务器发送 http 请求服务器返回 http 响应。这种模式对资源、数据的加载足够用但是需要数据推送的场景就不合适了。 有同学说http2 不是有 server push 么那只是推资源用的 比如浏览器请求了 html服务端可以连带把 css 一起推送给浏览器。浏览器可以决定接不接收。对于即时通讯等实时性要求高的场景就需要用 websocket 了。 从 http 到 websocekt 的切换 websocket 严格来说和 http 没什么关系是另外一种协议格式。但是需要一次从 http 到 websocekt 的切换过程。 切换过程详细来说是这样的 1. 请求的时候带上这几个 header Connection: Upgrade Upgrade: websocket Sec-WebSocket-Key: Ia3dQjfWrAug/6qm7mTZOg前两个很容易理解就是升级到 websocket 协议的意思。第三个 header 是保证安全用的一个 key。 2. 服务端返回这样的 header HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: websocket Sec-WebSocket-Accept: JkE58n3uIigYDMvCKsBbGZsp1A和请求 header 类似Sec-WebSocket-Accept 是对请求带过来的 Sec-WebSocket-Key 处理之后的结果。 加入这个 header 的校验是为了确定对方一定是有 WebSocket 能力的不然万一建立了连接对方却一直没消息那不就白等了么。 Sec-WebSocket-Key 与 Sec-WebSocket-Accept 那 Sec-WebSocket-Key 经过什么处理能得到 Sec-WebSocket-Accept 呢我用 node 实现了一下是这样的 const crypto  require(crypto);function hashKey(key) {const sha1  crypto.createHash(sha1);sha1.update(key  258EAFA5-E914-47DA-95CA-C5AB0DC85B11);return sha1.digest(base64); }也就是用客户端传过来的 key加上一个固定的字符串经过 sha1 加密之后转成 base64 的结果。 这个字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 是固定的不信你搜搜看 随便找个有 websocket 的网站比如知乎就有 过滤出 ws 类型的请求看看这几个 header是不是就是前面说的那些。这个 Sec-WebSocket-Key 是 wk60yiym2FEwCAMVZE3FgQ 而响应的 Sec-WebSocket-Accept 是 XRfPnS8xl11QWZherej/dkHPHM 我们算算看 是不是一毛一样这就是 websocket 升级协议时候的 Sec-WebSocket-Key 对应的 Sec-WebSocket-Accept 的计算过程。 这一步之后就换到 websocket 的协议了那是一个全新的协议。 全新的二进制协议 勾选 message 这一栏可以看到传输的消息可以是文本、可以是二进制。 全新的协议那具体是什么样的协议呢这样的 大家习惯的 http 协议是 key:value 的 header 带个 body 的 它是文本协议每个 header 都是容易理解的字符。这样好懂是好懂但是传输占的空间太大了。而 websocket 是二进制协议一个字节可以用来存储很多信息 比如协议的第一个字节8个二进制bit位就存储了 FIN结束标志、opcode内容类型是 binary 还是 text 等信息。 第二个字节存储了 mask是否有加密payload数据长度。 仅仅两个字节存储了多少信息呀这就是二进制协议比文本协议好的地方。 我们看到的 weboscket 的 message 的收发其实底层都是拼成这样的格式。 只是浏览器帮我们解析了这种格式的协议数据。 这就是 weboscket 的全部流程了。其实还是挺清晰的一个切换协议的过程然后是二进制的 weboscket 协议的收发。 自己实现一个 websocket 服务器 那我们就用 Node.js 自己实现一个 websocket 服务器吧 1. 定义个 MyWebsocket 的 class const { EventEmitter }  require(events); const http  require(http);class MyWebsocket extends EventEmitter {constructor(options) {super(options);const server  http.createServer();server.listen(options.port || 8080);server.on(upgrade, (req, socket)  {});} }继承 EventEmitter 是为了可以用 emit 发送一些事件外界可以通过 on 监听这个事件来处理。 我们在构造函数里创建了一个 http 服务当 ungrade 事件发生也就是收到了 Connection: upgrade 的 header 的时候返回切换协议的 header。 返回的 header 前面已经见过了就是要对 sec-websocket-key 做下处理。 server.on(upgrade, (req, socket)  {this.socket  socket;socket.setKeepAlive(true);const resHeaders  [HTTP/1.1 101 Switching Protocols,Upgrade: websocket,Connection: Upgrade,Sec-WebSocket-Accept:   hashKey(req.headers[sec-websocket-key]),,].join(\r\n);socket.write(resHeaders);socket.on(data, (data)  {console.log(data)});socket.on(close, (error)  {this.emit(close);}); });我们拿到 socket返回上面的 header其中 key 做的处理就是前面聊过的算法 function hashKey(key) {const sha1  crypto.createHash(sha1);sha1.update(key  258EAFA5-E914-47DA-95CA-C5AB0DC85B11);return sha1.digest(base64); }就这么简单就已经完成协议切换了。不信我们试试看。引入我们实现的 ws 服务器跑起来 const MyWebSocket  require(./ws); const ws  new MyWebSocket({ port: 8080 });ws.on(data, (data)  {console.log(receive data:  data); });ws.on(close, (code, reason)  {console.log(close:, code, reason); });然后新建这样一个 html !DOCTYPE HTML html bodyscriptconst ws  new WebSocket(ws://localhost:8080);ws.onopen  function () {ws.send(发送数据);setTimeout(()  {ws.send(发送数据2);}, 3000)};ws.onmessage  function (evt) {console.log(evt)};ws.onclose  function () {};/script /body/html用浏览器的 WebSocket api 建立连接发送消息。 用 npx http-server . 起个静态服务。然后浏览器访问这个 html 这时打开 devtools 你就会发现协议切换成功了 这 3 个 header 还有 101 状态码都是我们返回的。message 里也可以看到发送的消息 再去服务端看看也收到了这个消息 只不过是 Buffer 的也就是二进制的。 按照协议格式解析收到的Buffer 取出opcode 接下来只要按照协议格式解析这个 Buffer并且生成响应格式的协议数据 Buffer 返回就可以收发 websocket 数据了。 这一部分还是比较麻烦的我们一点点来看。 我们需要第一个字节的后四位也就是 opcode。 这样写 const byte1  bufferData.readUInt8(0); let opcode  byte1  0x0f; 读取 8 位无符号整数的内容也就是一个字节的内容。参数是偏移的字节这里是 0。 通过位运算取出后四位这就是 opcode 了。 取出MASK与payload长度 然后再处理第二个字节 第一位是 mask 标志位后 7 位是 payload 长度。 可以这样取 const byte2  bufferData.readUInt8(1); const str2  byte2.toString(2); const MASK  str2[0]; let payloadLength  parseInt(str2.substring(1), 2);还是用 buffer.readUInt8 读取一个字节的内容。 先转成二进制字符串这时第一位就是 mask然后再截取后 7 位的子串parseInt 成数字这就是 payload 长度了。 这样前两个字节的协议内容就解析完了。 有同学可能问了后面咋还有俩 payload 长度呢 这是因为数据不一定有多长可能需要 16 位存长度可能需要 32 位。 于是 websocket 协议就规定了如果那个 7 位的内容不超过 125那它就是 payload 长度。 如果 7 位的内容是 126那就不用它了用后面的 16 位的内容作为 payload 长度。 如果 7 位的内容是 127也不用它了用后面那个 64 位的内容作为 payload 长度。 其实还是容易理解的就是 3 个 if else。 用代码写出来就是这样的 let payloadLength  parseInt(str2.substring(1), 2);let curByteIndex  2;if (payloadLength  126) {payloadLength  bufferData.readUInt16BE(2);curByteIndex  2; } else if (payloadLength  127) {payloadLength  bufferData.readBigUInt64BE(2);curByteIndex  8; }这里的 curByteIndex 是存储当前处理到第几个字节的。 如果是 126那就从第 3 个字节开始读取 2 个字节也就是 16 位的长度用 buffer.readUInt16BE 方法。 如果是 127那就从第 3 个字节开始读取 8 个字节也就是 64 位的长度用 buffer.readBigUInt64BE 方法。 这样就拿到了 payload 的长度然后再用这个长度去截取内容就好了。 根据mask key读取数据 但在读取数据之前还有个 mask 要处理这个是用来给内容解密的 读 4 个字节就是 mask key。 再后面的就可以根据 payload 长度读出来。 let realData  null;if (MASK) {const maskKey  bufferData.slice(curByteIndex, curByteIndex  4);  curByteIndex  4;const payloadData  bufferData.slice(curByteIndex, curByteIndex  payloadLength);realData  handleMask(maskKey, payloadData); } else {realData  bufferData.slice(curByteIndex, curByteIndex  payloadLength);; }然后用 mask key 来解密数据。 这个算法也是固定的用每个字节的 mask key 和数据的每一位做按位异或就好了 function handleMask(maskBytes, data) {const payload  Buffer.alloc(data.length);for (let i  0; i  data.length; i) {payload[i]  maskBytes[i % 4] ^ data[i];}return payload; }这样我们就拿到了最终的数据 根据类型处理数据 但是传给处理程序之前还要根据类型来处理下因为内容分几种类型也就是 opcode 有几种值 const OPCODES  {CONTINUE: 0,TEXT: 1, // 文本BINARY: 2, // 二进制CLOSE: 8,PING: 9,PONG: 10, };我们只处理文本和二进制就好了 handleRealData(opcode, realDataBuffer) {switch (opcode) {case OPCODES.TEXT:this.emit(data, realDataBuffer.toString(utf8));break;case OPCODES.BINARY:this.emit(data, realDataBuffer);break;default:this.emit(close);break;} }文本就转成 utf-8 的字符串二进制数据就直接用 buffer 的数据。 这样处理程序里就能拿到解析后的数据。 我们来试一下 之前我们已经能拿到 weboscket 协议内容的 buffer 了 而现在我们能正确解析出其中的数据 至此我们 websocket 协议的解析成功了 frame 帧 这样的协议格式的数据叫做 frame也就是帧 数据的发送 解析可以了接下来我们再实现数据的发送。 发送也是构造一样的 frame 格式。 定义这样一个 send 方法 send(data) {let opcode;let buffer;if (Buffer.isBuffer(data)) {opcode  OPCODES.BINARY;buffer  data;} else if (typeof data  string) {opcode  OPCODES.TEXT;buffer  Buffer.from(data, utf8);} else {console.error(暂不支持发送的数据类型)}this.doSend(opcode, buffer); }doSend(opcode, bufferDatafer) {this.socket.write(encodeMessage(opcode, bufferDatafer)); }根据发送的是文本还是二进制数据来对内容作处理。 然后构造 websocket 的 frame function encodeMessage(opcode, payload) {//payload.length  126let bufferData  Buffer.alloc(payload.length  2  0);;let byte1  parseInt(10000000, 2) | opcode; // 设置 FIN 为 1let byte2  payload.length;bufferData.writeUInt8(byte1, 0);bufferData.writeUInt8(byte2, 1);payload.copy(bufferData, 2);return bufferData; }我们只处理数据长度小于 125 的情况。 第一个字节是 opcode我们把第一位置 1 通过按位或的方式 写入。 服务端给客户端回消息不需要 mask所以第二个字节就是 payload 长度。 分别把这前两个字节的数据写到 buffer 里指定不同的 offset bufferData.writeUInt8(byte1, 0); bufferData.writeUInt8(byte2, 1);之后把 payload 数据放在后面 payload.copy(bufferData, 2);这样一个 websocket 的 frame 就构造完了。 我们试一下 收到客户端消息后每两秒回一个消息。 收发消息都成功了 完整代码 就这样我们自己实现了一个 websocket 服务器实现了 websocket 协议的解析和生成 完整代码如下 MyWebSocket: //ws.js const { EventEmitter }  require(events); const http  require(http); const crypto  require(crypto);function hashKey(key) {const sha1  crypto.createHash(sha1);sha1.update(key  258EAFA5-E914-47DA-95CA-C5AB0DC85B11);return sha1.digest(base64); }function handleMask(maskBytes, data) {const payload  Buffer.alloc(data.length);for (let i  0; i  data.length; i) {payload[i]  maskBytes[i % 4] ^ data[i];}return payload; }const OPCODES  {CONTINUE: 0,TEXT: 1,BINARY: 2,CLOSE: 8,PING: 9,PONG: 10, };function encodeMessage(opcode, payload) {//payload.length  126let bufferData  Buffer.alloc(payload.length  2  0);;let byte1  parseInt(10000000, 2) | opcode; // 设置 FIN 为 1let byte2  payload.length;bufferData.writeUInt8(byte1, 0);bufferData.writeUInt8(byte2, 1);payload.copy(bufferData, 2);return bufferData; }class MyWebsocket extends EventEmitter {constructor(options) {super(options);const server  http.createServer();server.listen(options.port || 8080);server.on(upgrade, (req, socket)  {this.socket  socket;socket.setKeepAlive(true);const resHeaders  [HTTP/1.1 101 Switching Protocols,Upgrade: websocket,Connection: Upgrade,Sec-WebSocket-Accept:   hashKey(req.headers[sec-websocket-key]),,].join(\r\n);socket.write(resHeaders);socket.on(data, (data)  {this.processData(data);// console.log(data);});socket.on(close, (error)  {this.emit(close);});});}handleRealData(opcode, realDataBuffer) {switch (opcode) {case OPCODES.TEXT:this.emit(data, realDataBuffer.toString(utf8));break;case OPCODES.BINARY:this.emit(data, realDataBuffer);break;default:this.emit(close);break;}}processData(bufferData) {const byte1  bufferData.readUInt8(0);let opcode  byte1  0x0f; const byte2  bufferData.readUInt8(1);const str2  byte2.toString(2);const MASK  str2[0];let curByteIndex  2;let payloadLength  parseInt(str2.substring(1), 2);if (payloadLength  126) {payloadLength  bufferData.readUInt16BE(2);curByteIndex  2;} else if (payloadLength  127) {payloadLength  bufferData.readBigUInt64BE(2);curByteIndex  8;}let realData  null;if (MASK) {const maskKey  bufferData.slice(curByteIndex, curByteIndex  4);  curByteIndex  4;const payloadData  bufferData.slice(curByteIndex, curByteIndex  payloadLength);realData  handleMask(maskKey, payloadData);} this.handleRealData(opcode, realData);}send(data) {let opcode;let buffer;if (Buffer.isBuffer(data)) {opcode  OPCODES.BINARY;buffer  data;} else if (typeof data  string) {opcode  OPCODES.TEXT;buffer  Buffer.from(data, utf8);} else {console.error(暂不支持发送的数据类型)}this.doSend(opcode, buffer);}doSend(opcode, bufferDatafer) {this.socket.write(encodeMessage(opcode, bufferDatafer));} }module.exports  MyWebsocket;Index const MyWebSocket  require(./ws); const ws  new MyWebSocket({ port: 8080 });ws.on(data, (data)  {console.log(receive data:  data);setInterval(()  {ws.send(data     Date.now());}, 2000) });ws.on(close, (code, reason)  {console.log(close:, code, reason); });html: !DOCTYPE HTML html bodyscriptconst ws  new WebSocket(ws://localhost:8080);ws.onopen  function () {ws.send(发送数据);setTimeout(()  {ws.send(发送数据2);}, 3000)};ws.onmessage  function (evt) {console.log(evt)};ws.onclose  function () {};/script /body/html总结 实时性较高的需求我们会用 websocket 实现比如即时通讯、游戏等场景。 websocket 和 http 没什么关系但从 http 到 websocket 需要一次切换的过程。 这个切换过程除了要带 upgrade 的 header 外还要带 sec-websocket-key服务端根据这个 key 算出结果通过 sec-websocket-accept 返回。响应是 101 Switching Protocols 的状态码。 这个计算过程比较固定就是 key 固定的字符串 通过 sha1 加密后再 base64 的结果。 加这个机制是为了确保对方一定是 websocket 服务器而不是随意返回了个 101 状态码。 之后就是 websocket 协议了这是个二进制协议我们根据格式完成了 websocket 帧的解析和生成。 这样就是一个完整的 websocket 协议的实现了。 我们自己手写了一个 websocket 服务有没有感觉对 websocket 的理解更深了呢
http://www.hkea.cn/news/14399405/

相关文章:

  • 有关大数据的网站及网址开发网站找什么公司吗
  • 中国互联网网站性能贵阳网站建设王道下拉惠
  • 如何用微信小程序做网站门户网站广告的特点有
  • 做网站江西如何上传安装wordpress
  • 网站设计公司排名前十wordpress 获取评论
  • 做品牌网站找谁平台网站开发风险
  • 计算机基础网站建设和网络安全Python做网站 性能
  • 站长工具pr值查询有没有做微场景的网站
  • wordpress学做网站微信公众号h5网站开发
  • 网站建设后台程序用什么语言网站设计开发网站
  • 中国城乡住房建设厅网站首页柴油网站怎么做
  • 个人如何开网站新闻最新北京消息今天
  • 曹县住房和城乡建设局网站wordpress图片展示主题yousucai
  • 北京网站改版价格怎么查一个公司是否正规
  • 品牌网站建站网站设计 站
  • 阿里指数网站西安大型网站开发
  • 网站建设 蜂图网络做彩票网站违法吗
  • 网站开发结束语深圳住房建设局网站申报
  • 我有一个网站怎么做外贸厦门小程序开发的公司
  • 我做的网站关键词到首页了没单子vps可以多少wordpress
  • 新网 网站建立广州市网站建设分站价格
  • 湖北 个人网站备案时间南京网站设计机构
  • 成都专业网站建设优化团队昆明官网seo诊断
  • 企业网站服务器多少钱wordpress版权插件
  • 网站的优化从几个方面做网站需要绑定电脑ip吗
  • 网站建设中怎么解决网页上做ppt的网站
  • 网站开发 架构自己做qq头像静态的网站
  • 微网站免费创建平台江西建设厅培训网站
  • 郑州做网站找哪家做二手货的网站有哪些
  • 网站空间报价单记事本怎么做网站