您当前的位置:首页 > 电脑百科 > 程序开发 > 移动端 > 小程序

小程序websocket开发指南

时间:2020-12-17 11:32:51  来源:  作者:

背景:一般与服务端交互频繁的需求,可以使用轮询机制来实现。然而一些业务场景,比如游戏大厅、直播、即时聊天等,这些需求都可以或者说更适合使用长连接来实现,一方面可以减少轮询带来的流量浪费,另一方面可以减少对服务的请求压力,同时也可以更实时的与服务端进行消息交互。

背景知识

HTTP vs WebSocket

名词解释

  1. HTTP:是一个用于传输超媒体文档(如html)的应用层的无连接、无状态协议。
  2. WebSocket:HTML5开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议,基于TCL传输协议,并复用HTTP的握手通道。
小程序websocket开发指南

 

特点

  1. HTTP
  2. WebSocket建立在TCP协议之上,服务器端的实现比较容易;与HTTP协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用HTTP协议,因此握手时不容易屏蔽,能通过各种HTTP代理服务器;数据格式比较轻量,性能开销小,通信高效;可以发送文本(text),也可以发送二进制数据(ArrayBuffer);没有同源限制,客户端可以与任意服务器通信;协议标识符是ws(如果加密,则为wss),服务器网址就是URL;

二进制数组

名词解释

  1. ArrayBuffer​对象:代表原始的二进制数据。代表内存中的一段二进制数据,不能直接读写,只能通过“视图”(​TypedArray​和​DataView​)进行操作(以指定格式解读二进制数据)。“视图”部署了数组接口,这意味着,可以用数组的方法操作内存。
  2. ​TypedArray​对象:代表确定类型的二进制数据。用来生成内存的视图,通过9个构造函数,可以生成9种数据格式的视图,数组成员都是同一个数据类型,比如:​Unit8Array​:(无符号8位整数)数组视图​Int16Array​:(16位整数)数组视图​Float32Array​:(32位浮点数)数组视图
  • ...
  1. ​DataView​对象:代表不确定类型的二进制数据。用来生成内存的视图,可以自定义格式和字节序,比如第一个字节是​Uint8​(无符号8位整数)、第二个字节是​Int16​(16位整数)、第三个字节是​Float32​(32位浮点数)等等,数据成员可以是不同的数据类型。

举个栗子

​ArrayBuffer​也是一个构造函数,可以分配一段可以存放数据的连续内存区域

var buf = new ArrayBuffer(32); // 生成一段32字节的内存区域,每个字节的值默认都是0
为了读写buf,需要为它指定视图。
  1. ​DataView​视图,是一个构造函数,需要提供​ArrayBuffer​对象实例作为参数:
var dataView = new DataView(buf); // 不带符号的8位整数格式
dataView.getUnit8(0) // 0
  1. ​TypedArray​视图,是一组构造函数,代表不同的数据格式。
var x1 = new Init32Array(buf); // 32位带符号整数
x1[0] = 1;
var x2 = new Unit8Array(buf); // 8位不带符号整数
x2[0] = 2;
x1[0] // 2 两个视图对应同一段内存,一个视图修改底层内存,会影响另一个视图
TypedArray(buffer, byteOffset=0, length?)
  • buffer:必需,视图对应的底层​ArrayBuffer​对象
  • byteOffset:可选,视图开始的字节序号,默认从0开始,必须与所要建立的数据类型一致,否则会报错
var buffer = new ArrayBuffer(8);
var i16 = new Int16Array(buffer, 1);
// Uncaught RangeError: start offset of Int16Array should be a multiple of 2

因为,带符号的16位整数需要2个字节,所以byteOffset参数必须能够被2整除。

  • length:可选,视图包含的数据个数,默认直到本段内存区域结束

note:如果想从任意字节开始解读​ArrayBuffer​对象,必须使用​DataView​视图,因为​TypedArray​视图只提供9种固定的解读格式。

​TypedArray​视图的构造函数,除了接受​ArrayBuffer​实例作为参数,还可以接受正常数组作为参数,直接分配内存生成底层的​ArrayBuffer​实例,并同时完成对这段内存的赋值。

