MQTT
实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。
本协议运行在TCP/IP,或其它提供了有序、可靠、双向连接的网络连接上。它有以下特点:
- 使用发布/订阅消息模式,提供了一对多的消息分发和应用之间的解耦。
- 消息传输不需要知道负载内容。
- 提供三种等级的服务质量:
- “最多一次”,尽操作环境所能提供的最大努力分发消息。消息可能会丢失。例如,这个等级可用于环境传感器数据,单次的数据丢失没关系,因为不久之后会再次发送。
- “至少一次”,保证消息可以到达,但是可能会重复。
- “仅一次”,保证消息只到达一次。例如,这个等级可用在一个计费系统中,这里如果消息重复或丢失会导致不正确的收费。
- 很小的传输消耗和协议数据交换,最大限度减少网络流量。
- 异常连接断开发生时,能通知到相关各方。
MQTT传输的消息分为:主题(Topic)和负载(payload)两部分:
(1)Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload);
(2)payload,可以理解为消息的内容,是指订阅者具体要使用的内容。
MQTT协议中的方法
MQTT协议中定义了一些方法(也被称为动作),来于表示对确定资源所进行操作。这个资源可以代表预先存在的数据或动态生成数据,这取决于服务器的实现。通常来说,资源指服务器上的文件或输出。主要方法有:
(1)Connect :等待与服务器建立连接。
(2)Disconnect :等待MQTT客户端完成所做的工作,并与服务器断开TCP/IP会话。
(3)Subscribe :等待完成订阅。
(4)UnSubscribe :等待服务器取消客户端的一个或多个topics订阅。
(5)Publish :MQTT客户端发送消息请求,发送完成后返回应用程序线程。
协议数据包结构
一个MQTT数据包由:固定头(Fixed header)、可变头(Variable header)、消息体(payload)三部分构成
1.MQTT数据包结构
固定头(Fixed header),存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识
可变头(Variable header),存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容
消息体(Payload),存在于部分MQTT数据包中,表示客户端收到的具体内容
2.MQTT固定头
固定头
存在于所有MQTT
数据包中,其结构如下:
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|
byte 1 | MQTT控制报文的类型 | 用于指定控制报文类型的标志位 |
byte 2… | 剩余长度 |
(1) MQTT数据包类型
位置: 第1个字节,二进制位7-4 , 相于一个4位的无符号值
名称 | 值 | 流方向 | 描述 |
---|---|---|---|
Reserved (0000xxxx) | 0 | 禁止 | 保留位 |
CONNECT (0001xxxx) | 1 | 客户端到服务器 | 客户端请求连接到服务器 |
CONNACK (0010xxxx) | 2 | 服务器到客户端 | 连接报文确认 |
PUBLISH (0011xxxx) | 3 | 两个方向都允许 | 发布消息 |
PUBACK (0100xxxx) | 4 | 两个方向都允许 | QoS 1消息发布收到确认 |
PUBREC (0101xxxx) | 5 | 两个方向都允许 | 发布收到(保证交付第一步) |
PUBREL (0110xxxx) | 6 | 两个方向都允许 | 发布释放(保证交付第二步) |
PUBCOMP (0111xxxx) | 7 | 两个方向都允许 | QoS 2消息发布完成(保证交互第三步) |
SUBSCRIBE (1000xxxx) | 8 | 客户端到服务器 | 客户端订阅请求 |
SUBACK (1001xxxx) | 9 | 服务器到客户端 | 订阅请求报文确认 |
UNSUBSCRIBE (1010xxxx) | 10 | 客户端到服务器 | 客户端取消订阅请求 |
UNSUBACK (1011xxxx) | 11 | 服务器到客户端 | 取消订阅报文确认 |
PINGREQ (1100xxxx) | 12 | 客户端到服务器 | 心跳请求 |
PINGRESP (1101xxxx) | 13 | 服务器到客户端 | 心跳响应 |
DISCONNECT (1110xxxx) | 14 | 两个方向都允许 | 断开连接通知 |
AUTH (1111xxxx) | 15 | 两个方向都允许 | 认证信息交换 |
标识位
byte 1
位置:bits 3-0。
在不使用标识位的消息类型中,标识位被做为保留位。如果收到无效的标志时,接收端必须关闭网络连接:
数据包 | 标识位 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|
CONNECT | 保留位 | 0 | 0 | 0 | 0 |
CONNACK | 保留位 | 0 | 0 | 0 | 0 |
PUBLISH | MQTT 5.0使用 | DUP | QoS | QoS | RETAIN |
PUBACK | 保留位 | 0 | 0 | 0 | 0 |
PUBREC | 保留位 | 0 | 0 | 0 | 0 |
PUBREL | 保留位 | 0 | 0 | 1 | 0 |
PUBCOMP | 保留位 | 0 | 0 | 0 | 0 |
SUBSCRIBE | 保留位 | 0 | 0 | 1 | 0 |
SUBACK | 保留位 | 0 | 0 | 0 | 0 |
UNSUBSCRIBE | 保留位 | 0 | 0 | 1 | 0 |
UNSUBACK | 保留位 | 0 | 0 | 0 | 0 |
PINGREQ | 保留位 | 0 | 0 | 0 | 0 |
PINGRESP | 保留位 | 0 | 0 | 0 | 0 |
DISCONNECT | 保留位 | 0 | 0 | 0 | 0 |
•DUP
:发布消息的副本。用来在保证消息的可靠传输,如果设置为 1,则在下面的变长中增加MessageId,并且需要回复确认,以保证消息传输完成,但不能用于检测消息重复发送。
•QoS
:发布消息的服务质量,即:保证消息传递的次数
00:最多一次,即:<=1
01:至少一次,即:>=1
10:一次,即:=1
11:预留
•RETAIN
: 发布保留标识,表示服务器要保留这次推送的信息,如果有新的订阅者出现,就把这消息推送给它,如果设有那么推送至当前订阅者后释放。
(2) 剩余长度(Remaining Length)
位置:byte 2(从byte 2,最大可至byte 5)
该字段表示当前消息的剩余内容的字节数,包括可变头部和有效载荷的数据。
该字段本身的字节数是根据可变头部和有效载荷的长度不同而变化的。该可变长度编码方案如下:每个字节的低7位(7-0位)编码剩余长度的数据,第8位表示后面是否还有编码剩余长度的字节。即每个字节编码128个值和一个“延续位”。所以只用一个字节时,最大只可表示127字节的长度。
举例如下,十进制数字64只需用1个字节来编码,即0x40。 十进制数字321(=65 + 2x128)则需要用2个字节来编码,其中第1个字节为1100 0001,该字节的低7位表示65,第8位表示后面还有字节;第2个字节为0000 0010,表示2x128。
协议限制该字段最大为4个字节,这允许应用程序发送的最大消息长度为268435455(256MB),即0xFF,0xFF,0xFF,0x7F。
3.MQTT可变头
MQTT数据包中包含一个可变头,它驻位于固定的头和负载之间。可变头的内容因数据包类型而不同,较常的应用是做为包的标识:
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|
byte 1 | 包标签符(MSB) |
byte 2… | 包标签符(LSB) |
很多类型数据包中都包括一个2字节的数据包标识字段,这些类型的包有:PUBLISH (QoS > 0)、PUBACK、PUBREC、PUBREL、PUBCOMP、SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK
4.Payload消息体
Payload消息体为MQTT数据包的第三部分,CONNECT、SUBSCRIBE、SUBACK、UNSUBSCRIBE四种类型的消息
CONNECT
,消息体内容主要是:客户端的ClientID、订阅的Topic、Message以及用户名和密码。
SUBSCRIBE
,消息体内容是一系列的要订阅的主题以及QoS。
SUBACK
,消息体内容是服务器对于SUBSCRIBE所申请的主题及QoS进行确认和回复。
UNSUBSCRIBE
,消息体内容是要订阅的主题。
MQTT控制报文
CONNECT - 连接请求
客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文必须是CONNECT报文。
有效载荷包含一个或多个编码的字段。包括客户端的唯一标识符,Will主题,Will消息,用户名和密码。除了客户端标识之外,其它的字段都是可选的,基于标志位来决定可变报头中是否需要包含这些字段。
(1)CONNECT 固定报头
(2)CONNECT 可变报头
CONNECT 报文的可变报头按下列次序包含四个字段:协议名(Protocol Name),协议级别(Protocol Level),连接标志(Connect Flags),保持连接(Keep Alive)和属性(Properties)
协议名
: 表示协议名MQTT的UTF-8编码的字符串。MQTT规范的后续版本不会改变这个字符串的偏移和长度。
支持多种协议的服务端使用协议名字段判断数据是否为MQTT报文。协议名必须是UTF-8字符串“MQTT”。如果服务端不愿意接受CONNECT但希望表明其MQTT服务端身份,可以发送包含原因码为0x84(不支持的协议版本)的CONNACK报文,然后必须关闭网络连接
协议级别
: 协议级别字节 byte7 版本 5 (0b00000101)
连接标志
: 协议级别字节 byte8 服务端必须验证CONNECT控制报文的保留标志位(第0位)是否为0 ,如果不为0则此报文为无效报文
保持连接
:使用双字节整数来表示以秒为单位的时间间隔。它是指在客户端传输完成一个MQTT控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保持连接的值。如果没有任何其它的MQTT控制报文可以发送,客户端必须发送一个PINGREQ 报文。如果保持连接的值非零,并且服务端在1.5倍的保持连接时间内没有收到客户端的控制报文,它必须断开客户端的网络连接,并判定网络连接已断开。客户端发送了PINGREQ报文之后,如果在合理的时间内仍没有收到PINGRESP报文,它应该关闭到服务端的网络连接。
保持连接(Keep Alive)值为零的结果是关闭保持连接(Keep Alive)机制。如果保持连接(Keep Alive)值为零,客户端不必按照任何特定的时间发送MQTT控制报文。
属性
:属性长度,会话过期间隔,接收最大值,最大报文长度,主题别名最大值,请求响应信息,请求问题信息,用户属性,认证方法,认证数据
(3)CONNECT 载荷
CONNECT报文的载荷中包含由可变报头(Variable Header)中的标志确定的一个或多个以长度为前缀的字段。这些字段若存在,必须按照客户标识符(Client Identifier)、遗嘱属性(Will Properties)、遗嘱主题(Will Topic)、遗嘱载荷(Will Payload)、用户名(User Name)、密码(Password)的顺序出现
CONNACK - 确认连接请求
CONNACK报文由服务端所发送,作为对来自客户端的CONNECT报文的响应。服务端在发送任何除AUTH以外的报文之前必须先发送包含原因码为0x00(成功)的CONNACK报文。服务端在一次网络连接中不能发送多个CONNACK报文。如果客户端在合理的时间内没有收到服务端的CONNACK报文,客户端应该关闭网络连接。合理 的时间取决于应用的类型和通信基础设施。
PUBLISH - 发布消息
PUBLISH报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。
PUBLISH报文可变报头按顺序包含:主题名(Topic Name),报文标识符(Packet Identifier),属性(Properties)
PUBACK - 发布确认
PUBACK报文是对QoS 1等级的PUBLISH报文的响应。
PUBREC - 发布已接收
PUBREC报文是对QoS等级2的PUBLISH报文的响应。它是QoS 2等级协议交换的第二个报文。
PUBREL - 发布释放
PUBREL报文是对PUBREC报文的响应。它是QoS 2等级协议交换的第三个报文。
PUBCOMP - 发布完成
PUBCOMP报文是对PUBREL报文的响应。它是QoS 2等级协议交换的第四个也是最后一个报文。
SUBSCRIBE - 订阅请求
客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅。每个订阅(Subscription)注册客户端所感兴趣的一个或多个主题。服务端向客户端发送PUBLISH报文以转发被发布到符合这些订阅主题的应用消息。SUBSCRIBE报文同样(为每个订阅)指定了服务端可以向其发送的应用消息最大QoS等级。
SUBACK - 订阅确认
服务端发送SUBACK报文给客户端,用于确认它已收到并且正在处理SUBSCRIBE报文。
SUBACK报文包含一个原因码列表,用于指定授予的最大QoS等级或SUBSCRIBE报文所请求的每个订阅发生的错误。
UNSUBSCRIBE - 取消订阅请求
客户端发送UNSUBSCRIBE报文给服务端,用于取消订阅主题。
UNSUBACK - 取消订阅确认
服务端发送UNSUBACK报文给客户端用于确认收到UNSUBSCRIBE报文。
PINGREQ - PING 请求
客户端发送PINGREQ报文给服务端,可被用于:
- 在没有任何其他MQTT控制报文从客户端发给服务端时,告知服务端客户端还活着。
- 请求服务端发送响应以确认服务端还活着。
- 使用网络已确认网络连接没有断开。
此报文被用在保持连接(Keep Alive)的处理中。
PINGRESP – 心跳响应
服务端发送PINGRESP报文响应客户端的PINGREQ报文。表示服务端还活着。
DISCONNECT – 断开通知
DISCONNECT报文是客户端发给服务端的最后一个MQTT控制报文。表示客户端为什么断开网络连接的原因。客户端和服务端在关闭网络连接之前可以发送一个DISCONNECT报文。如果在客户端没有首先发送包含原因码为0x00(正常断开)DISCONNECT报文并且连接包含遗嘱消息的情况下,遗嘱消息会被发布。
服务端不能发送DISCONNECT报文,直到它发送了包含原因码小于0x80的CONNACK报文之后
AUTH – 认证交换
AUTH报文被从客户端发送给服务端,或从服务端发送给客户端,作为扩展认证交换的一部分,比如质询/响应认证。如果CONNECT报文不包含相同的认证方法,则客户端或服务端发送AUTH报文将造成协议错误(Protocol Error)。
操作行为
会话状态,网络连接,服务质量等级和协议流程,消息分发重试,消息收到, 消息排序,主题名和主题过滤器, 订阅, 流控, 请求/响应, 服务端重定向, 增强认证, 错误处理
使用WebSocket作为网络层
如果MQTT在WebSocket连接上传输,必须满足下面的条件:
- MQTT控制报文必须使用WebSocket二进制数据帧发送。如果收到任何其它类型的数据帧,接收者必须关闭网络连接。
- 单个WebSocket数据帧可以包含多个或者部分MQTT报文。接收者不能假设MQTT控制报文按WebSocket帧边界对齐。
- 客户端必须将字符串”mqtt”包含在它提供的WebSocket子协议列表里。
- 服务端选择和返回的WebSocket子协议名必须是 mqtt。
- 用于连接客户端和服务器的WebSocket URI对MQTT协议没有任何影响。
IP协议的工作方式
同网段
如果源地址主机和目标地址主机在同一网段,目标 IP 地址被 ARP 协议解析为 MAC 地址,然后根据 MAC 地址,源主机直接把数据包发给目标主机。
不同网段
如果源地址主机和目标地址主机在不同网段,数据包发送过程如下:
- 网关(一般为路由器)的 IP 地址被 ARP 协议解析为 MAC 地址。根据该 MAC 地址,源主机将数据包发送到网关。
- 网关根据数据包中的网段 ID 寻找目标网络。如果找到,将数据包发送到目标网段;如果没找到,重复步骤(1)将数据包发送到上一级网关。
- 数据包经过网关被发送到正确的网段中。目标IP地址被ARP协议解析为 MAC 地址。根据该 MAC 地址,数据包被发送给目标地址的主机。
TCP
TCP 协议使用的是面向连接的方法进行通信的,其作用如下:
- 面向流的处理:TCP 以流的方式处理数据。换句话说,TCP 可以一个字节一个字节地接收数据,而不是一次接收一个预订格式的数据块。TCP 把接收到的数据组成长度不等的段,再传递到网际层。
- 重新排序:如果数据以错误的顺序到达目的地,TCP 模块能够对数据重新排序,来恢复原始数据。
- 流量控制:TCP 能够确保数据传输不会超过目的计算机接收数据的能力。
- 优先级与安全:为 TCP 连接设置可选的优先级和安全级别。
- 适当的关闭:以确保所有的数据被发送或接收以后,再进行关闭连接。
TCP 工作模式
TCP 协议的数据包进行传输采用的是服务器端和客户端模式。发送 TCP 数据请求方为客户端,另一方则为服务器端。客户端要与服务器端进行通信,服务器端必须开启监听的端口,客户端才能通过端口连接到服务器,然后进行通信。
netwox 工具提供了相关模块,用于建立 TCP 服务器端和 TCP 客户端。客户端连接服务器端后,可以进行数据通信。为了能够对服务器端进行远程操控,用户也可以建立远程 TCP 服务器端和远程 TCP 客户端,连接以后,可以在服务器端执行命令,进行上传和下载。
TCP/IP
用户数据报协议 UDP(User Datagram Protocol)
- UDP 在传送数据之前不需要先建立连接 ,远程主机在收到 UDP 报文后,不需要给出任何确认。
- 虽然 UDP 不提供可靠交付 ,但在某些情况下 UDP 确是一种最有效的工作方式(一般用于即时通信),比如: QQ 语音、 QQ 视频 、直播等等
传输控制协议 TCP(Transmission Control Protocol)
- TCP 提供 面向连接 的服务。 在传送数据之前必须先建立连接,数据传送结束后要释放连接。
- TCP 不提供广播或多播服务。 由于 TCP 要提供 可靠 的,面向连接的传输服务(TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、流量控制、拥塞控制机制,在数据传完后,还会四次挥手断开连接用来节约系统资源),这不仅使协议数据单元的首部增大很多,还要占用许多处理机资源 。
- TCP 一般用于文件传输、发送和接收邮件、远程登录等场景 。
TCP分层模型
TCP处理
三次握手
- SYN : 连接请求/接收 报文段
- seq : 发送的第一个字节的序号
- ACK : 确认报文段
- ack : 确认号。 希望收到的下一个数据的第一个字节的序号
四次挥手
- FIN : 连接终止位
- seq : 发送的第一个字节的序号
- ACK : 确认报文段
- ack : 确认号。 希望收到的下一个数据的第一个字节的序号
为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,”你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步挥手。
为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。
为什么不能用两次握手进行连接?
答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。
如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
TCP 和UDP 每个数据包的大小
UDP 包的大小是1492 - IP 头(20) - UDP 头(8) = 1464 字节
TCP 包的大小是1492 - IP 头(20) - TCP 头(20) = 1452 字节
HTTP
HTTP报文由哪三部分组成?
HTTP报文由起始行(start line)、头部(header)和主体(body)三部分组成,起始行是对报文进行的描述,头部包含报文的一些属性,主体包含报文的数据(可选,非必选)。
HTTP报文分为哪两类?
HTTP报文可以分为:请求报文(request message)和响应报文(response message)。当客户端向服务端发送请求时,就是发送请求报文;当服务端向客户端返回数据时,就是返回响应报文。
HTTP协议中有那些请求方式?
- GET: 用于请求访问已经被URI(统一资源标识符)识别的资源,可以通过URL传参给服务器
- POST:用于传输信息给服务器,主要功能与GET方法类似,但一般推荐使用POST方式。
- PUT: 传输文件,报文主体中包含文件内容,保存到对应URI位置。
- HEAD: 获得报文首部,与GET方法类似,只是不返回报文主体,一般用于验证URI是否有效。
- DELETE:删除文件,与PUT方法相反,删除对应URI位置的文件。
- OPTIONS:查询相应URI支持的HTTP方法。
HTTP1.0和HTTP1.1的区别
长连接,支持断点续传功能,HOST域,缓存处理,错误通知的管理
HTTP1.x和HTTP2.0的区别
- HTTP/2采用
二进制格式
而非文本格式 - HTTP/2是完全
多路复用
的,而非有序并阻塞的,只需一个连接即可实现并行。多路复用允许单一的 HTTP/2 连接同时发起多重的请求-响应消息 - 使用
报头压缩
,HTTP/2降低了开销。使用HPACK算法对header的数据进行压缩 - HTTP/2让服务器可以将响应
主动“推送”
到客户端缓存中。为了改善延迟,HTTP2.0引入了server push,它允许服务端推送资源给浏览器,在浏览器明确地请求之前,免得客户端再次创建连接发送请求到服务器端获取。这样客户端可以直接从本地加载这些资源,不用再通过网络。
HTTPS
SSL认证过程
SSL(Secure Sockets Layer 安全套接字协议), 及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。
SSL协议中加密传输,采用对称加密和非对称加密的混合加密方式完成。
1.服务器通过非对称加密算法生成一对秘钥(公钥与私钥),拿着公钥去权威机构生成CA证书,证书中包含自己的机构信息以及权威机构信息,以及证书的有效期,以及公钥信息等
2.在tcp建立连接后,服务器首先将自己在权威机构颁发的证书发送给客户端
3.客户端收到证书后,解析得到机构信息以及公钥信息,然后去自己信任的权威机构对当前服务器进行身份验证,通过则通信继续,否则可以选择中断通信或者忽略检测
4.拿着证书中的公钥加密自己要发送给服务端的数据,数据中包含自己支持的对称加密算法列表以及一个随机数
5.服务端收到客户端的公钥加密数据后,使用私钥进行解密,得到对方的对称算法列表和随机数,然后给客户端也回复一个随机数
6.双方通过自己与对方的随机数以及算法列表计算最终得到一个对称加密算法进行加密实际的数据传输。
Socket
Socket又称之为“套接字”,是系统提供的用于网络通信的方法。它的实质并不是一种协议。Socket描述了一个IP、端口对。它简化了程序员的操作,知道对方的IP以及PORT就可以给对方发送消息,再由服务器端来处理发送的这些消息。所以,Socket一定包含了通信的双方,即客户端(Client)与服务端(server)。
Socket是通讯的根本,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机地址的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
应该包含以下步骤:
(1)服务端利用Socket监听端口;
(2)客户端发起连接;
(3)服务端返回信息,建立连接,开始通信;
(4)客户端,服务端断开连接。
建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket,另一个运行于服务器端,称为ServerSocket。
套接字之间的连接过程分为三个步骤:
(1)服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求
(2)客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求
(3)连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求
Netty
Java NIO 和BIO 之间第一个最大的区别是,BIO 是面向流的,NIO 是面向缓冲区的。Java BIO 面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。Java NIO 的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。
在NIO 中有几个核心对象需要掌握:缓冲区(Buffer)、选择器(Selector)、通道(Channel)。
Pipeline
:相信大家都已经知道,在Netty 中每个Channel 都有且仅有一个ChannelPipeline 与之对应,它们的组成关系:一个Channel 包含了一个ChannelPipeline , 而ChannelPipeline 中又维护了一个由ChannelHandlerContext 组成的双向链表。这个链表的头是HeadContext,链表的尾是TailContext,并且每个ChannelHandlerContext 中又关联着一个ChannelHandler。
EventLoop
:一个Netty 程序启动时,至少要指定一个EventLoopGroup(如果使用到的是NIO,通常是指NioEventLoopGroup)。
I/O复用模型
I/O 多路复用的本质是通过一种机制(系统内核缓冲 I/O 数据),让单个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。常见的IO 多路复用方式有【 select 、 poll 、 epoll 、kqueue】 ,都是 Linux API 提供的 IO 复用方式 。
什么是 fd :在 linux 中,内核把所有的外部设备都当成是一个文件来操作,对一个文件的读写会调用内核提供的系统命令,返回一个 fd 文件描述符 。 而对于一个 socket 的读写也会有相应的文件描述符,成为 socketfd。
select
:进程可以通过把一个或者多个 fd 传递给 select 系统调用,进程会阻塞在 select 操作上,这样 select 可以帮我们检测多个 fd 是否处于就绪状态。
二 个缺点: 那么当前进程需要线性轮询所有的 fd,也就是监听的 fd 越多,性能开销越大 。select 在单个进程中能打开的 fd 是有限制的,默认是 1024个。
Reactor 模式(反应器设计模式)
- 由一个专门的线程来处理所有的IO 事件,并负责分发。
- 事件驱动机制:事件到的时候触发,而不是同步的去监视事件。
- 线程通讯:线程之间通过wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。
epoll
: linux 还提供了 epoll 的系统调用, epoll 是基于事件驱动方式来代替顺序扫描,因此性能相对来说更高。 主要原理是,当被监听的 fd 中,有 fd 就绪时, 会告知当前进程具体哪一个 fd 就绪,那么当前进程只需要去从指定的 fd 上读取数据即可。另外,epoll 所能支持的 fd 上线是操作系统的最大文件句柄,这个数字要远远大于 1024。–> AIO (异步IO)
一台机器理论能支持的连接数:
所以对于 tcp 连接的 4 元组中,如果 destination _ip 和 destination _port 不变。那么只有 source _ip 和 source _port是可变的,因此最大的 tcp 连接数应该为 客户端的 ip 数 乘以 客户端的端口数。在 IPV 4 中,不考虑 ip 分类等因素,最大的 ip 数为 2 的 32 次方 * 客户端最大的端口数为 2 的 16 次方,也就是 65536。也就是服务端单机最大的 tcp 连接数约为 2 的 48 次方 。
当然,这只是一个理论值,以 linux 服务器为例 实际的连接数还取决于:内存大小,文件句柄限制,带宽资源的限制。
抓包工具
Flidder
Fiddler是位于客户端和服务器端的HTTP代理,也是目前最常用的http抓包工具之一 。 它能够记录客户端和服务器之间的所有 HTTP请求,可以针对特定的HTTP请求,分析请求数据、设置断点、调试web应用、修改请求的数据,甚至可以修改服务器返回的数据,功能非常强大,是web调试的利器。
Wireshark
是一个网络封包分析软件。网络封包分析软件的功能是截取网络封包,并尽可能显示出最为详细的网络封包资料。Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换。
tcpdump抓包
/data/local/tcpdump -p -vv -s 0 -w /sdcard/capture.pcap