var typedArray = new Unit8Array([0, 1, 2]);
typedArray.length // 3
typedArray[0] = 5;
typedArray // [5, 1, 2]

总结

​ArrayBuffer​是一(大)块内存,但不能直接访问​ArrayBuffer​里面的字节。​TypedArray​只是一层视图,本身不储存数据,它的数据都储存在底层的​ArrayBuffer​对象之中,要获取底层对象必须使用buffer属性。其实​ArrayBuffer​ 跟 ​TypedArray​ 是一个东西,前者是一(大)块内存,后者用来访问这块内存。

Protocol Buffers

我们编码的目的是将结构化数据写入磁盘或用于网络传输,以便他人来读取,写入方式有多种选择,比如将数据转换为字符串,然后将字符串写入磁盘。也可以将需要处理的结构化数据由 .proto 文件描述,用 Protobuf 编译器将该文件编译成目标语言。

名词解释

Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

基本原理

一般情况下,采用静态编译模式,先写好 .proto 文件,再用 Protobuf 编译器生成目标语言所需要的源代码文件,将这些生成的代码和应用程序一起编译。

读写数据过程是将对象序列化后生成二进制数据流,写入一个 fstream 流,从一个 fstream 流中读取信息并反序列化。

优缺点

  • 优点

Protocol Buffers 在序列化数据方面,它是灵活的,高效的。相比于 XML 来说,Protocol Buffers 更加小巧,更加快速,更加简单。一旦定义了要处理的数据的数据结构之后,就可以利用 Protocol Buffers 的代码生成工具生成相关的代码。甚至可以在无需重新部署程序的情况下更新数据结构。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言或从各种不同数据流中对你的结构化数据轻松读写。

Protocol Buffers 很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

  • 缺点

消息结构可读性不高,序列化后的字节序列为二进制序列不能简单的分析有效性;

字节消息通道(Frontier)系统

整体设计

为了维护用户在线状态,需要和服务端保持长连接,决定采用websocket来跟服务端进行通信,同时使用消息通道系统来转发消息。

时序图

小程序websocket开发指南

 

技术要点

交互协议

  • connectSocket:创建一个WebSocket连接实例,并通过返回的​socketTask​操作该连接。
const wsUrl = `${domain}/ws/v2?aid=2493&device_id=${did}&fpid=100&access_key=${access_key}&code=${code}`
let socketTask = tt.connectSocket({
    url: wsUrl,
    protocols: ['p1']
});
  • ​wsUrl​遵循​Frontier​的交互协议:
  • aid:应用id,不是宿主App的appid,由服务端指定
  • fpid:由服务端指定
  • device_id:设备id,服务端通过aid+userid+did来维护长连接
  • access_key:用于防止攻击,一般用md5加密算法生成(​md5.hexMD5(fpid + appkey + did + salt);​)
  • code:调用​tt.login​获取的code,服务端通过 code2Session 可以将其转化为open_id,然后进一步转化为user_id用于标识用户的唯一性。
  • note:由于code具有时效性,每次重新建立​websocket​连接时,需要调用​tt.login​重新获取code。

数据协议

前面介绍了那么多关于​Protobuf​的内容,小程序的​webSocket​接口发送数据的类型支持​ArrayBuffer​,再加上​Frontier​对​Protobuf​支持得比较好,因此和服务端商定采用​Protobuf​作为整个长连接的数据通信协议。

想要在小程序中使用​Protobuf​,首先将.proto文件转换成js能解析的json,这样也比直接使用.proto文件更轻量,可以使用pbjs工具进行解析:

  1. 安装pbjs工具
  • 基于node.js,首先安装protobufjs
$ npm install -g protobufjs
  • 安装 pbjs需要的库 命令行执行下“pbjs”就ok
$ pbjs
  1. 使用pbjs转换.proto文件
  • 和服务端约定好的.proto文件
// awesome.proto
package wenlipackage;
syntax = "proto2";
message Header {
required string key = 1;
required string value = 2;
}
message Frame {
required uint64 SeqID = 1;
required uint64 LogID = 2; 
required int32 service = 3;
required int32 method = 4;
repeated Header headers = 5;
optional string payload_encoding = 6;
optional string payload_type = 7;
optional bytes payload = 8;
}
  • 转换awesome.proto文件
$ pbjs -t json awesome.proto > awesome.json

生成如下的awesom.json文件:

{
"nested": {
"wenlipackage": {
"nested": {
"Header": {
"fields": {
            ...
          }
        },
"Frame": {
"fields": {
            ...
          }
        }
      }
    }
  }
}
  • 此时的json文件还不能直接使用,必须采用​module.exports​的方式将其导出去,可生成如下的awesome.js文件供小程序引用。
module.exports = {
"nested": {
"wenlipackage": {
"nested": {
"Header": {
"fields": {
            ...
          }
        },
"Frame": {
"fields": {
            ...
          }
        }
      }
    }
  }
}
  1. 采用Protobuf库编/解码数据
// 引入protobuf模块
import * as protobuf from './weichatPb/protobuf'; 
// 加载awesome.proto对应的json
import awesomeConfig from './awesome.js'; 
// 加载JSON descriptor
const AwesomeRoot = protobuf.Root.fromJSON(awesomeConfig);
// Message类,.proto文件中定义了Frame是消息主体
const AwesomeMessage = AwesomeRoot.lookupType("Frame");
const payload = {test: "123"};
const message = AwesomeMessage.create(payload);
const array = AwesomeMessage.encode(message).finish();
// unit8Array => ArrayBuffer
const enMessage = array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset)
console.log("encodeMessage", enMessage);
// buffer 表示通过小程序this.socketTask.onMessage((msg) => {});接收到的数据
const deMessage = AwesomeMessage.decode(new Uint8Array(buffer));
console.log("decodeMessage", deMessage);

消息通信

一个​websocket​实例的生成需要经过以下步骤:

  1. 建立连接
  • 建立连接后会返回一个websoket实例
  1. 连接打开
  • 连接建立->连接打开是一个异步的过程,在这段时间内是监听不到消息,更是无法发送消息的
  1. 监听消息
  • 监听的时机比较关键,只有当连接建立并生成websocket实例后才能监听
  1. 发送消息
  • 发送当时机也很关键,只有当连接真正打开后才能发送消息

将小程序WebSocket的一些功能封装成一个类,里面包括建立连接、监听消息、发送消息、心跳检测、断线重连等等常用的功能。

  1. 封装websocket类
export default class websocket {
constructor({ heartCheck, isReconnection }) {
this.socketTask = null;// websocket实例
this._isLogin = false;// 是否连接
this._netWork = true;// 当前网络状态
this._isClosed = false;// 是否人为退出
this._timeout = 10000;// 心跳检测频率
this._timeoutObj = null;
this._connectNum = 0;// 当前重连次数
this._reConnectTimer = null;
this._heartCheck = heartCheck;// 心跳检测和断线重连开关,true为启用,false为关闭
this._isReconnection = isReconnection;
  }
  _reset() {}// 心跳重置
_start() {} // 心跳开始
  onSocketClosed(options) {}  // 监听websocket连接关闭
  onSocketError(options) {}  // 监听websocket连接关闭
  onNetworkChange(options) {}  // 检测网络变化
  _onSocketOpened() {}  // 监听websocket连接打开
  onReceivedMsg(callBack) {}  // 接收服务器返回的消息
  initWebSocket(options) {}  // 建立websocket连接
  sendWebSocketMsg(options) {}  // 发送websocket消息
  _reConnect(options) {}  // 重连方法,会根据时间频率越来越慢
  closeWebSocket(){}  // 关闭websocket连接
}

 

  1. 多个page使用同一个​websocket​对象

引入​vuex​维护一个全局​websocket​对象​globalWebsocket​,通过​mapMutations​的​changeGlobalWebsocket​方法改变全局​websocket​对象:

methods: {
    ...mapMutations(['changeGlobalWebsocket']),
    linkWebsocket(websocketUrl) {
// 建立连接
this.websocket.initWebSocket({
        url: websocketUrl,
success(res) { console.log('连接建立成功', res) },
fail(err) { console.log('连接建立失败', err) },
complate: (res) => {
this.changeGlobalWebsocket(res);
        }
      })
    }
}
  • 通过WebSocket类建立连接,将tt.connectSocket返回的websocket实例透传出来,全局共享。
computed: {
    ...mapState(['globalWebsocket']),
    newGlobalWebsocket() {
// 只有当连接建立并生成websocket实例后才能监听
if (this.globalWebsocket && this.globalWebsocket.socketTask) {
if (!this.hasListen) {
this.globalWebsocket.onReceivedMsg((res, data) => {
// 处理服务端发来的各类消息
this.handleServiceMsg(res, data);
          });
this.hasListen = true;
        }
if (this.globalWebsocket.socketTask.readyState === 1) {
// 当连接真正打开后才能发送消息
        }
      }
return this.globalWebsocket;
    },
},
watch: {
    newGlobalWebsocket(newVal, oldVal) {
if(oldVal && newVal.socketTask && newVal.socketTask !== oldVal.socketTask) {
// 重新监听
this.globalWebsocket.onReceivedMsg((res, data) => {
this.handleServiceMsg(res, data);
        });
      }
    },
  },

由于需要监听​websocket​的连接与断开,因此需要新生成一个computed属性​newGlobalWebsocket​,直接返回全局的​globalWebsocket​对象,这样才能watch到它的变化,并且在重新监听的时候需要控制好条件,只有​globalWebsocket​对象socketTask真正发生改变的时候才进行重新监听逻辑,否则会收到重复的消息。

问题总结

  1. 直接引入google官方Protobuf库(protobuf.js)将json => pb,在开发者工具能正常使用,真机却报错:
小程序websocket开发指南

 


小程序websocket开发指南

 

原因是protobufjs 代码里面有用到 Function() {} 来执行一段代码,在小程序中Function 和 eval 相关的动态执行代码方式都给屏蔽了,是不允许开发者使用的,导致这个库不能正常使用。

解决办法:搜了一圈github,找到有人专门针对这个问题,修改了dcodeIO 的protobuf.js部分实现方式,写了一个能在小程序中运行的 protobuf.js 。

  1. ​ArrayBuffer​ vs ​Unit8Array​ 到底是个什么关系??!
  • 受小程序框架、protobuf.js工具以及Frontier系统限制,发送消息和接收消息的格式如下
小程序websocket开发指南

 


小程序websocket开发指南

 

可以看到:

  • 发送消息经过protobuf.js编码后的消息是​Unit8Array​格式的
  • 接收到的服务器原始消息是​ArrayBuffer​格式的

上文介绍了​TyedArray​和​ArrayBuffer​的区别,​Unit8Array​是​TypedArray​对象的一种类型,用来表示​ArrayBuffer​的视图,用来读写​ArrayBuffer​,要访问​ArrayBuffer​的底层对象,必须使用​Unit8Array​的buffer属性。

  • 一开始跟服务端调websocket的连通性,发现用​AwesomeMessage.decode​解析服务端消息会解析失败:
小程序websocket开发指南

 

const msg = xxx; // ArrayBuffer类型
const res = AwesomeMessage.decode(msg); // 直接解析ArrayBuffer会报错
const res = AwesomeMessage.decode(new Uint8Array(msg)); // ArrayBuffer => Unit8Array => decode => JSON

原因是原始msg是​ArrayBuffer​类型,protobuf.js在解码的时候限制了类型是​TypedArray​类型,否则解析失败,因此需要将其转换为​TypedArray​对象,选择​Uint8Array​子类型,才能解析成前端能读取的json对象。

  • 在开发者工具调通协议后,转到真机,发现后端解析不了前端发的消息:
小程序websocket开发指南

 


小程序websocket开发指南

 

【开发者工具抓包消息】

小程序websocket开发指南

 

【真机抓包消息】

抓包发现在开发者工具发送的消息是二进制(Binary)类型的,真机却是文本(Text)类型,这就很奇怪了,仔细翻了下小程序文档:

小程序websocket开发指南

 

小程序框架对发送的消息类型进行了限制,只能是string(Text)或arraybuffer(Binary)类型的,真机为啥被转成了text类型呢,首先肯定不是主动发送的string类型,一种可能就是发送的消息不是arraybuffer类型,默认被转成了string。看了下代码:

const encodeMsg = (msg) => {
const message = AwesomeMessage.create(msg);
const array = AwesomeMessage.encode(message).finish();// unit8Array
return array;
};

发现发送的类型直接是​Unit8Array​,开发者工具没有对其进行转换,这个数据是能直接被服务端解析的,然而在真机被转换成了String,导致服务端解析不了,更改代码,将​Unit8Array​转换成​ArrayBuffer​,问题得到解决,在真机和开发者工具都正常:

const encodeMsg = (msg) => {
const message = AwesomeMessage.create(msg);
const array = AwesomeMessage.encode(message).finish();
  console.log('加密后即将发送的消息', array);
// unit8Array => ArrayBuffer,只支持ArrayBuffer
return array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset)
};

其实还发现一个现象:

小程序websocket开发指南

 

即收到的服务端原始消息最外层是​ArrayBuffer​类型的,解密后的业务数据payload却是​Unit8Array​类型的,结合发送消息时encdoe后的类型也是​Unit8Array​类型,得出如下结论:

  • protobuf.js库和Frontier对数据的处理是以​Unit8Array​类型为准,服务端同时支持​ArrayBuffer​和​Unit8Array​两种类型数据的解析;
  • 小程序框架只支持​ArrayBuffer​和​String​类型数据,其余类型会默认当成​String​类型;

上述两个规则限制导致在数据传输过程中,需要将数据格式转成标准的​ArrayBuffer​即小程序框架支持的数据格式。

ps:至于为啥开发者工具和真机表现不一致,这是因为开发者工具其实是一个web,和小程序的运行时并不太一样,同时由于两者不统一,导致在开发调试过程中踩了许多的坑。 ‍♀️

参考文献

小程序WebSocket接口文档:

https://developer.toutiao.com/docs/api/connectSocket.html#%E8%BE%93%E5%85%A5

protocol buffers介绍:

https://halfrost.com/protobuf_encode/

 

作者:byte

出处:https://segmentfault.com/a/1190000024456875



Tags:websocket   点击:()  评论:()
声明:本站部分内容来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除,谢谢。
▌相关评论
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
▌相关推荐
背景:一般与服务端交互频繁的需求,可以使用轮询机制来实现。然而一些业务场景,比如游戏大厅、直播、即时聊天等,这些需求都可以或者说更适合使用长连接来实现,一方面可以减少轮询...【详细内容】
2020-12-17   websocket  点击:(0)  评论:(0)  加入收藏
1. 前言Websocket是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送...【详细内容】
2020-11-11   websocket  点击:(4)  评论:(0)  加入收藏
前段时间我有这样一个需求,想和一个异地的人一起看电影,先后在网上找了一些方案,不过那几个案都有一些缺点...【详细内容】
2020-10-21   websocket  点击:(8)  评论:(0)  加入收藏
1. 前言Websocket是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数...【详细内容】
2020-09-18   websocket  点击:(8)  评论:(0)  加入收藏
前言服务器和客户端保持长连接通信,实现方式比较多。有很多成熟的框架可以完成,底层无非都是对Socket流的封装和使用。一、SOCKET原理Socket大致是指在端到端的一个连接中,这...【详细内容】
2020-08-06   websocket  点击:(5)  评论:(0)  加入收藏
1. 心跳重连原由 心跳和重连的目的用一句话概括就是客户端和服务端保证彼此还活着,避免丢包发生。websocket连接断开有以下两种情况:前端断开在使用websocket过程中,可能会出现...【详细内容】
2020-07-29   websocket  点击:(3)  评论:(0)  加入收藏
用这些简化了 WebSockets 的开源支持工具来控制你的流媒体。 来源:https://linux.cn/article-12347-1.html 作者:Kevin Sonney 译者:Xingyu.Wang(本文字数:4340,阅读时长大约:6 分...【详细内容】
2020-06-25   websocket  点击:(8)  评论:(0)  加入收藏
前言项目中有即时聊天的需求,经过调研我们采用了socket.io自己实现了一个聊天服务器。开始的一段时间由于用户不是很多,消息的发送接收都还算流畅,最近随着在线用户数量飙升,每...【详细内容】
2020-05-04   websocket  点击:(18)  评论:(0)  加入收藏
公司项目使用WebSocket作为主要的请求方式,知其然也要知其所以然,会用也需要知道它的基本原理,所以写此文章分享下自己的浅见,文章主要包括以下内容: WebSocket是什么 WebSocket...【详细内容】
2020-04-30   websocket  点击:(5)  评论:(0)  加入收藏
大家看看华为商城的客服系统,有没有想过到底是如何制作出来的。你和客服MM的一问一答到底是如何实现的?学过ajax的朋友肯定知道,可以使用轮询方式,隔一秒到服务器里面去查询是否...【详细内容】
2020-03-16   websocket  点击:(14)  评论:(0)  加入收藏
在以前的文章中,我们介绍了HTTP通讯,这种通讯有一个缺点,如果我想从直接从服务器发消息给客户端,需要客户端先发起HTTP请求后服务器才能返回数据,且后续服务器想发送数据给客户端...【详细内容】
2020-03-09   websocket  点击:(18)  评论:(0)  加入收藏
什么是WebSocketWebSocket用于在Web浏览器和服务器之间进行任意的双向数据传输的一种技术。WebSocket协议基于TCP协议实现,包含初始的握手过程,以及后续的多次数据帧双向传输...【详细内容】
2020-03-01   websocket  点击:(21)  评论:(0)  加入收藏
第一部分 介绍HTTP的缺点在于通信只能由客户端发起,如果服务器有连续的状态变化,客户端要获知就非常的麻烦,只能够使用轮训的方法,很消耗服务器资源。WebSocket很好的解决了HT...【详细内容】
2020-01-09   websocket  点击:(29)  评论:(0)  加入收藏
WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输。但它跟 HTTP 没什么关系,它是基于 TCP 的一种独立实现。...【详细内容】
2019-11-06   websocket  点击:(50)  评论:(0)  加入收藏
如今,在不刷新页面的情况下发送消息并获得即时响应在我们看来是理所当然的事情。但是曾几何时,启用实时功能对开发人员来说是一个真正的挑战。开发社区在HTTP长轮询(http long...【详细内容】
2019-10-21   websocket  点击:(41)  评论:(0)  加入收藏
一、HTTPHTTP协议是互联网上应用最为广泛的应用层协议,万维网都要遵守HTTP协议。HTTP/1.0HTTP/1.0版本实现了HTTP协议的基本功能,但是1.0版本性能问题比较明显,因为HTTP协议是...【详细内容】
2019-10-12   websocket  点击:(156)  评论:(0)  加入收藏
(一)websocket协议概述假设我们要实现一个WEB版的聊天室可以采用哪些方案?1.Ajax轮询去服务器取消息客户端按照某个时间间隔不断地向服务端发送请求,请求服务端的最新数据然...【详细内容】
2019-09-09   websocket  点击:(30)  评论:(0)  加入收藏
服务器端推送技术在web开发中比较常用,在早期最简单的解决方案是采用ajax向服务器轮询消息,这种方式的轮询频率不好控制,会导致服务器的压力。稍优的方案为:当客户端向服务器发...【详细内容】
2019-09-05   websocket  点击:(58)  评论:(0)  加入收藏
应用的信息交互过程通常是客户端通过浏览器发出一个请求,服务器端接收和审核完请求后进行处理并返回结果给客户端,然后客户端浏览器将信息呈现出来,这种机制对于信息变化不是特别频繁的应用尚能相安无事,但是对于那些实时...【详细内容】
2019-08-15   websocket  点击:(56)  评论:(0)  加入收藏
TCP/IP协议栈主要分为四层:应用层、传输层、网络层、数据链路层,每层都有相应的协议,如下图 IP:网络层协议;(高速公路)TCP和UDP:传输层协议;(卡车)HTTP:应用层协议;(货物)。HTTP(超文本传...【详细内容】
2019-06-24   websocket  点击:(646)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条