Android 网络协议全解

** 网络分层**
OSI七层模型
OSI七层协议模型主要是:应用层(Application)、表示层(Presentation)、会话层(Session)、传输层(Transport)、网络层(Network)、数据链路层(Data Link)、物理层(Physical)。

TCP/IP五层模型
TCP/IP五层模型:应用层(Application)、传输层(Transport)、网络层(Network)、数据链路层(Data Link)、物理层(Physical)。(也可以说四层,没有物理层)

TCP,UDP
TCP和UDP协议都位于OSI七层模型中的传输层,处于IP协议的上一层,隶属于TCP/IP协议簇

TCP和UDP是传输层的两个主要协议,互为补充,都是用于处理数据包。UDP支持无连接传输,是不可靠的,但是传输性能好;TCP是面向连接的,可靠性更高,用得也最多。

TCP协议
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

应用层向TCP层发送用于网间传输的、用8位字节表示的数据流,然后TCP把数据流分区成适当长度的报文段之后TCP把结果包传给IP层,由它来通过网络将包传送给接收端实体的TCP层。TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。

TCP的可靠性通过以下方式来保证:

超时重传:TCP每发送出一个报文段后,都会启动一个定时器,对目的端传回的确认信息进行确认计时,超时后便重传。
确认信号:当TCP收到一个来自TCP的报文段后,便会发送回一个确认信号。
检验和:TCP将始终保持首部和数据的检验和,如果收到的报文段的检验和有差错,便将其丢弃,希望发送端超时重传。
重新排序:由于IP数据报的达到可能失序,因此TCP将会对数据进行重新排序,以正确的顺序交给应用层。
丢弃重复:由于IP数据报有可能重复,因此TCP将会丢弃重复的数据。
流量控制:TCP连接的两端都有固定大小的缓冲区空间,TCP接受端只允许对端发送本端缓冲区能容纳的数据。
TCP提供流量控制。在双方进行交互时,会彼此通知自己目前接收缓冲区最多可以接收的数据量(通告窗口),以此确保发送方发送的数据不会溢出接收缓冲区。

TCP数据包主要包括:

SYN包:请求建立连接的数据包
ACK包:回应数据包,表示接收到了对方的某个数据包
PSH包:正常数据包
FIN包:通讯结束包
RST包: 重置连接
导致TCP协议发送RST包的原因:
1)SYN 数据段指定的目的端口处没有接收进程在等待。
2)TCP协议想放弃一个已经存在的连接。
3)TCP接收到一个数据段,但是这个数据段所标识的连接不存在。
源、目标端口号字段:占16比特。TCP协议通过使用”端口”来标识源端和目标端的应用进程。端口号可以使用0到65535之间的任何数字。在收到服务请求时,操作系统动态地为客户端的应用程序分配端口号。在服务器端,每种服务在”众所周知的端口”(Well-Know Port)为用户提供服务。

顺序号字段:占32比特。用来标识从TCP源端向TCP目标端发送的数据字节流,它表示在这个报文段中的第一个数据字节。

确认号字段:占32比特。只有ACK标志为1时,确认号字段才有效。它包含目标端所期望收到源端的下一个数据字节。

头部长度字段:占4比特。给出头部占32比特的数目。没有任何选项字段的TCP头部长度为20字节;最多可以有60字节的TCP头部。

标志位字段(U、A、P、R、S、F):占6比特。各比特的含义如下:

◆URG:紧急指针(urgent pointer)有效。
◆ACK:为1时,确认序号有效。  
◆PSH:为1时,接收方应该尽快将这个报文段交给应用层。  
◆RST:为1时,重建连接。
◆SYN:为1时,同步程序,发起一个连接。  
◆FIN:为1时,发送端完成任务,释放一个连接。

窗口大小字段:占16比特。此字段用来进行流量控制。单位为字节数,这个值是本机期望一次接收的字节数。

TCP校验和字段:占16比特。对整个TCP报文段,即TCP头部和TCP数据进行校验和计算,并由目标端进行验证。

紧急指针字段:占16比特。它是一个偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。

选项字段:占32比特。可能包括”窗口扩大因子”、”时间戳”等选项。

TCP建立三次连接的过程(三次握手)
TCP是因特网中的传输层协议,使用三次握手协议建立连接。当主动方发出SYN连接请求后,等待对方回答 SYN + ACK ,并最终对对方的 SYN 执行 ACK 确认。这种建立连接的方法可以防止产生错误的连接,TCP 使用的流量控制协议是可变大小的滑动窗口协议。

TCP三次握手的过程如下:

客户端发送 SYN(SEQ=x)报文给服务器端,进入 SYN_SEND 状态。
服务器端收到 SYN 报文,回应一个 SYN (SEQ=y)ACK(ACK=x+1)报文,进入 SYN_RECV 状态。
客户端收到服务器端的 SYN 报文,回应一个 ACK(ACK=y+1)报文,进入 Established 状态。
三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。

三次握手可以简单理解为A、B两人开始连麦开黑:

A:听到吗? (第一次握手)
B:听得到,你能听到我的声音吗? (第二次握手)
A:能听到。 (第三次握手)

ok,麦没问题,开始游戏

TCP终止连接过程(四次挥手)
客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。(等待 2MSL 的真正目的是为了避免前后两个使用相同四元组的连接中的前一个连接的报文干扰后一个连接,所以让TIME_WAIT状态保持时间⾜够长(2MSL),连接相应⽅向的上的TCP报⽂要么完全响应完毕,要么被丢弃。建⽴第⼆个连接的时候,不会混淆。就是为了让此次 TCP 连接中的所有报文在网络中消失。)
.
服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

image.png

第一次挥手:客户端发送报文告诉服务器没有数据要发送了
第二次挥手:服务端收到,再发送给客户端告诉它我收到了
第三次挥手:服务端向客户端发送报文,请求关闭连接
第四次挥手:客户端收到关闭连接的请求,向服务端发送报文,服务端关闭连接

四次挥手可以简单地理解为A、B两人结束对话:
A:我已经讲完了。 (第一次挥手)
B:知道啦。 (第二次挥手)
B:但是我还没讲完,我跟你讲哦%#¥#¥%#… (A在听B讲完)
B:我讲完啦。 (第三次挥手)
A:收到。 (第四次挥手)

然后各回各家,各找各妈

另一篇文章说法:
三次握手与四次挥手

第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND(邮寄)状态,等待服务器确认;

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV(记录)状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次挥手”

第一次挥手:客户端发送报文告诉服务器没有数据要发送了

第二次挥手:服务端收到,再发送给客户端告诉它我收到了

第三次挥手:服务端向客户端发送报文,请求关闭连接

第四次挥手:客户端收到关闭连接的请求,向服务端发送报文,服务端关闭连接

TCP为什么三次握手不是两次握手,为什么两次握手不安全

为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤

如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认

TCP数据的发送
TCP分组: TCP是可靠传输协议,通过超时与重传机制,来保证收到的数据是完整的。因为TCP是可靠传输协议,如果要传输的数据大于 1480 - 20(tcp头部) =1460Byte时,在ip层被分片,而ip层分片会导致,如果其中的某一个分片丢失,因为tcp层不知道哪个ip数据片丢失,所以就需要重传整个数据段,这样就造成了很大空间和时间资源的浪费,为了解决这个问题,就有了tcp分组和MSS(最长报文大小)概念,利用tcp三次握手建立链接的过程,交互各自的MTU(最大传输单元),然后用小的那个MTU-20-20, 得到MSS,这样就避免在ip层被分片。

在TCP连接建立后, TCP便进入了数据传输的状态, 数据发送的步骤如下图所示,具体为:

image.png

在发送数据时, 发送方对分组数据进行封装, 这里将该分组称为F(x)。 设置F(x)分组的SEQ为其携带的数据的首字节的序号,这里假设其为100;ACK设置为发送方上一次接收到的分组的SEQ+数据长度+1,这里假设其为1。
接收方接收到该分组后, 会向发送方发送一个确认报文, 这里将其称为F(x+1)。 该报文的ACK字段为F(x).SEQ+LEN,即希望接收的下一个分组的序列号,这里为100+60=160(注意该分组的最后一个字节的序列号为159)。
在上述传输中, 若F(x+1)无法顺利到达或者延迟到达发送方, 则会导致F(x)的重发。 这种情况可能导致接收方收到多个F(x)的副本,接收方通过SEQ来判断是否接收过该分组, 并丢弃重复的分组。

接收端在收到数据包后不立马进行ACK数据包的发送,而是等待一定的时间,并统一对收到的多个数据包进行ACK,这种方法称为延迟确认。延迟确认能够减少数据包的发送,节约资源。当接收队列中存在失序分组时,延迟确认将不起作用:收到任意数据包时都将立马进行ACK。

分组窗口和滑动窗口
在上述的TCP分组发送过程中, 上一个分组发送成功并得到确认后, 下一个分组才能发送, 而这中间的“等待”会造成效率的降低。 如果同时允许多个分组进入网络又会引发一系列的问题。 为解决这一些列的问题, 滑动窗口的概念被提出。

建立TCP的两端都维护着一个发送窗口结构和接收窗口结构。发送窗口指的是将发送的TCP分组按照其序列号顺序放置到一个窗口中,窗口左边为已确认的分组,右边为待发送的分组,窗口里为已发送但还未确认的分组,如下图所示。图中,2、3为已确认的分组,10、11为待发送的分组,4-9为窗口中的已发送待确认的分组。

image.png

此时当我们收到4号分组的ACK,10号分组会被发送从而进入窗口,4号分组得到确认从而退出窗口,这个过程仿佛窗口往右滑动了一段,因此称为滑动窗口。

接收窗口的实现逻辑与发送窗口类似,这里不再赘述。

在分组传输过程中,窗口的大小是动态变化的,接收方会通过窗口通告发送分组告知发送方采用多大的窗口大小(简称为:目标窗口大小)。当目标窗口大小小于当前窗口大小时,发送窗口的右边界不动,左边界右移,实现窗口的“缩小”;当目标窗口大小大于当前窗口大小时,发送窗口的左边界不动,右边界右移,实现窗口的“放大”。

在数据发送过程中,当接收方跟不上发送方的速度时,需要告知发送方慢下来,这称为流量控制。使用滑动窗口能够很好的进行流量控制:接收方通过通告较小的窗口大小来降低发送方的发送速度。当接收方忙碌时,可以将目标窗口大小设置为0,从而使发送方停止发送。此时发送方将定期向接收方发送探测报文(keep-alive)来查看接收方窗口的状态,一旦查询到目标窗口大小为非零值,将继续进行分组的发送。

UDP
UDP协议
UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。

UDP不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。比如我们聊天用的QQ就是使用的UDP协议。

UDP应用场景:
1.面向数据报方式
2.网络数据大多为短消息
3.拥有大量Client
4.对数据安全性无特殊要求
5.网络负担非常重,但对响应速度要求高

TCP传输的可靠性由应用层负责,由应用程序根据需要提供报文ACK机制、重传机制、序号机制、重排机制和窗口机制。也可以使用RUDP实现。

原文链接:https://blog.csdn.net/qq_24125575/article/details/105531656

为什么TCP是可靠的,UDP是不可靠的?为什么UDP比TCP快?

TCP/IP协议拥有三次握手双向机制,这一机制保证校验了数据,保证了他的可靠性。

UDP就没有了,udp信息发出后,不验证是否到达对方,所以不可靠。

TCP和UDP的区别

image.png

1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接

2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达; UDP尽最大努力交付,即不保证可靠交付

3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的

UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)

4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

5、TCP首部开销20字节;UDP的首部开销小,只有8个字节

6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

TCP粘包、分包的解决办法

粘包的原因:

(1)发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。

(2)接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。

粘包的解决办法————封包:

封包就是给一段数据加上包头,这样一来数据包就分为包头和包体两部分内容了。包头其实上是个大小固定的结构体,其中有个结构体成员变量表示包体的长度,这是个很重要的变量,其他的结构体成员可根据需要自己定义。根据包头长度固定以及包头中含有包体长度的变量就能正确的拆分出一个完整的数据包。

拆包:

根据封包的包头规则解析出每一个完整的数据包,然后做相应的业务处理。

UDP分包发送和接收方重组数据包

分包:

分包发送(封装包的首部,包括包的大小、类型、序号、数量等)

1、在客户端将你要发送的内容(文件什么的都可以)分块,每块内容进行编号,然后发送;

2、服务端在接收到你的分块数据以后,根据你的客户端数据类容的编号重新组装;

3、一般我们在发送数据的时候,尽量采用比较小的数据块的方式(我的都没有超过1024的),数据块太大的话容易出现发送和接收的数据时间长,匹配出问题。

组包:

假设一个端口只接收固定一个对方数据源,这样,收到一个数据包放到缓冲里,然后在缓冲里根据帧的序号排序(每一帧的大序号是相同的,自己可以给每一个小片加上小序号,包头里可以加上本次数据帧一共分多少片,收到一片就统计一下,判断是否收齐)。 当收齐后,这个帧去掉包头回调给上层。当在一定时间内该帧数据还没有收齐,就说明传输过程有丢包了,把已收到的都丢掉就可以。

当上层的应该收到回调的数据后,可以进行解码播放。不过在解码之前,先判断一下帧序列是否连续。做为视频数据,

如果中间有缺少的,就把这一序列都丢掉,直到下一个I帧。每个帧的序号,最好收发之间协商好,在发送的时候带上。

Tcp/ip协议
定义
TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议不仅仅指的是TCP 和IP两个协议,而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇, 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议。

简介
TCP/IP传输协议,即传输控制/网络协议,也叫作网络通讯协议。它是在网络的使用中的最基本的通信协议。TCP/IP传输协议对互联网中各部分进行通信的标准和方法进行了规定。并且,TCP/IP传输协议是保证网络数据信息及时、完整传输的两个重要的协议。TCP/IP传输协议是严格来说是一个四层的体系结构,应用层、传输层、网络层和数据链路层都包含其中。

TCP/IP协议是Internet最基本的协议,其中应用层的主要协议有Telnet、FTP、SMTP等,是用来接收来自传输层的数据或者按不同应用要求与方式将数据传输至传输层;传输层的主要协议有UDP、TCP,是使用者使用平台和计算机信息网内部数据结合的通道,可以实现数据传输与数据共享;网络层的主要协议有ICMP、IP、IGMP,主要负责网络中数据包的传送等;而网络访问层,也叫网路接口层或数据链路层,主要协议有ARP、RARP,主要功能是提供链路管理错误检测、对不同通信媒介有关信息细节问题进行有效处理等。

TCP/IP模型与OSI模型各层的对照关系:

数据封装
TCP/IP协议族按照层次由上到下,层层包装。最上面的是应用层,这里面有http,ftp,等等我们熟悉的协议。而第二层则是传输层,著名的TCP和UDP协议就在这个层次。第三层是网络层,IP协议就在这里,它负责对数据加上IP地址和其他的数据以确定传输的目标。第四层是数据链路层,这个层次为待传送的数据加入一个以太网协议头,并进行CRC编码,为最后的数据传输做准备。

数据链路层
物理层负责0、1比特流与物理设备电压高低、光的闪灭之间的互换。 数据链路层负责将0、1序列划分为数据帧从一个节点传输到临近的另一个节点,这些节点是通过MAC来唯一标识的(MAC,物理地址,一个主机会有一个MAC地址)。

封装成帧: 把网络层数据包加头和尾,封装成帧,帧头中包括源MAC地址和目的MAC地址。

透明传输:零比特填充、转义字符。

可靠传输: 在出错率很低的链路上很少用,但是无线链路WLAN会保证可靠传输。

差错检测(CRC):接收者检测错误,如果发现差错,丢弃该帧。

网络层
1、IP协议
IP协议是TCP/IP协议的核心,所有的TCP,UDP,IMCP,IGMP的数据都以IP数据格式传输。要注意的是,IP不是可靠的协议,这是说,IP协议没有提供一种数据未传达以后的处理机制,这被认为是上层协议:TCP或UDP要做的事情。

IP所提供的服务大致可归纳为两类:

IP信息包的传送。
IP信息包的分割与重组。
1.1、IP信息包传送
IP是网络之间信息传送的协议,可将IP信息包从源设备(例如用户的计算机)传送到目的设备(例如某部门的www服务器)。为了达到这样的目的,IP必须依赖IP地址与IP路由器两种机制来实现。

1.1.1、IP地址
IP规定网络上所有的设备都必须有一个独一无二的IP地址,就好比是邮件上都必须注明收件人地址,邮递员才能将邮件送到。同理,每个IP信息包都必须包含有目的设备的IP地址,信息包才可以正确地送到目的地。同一设备不可以拥有多个IP地址,所有使用IP的网络设备至少有一个唯一的IP地址。

1.1.2、IP路由
互联网是由许多个网络连接所形成的大型网络。如果要在互联网中传送IP信息包,除了确保网络上每个设备都有一个唯一的IP地址之外,网络之间还必须有传送的机制,才能将IP信息包通过一个个的网络传送到目的地。此种传送机制称为IP路由。 [2]
各个网络通过路由器相互连接。路由器的功能是为IP信息包选择传送的路径。换言之,必须依靠沿途各路由器的通力合作,才能将IP信息包送到目的地。在IP路由的过程中,由路由器负责选择路径,IP信息包则是被传送的对象。

1.2、IP信息包的分割与重组
为了能把一个IP报文放在不同的物理帧中,最大IP报文的长度就只能等于这条路径上所有物理网络的MTU的最小值。当数据报通过一个可以传输长度更大的帧的网络时,把数据报的大小限制在互联网上最小的MTU之下不经济;如果数据报的长度超过互联网中最小的MTU值的话,则当该数据报在穿越该子网时,就无法被封装在一个帧中。

IP协议在发送IP报文时,一般选择一个合适的初始长度。如果这个报文要经历的中间物理网络的MTU值比IP报文长度要小,则IP协议把这个报文的数据部分分割成若干个较小的数据片,组成较小的报文,然后放到物理帧中去发送。每个小的报文称为一个分段。分段的动作一般在路由器上进行。如果路由器从某个网络接口收到了一个IP报文,要向另外一个网络转发,而该网络的MTU比IP报文长度要小,那么就要把该IP报文分成多个小IP分段后再分别发送。

重组是分段的逆过程,把若干个IP分段重新组合后还原为原来的IP报文。在目的端收到一个IP报文时,可以根据其分段偏移和MF标志位来判断它是否是一个分段。如果MF位是0,并且分段偏移为0,则表明这是一个完整的IP数据报。否则,如果分段偏移不为0,或者MF标志位为1,则表明它是一个分段。这时目的地端需要实行分段重组。IP协议根据IP报文头中的标识符字段的值来确定哪些分段属于同一个原始报文,根据分段偏移来确定分段在原始报文中的位置。如果一个IP数据报的所有分段都正确地到达目的地,则把它重新组织成一个完整的报文后交给上层协议去处理。

1、ARP及RARP协议
ARP 是根据IP地址获取MAC地址的一种协议。

ARP(地址解析)协议是一种解析协议,本来主机是完全不知道这个IP对应的是哪个主机的哪个接口,当主机要发送一个IP包的时候,会首先查一下自己的ARP高速缓存(就是一个IP-MAC地址对应表缓存)。

如果查询的IP-MAC值对不存在,那么主机就向网络发送一个ARP协议广播包,这个广播包里面就有待查询的IP地址,而直接收到这份广播的包的所有主机都会查询自己的IP地址,如果收到广播包的某一个主机发现自己符合条件,那么就准备好一个包含自己的MAC地址的ARP包传送给发送ARP广播的主机。

而广播主机拿到ARP包后会更新自己的ARP缓存(就是存放IP-MAC对应表的地方)。发送广播的主机就会用新的ARP缓存数据准备好数据链路层的的数据包发送工作。
原文链接:https://blog.csdn.net/qq_24125575/article/details/105488168

Socket原理
1、什么是Socket
套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。

传输层实现端到端的通信,因此,每一个传输层连接有两个端点。那么,传输层连接的端点是什么呢?不是主机,不是主机的IP地址,不是应用进程,也不是传输层的协议端口。传输层连接的端点叫做套接字(socket)。根据RFC793的定义:端口号拼接到IP地址就构成了套接字。所谓套接字,实际上是一个通信端点,每个套接字都有一个套接字序号,包括主机的IP地址与一个16位的主机端口号,即形如(主机IP地址:端口号)。例如,如果IP地址是210.37.145.1,而端口号是23,那么得到套接字就是(210.37.145.1:23)。

那么套接字这个抽象层位于TCP/IP协议簇四层结构中的哪个位置呢?看下图就一目了然了:

它位于应用层与传输层之间,是一组封装好的接口,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据。


2、Java Socket实现
2.1、主要方法

Accept方法用于产生”阻塞”,直到接受到一个连接,并且返回一个客户端的Socket对象实例。”阻塞”是一个术语,它使程序运行暂时”停留”在这个地方,直到一个会话产生,然后程序继续;通常”阻塞”是由循环产生的。
getInputStream方法获得网络连接输入,同时返回一个InputStream对象实例。
getOutputStream方法连接的另一端将得到输入,同时返回一个OutputStream对象实例。
2.2、服务器端

创建ServerSocket对象,绑定监听端口。
通过accept()方法监听客户端请求。
连接建立后,通过输入流读取客户端发送的请求信息。
通过输出流向客户端发送响应信息。
关闭响应的资源。
2.3、客户端

创建Socket对象,指明需要连接的服务器的地址和端口号。
连接建立后,通过输出流向服务器发送请求信息。
通过输入流获取服务器响应的信息。
关闭相应资源。
2.4、多线程实现服务器与多客户端之间通信步骤

服务器端创建ServerSocket,循环调用accept()等待客户端连接。
客户端创建一个socket并请求和服务器端连接。
服务器端接受客户端请求,创建socket与该客户建立专线连接。
建立连接的两个socket在一个单独的线程上对话。
服务器端继续等待新的连接。
2.5、客户端代码

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class SocketClient {
public static void main(String args[]) throws Exception {
// 要连接的服务端IP地址和端口
String host = "127.0.0.1";
int port = 60000;
// 与服务端建立连接
Socket socket = new Socket(host, port);
// 建立连接后获得输出流
OutputStream outputStream = socket.getOutputStream();
String message = "hello server";
socket.getOutputStream().write(message.getBytes("UTF-8"));
//通过shutdownOutput告诉服务器已经发送完数据,后续只能接受数据
socket.shutdownOutput();

InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
StringBuilder sb = new StringBuilder();
while ((len = inputStream.read(bytes)) != -1) {
  //注意指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
  sb.append(new String(bytes, 0, len,"UTF-8"));
}
System.out.println("get message from server: " + sb);

inputStream.close();
outputStream.close();
socket.close();
}
}

2.6、服务器端代码

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketServer {
public static void main(String[] args) throws Exception {
 // 监听指定的端口
int port = 60000;
ServerSocket server = new ServerSocket(port);
// server将一直等待连接的到来
System.out.println("server将一直等待连接的到来");
Socket socket = server.accept();
// 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
StringBuilder sb = new StringBuilder();
//只有当客户端关闭它的输出流的时候,服务端才能取得结尾的-1
while ((len = inputStream.read(bytes)) != -1) {
  // 注意指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
  sb.append(new String(bytes, 0, len, "UTF-8"));
}
System.out.println("get message from client: " + sb);

OutputStream outputStream = socket.getOutputStream();
outputStream.write("Hello Client,I get the message.".getBytes("UTF-8"));

inputStream.close();
outputStream.close();
socket.close();
server.close();
}
}

http协议
.http协议是hype text transfer protocol(超文本传输协议)的缩写,是一个基于请求与响应模式的短连接,无状态,应用层的协议,支持c/s模式,简单快速,灵活。,有四个版本,0.9,1.0,1.1,2.0.我们常用的是1.1版本。

0.9

0.9协议是适用于各种数据信息的简洁快速协议,但是远不能满足日益发展的各种应用的需要。0.9协议就是一个交换信息的无序协议,仅仅限于文字。由于无法进行内容的协商,在双发的握手和协议中,并有规定双发的内容是什么,也就是图片是无法显示和处理的。 [3]

1.0

到了1.0协议阶段,也就是在1982年,Tim Berners-Lee提出了HTTP/1.0。在此后的不断丰富和发展中,HTTP/1.0成为最重要的面向事务的应用层协议。该协议对每一次请求/响应建立并拆除一次连接。其特点是简单、易于管理,所以它符合了大家的需要,得到了广泛的应用。

1.1

在1.0协议中,双方规定了连接方式和连接类型,这已经极大扩展了HTTP的领域,但对于互联网最重要的速度和效率,并没有太多的考虑。毕竟,作为协议的制定者,当时也没有想到HTTP会有那么快的普及速度。 [3]

关于HTTP1.1协议的具体内容可以参考RFC 2616。 [4]

2.0

HTTP2.0的前身是HTTP1.0和HTTP1.1。虽然之前仅仅只有两个版本,但这两个版本所包含的协议规范之庞大,足以让任何一个有经验的工程师为之头疼。网络协议新版本并不会马上取代旧版本。实际上,1.0和1.1在之后很长的一段时间内一直并存,这是由于网络基础设施更新缓慢所决定的。 [5]

关于HTTP2.0协议的具体内容可以参考RFC 7540。 [6]

C/S:支持C/S(客户/服务器)模式
简单快速:客户向服务器请求服务时,只需传送请求方法和路径

请求方法常用的有GET、HEAD、POST,每种方法规定了客户与服务器联系的类型不同。 由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度

灵活:允许传输任意类型的数据对象,由Content-Type标记

短连接:每次处理一个请求,处理完成后既断开(服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。)

无状态:对事务处理没有记忆能力,少状态意味着果后续处理需要前面信息,则它必须重传,这样可能导致每次连接传送的数据量增大。
另一方面,在服务器不需要先前信息时它的应答较快

HTTP是基于[客户/服务器]模式,且面向连接的。典型的HTTP[事务处理]有如下的过程:

(1)客户与服务器建立连接;

(2)客户向服务器提出请求;

(3)服务器接受请求,并根据请求返回相应的文件作为应答;

(4)客户与服务器关闭连接。

客户与服务器之间的HTTP连接是一种一次性连接,它限制每次连接只处理一个请求,当服务器返回本次请求的应答后便立即关闭连接,下次请求再重新建立连接。

HTTP是一种[无状态协议]即服务器不保留与客户交易时的任何状态。这就大大减轻了服务器记忆负担,从而保持较快的[响应速度]
HTTP是一种面向对象的协议。允许传送任意类型的[数据对象]它通过[数据类型]和长度来标识所传送的数据内容和大小,并允许对数据进行压缩传送。

HTTP支持[持久连接](指不必为每个request object(例如很小的gif广告图像)的传送建立一个新的TCP(socket)连接,减少TCP建立时间和相应的系统损耗。),在HTTP / 0.9和1.0中,连接在单个请求/响应对之后关闭。在HTTP / 1.1中,引入了保持活动机制,其中连接可以重用于多个请求。这样的[持久性]连接可以明显减少请求延迟,因为在发送第一个请求之后,客户端不需要重新协商[TCP]3-Way-Handshake连接。另一个积极的副作用是,通常,由于TCP的缓慢启动机制,连接随着时间的推移而变得更快。

该协议的1.1版还对HTTP / 1.0进行了带宽优化改进。例如,HTTP / 1.1引入了[分块传输编码],以允许流传输而不是缓冲持久连接上的内容。HTTP流水线进一步减少了[延迟时间],允许客户端在等待每个响应之前发送多个请求。协议的另一项[附加功能]是字节服务,即服务器仅传输客户端明确请求的资源部分。

HTTP规范定义了9种请求方法,每种请求方法规定了客户和服务器之间不同的[信息交换]方式,常用的请求方法是GET和POST。服务器将根据客户请求完成相应操作,并以应答块形式返回给客户,最后关闭连接。
HTTP报文由从[客户机]到服务器的请求和从服务器到客户机的响应构成。请求报文格式如下:

请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体

请求行以方法字段开始,后面分别是URL字段和HTTP协议版本字段,并以CR[LF]结尾。SP是[分隔符]。除了在最后的CRLF序列中CF和LF是必需的之外,其他都可以不要。有关通用信息头,请求头和实体头方面的具体内容可以参照相关文件。

应答报文格式如下:

状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体

状态[码元]由3位数字组成,表示请求是否被理解或被满足。[原因分析]是对原文的状态码作简短的描述,状态码用来支持[自动操作],而原因分析用来供用户使用。客户机无需用来检查或显示语法。有关通用信息头,响应头和实体头方面的具体内容可以参照相关文件。

http有两种报文:请求报文和响应报文

请求报文由请求行,请求报头,和请求数据组成

请求行:抓包第一行,包括请求方法,url和http版本

请求报头:指的就是题目中“里面的协议头部”

请求数据:指post方式提交的表单数据

响应报文由状态行,响应报头,响应正文组成

状态行:状态码

响应报头:同请求报头

响应正文:服务器返回的资源数据

接下来是http头部,既请求报头和响应报头,统称消息报头,消息报头可以分为通用报头,请求报头,响应报头,实体报头等

通用报头和实体报头既可以出现在请求报头中,也可以出现在响应报头中,通用报头包含的字段如:Date Connection Cache-Control,实体报头中有Content-Type Content-Length Content-Language Content-Encoding.

请求报头中包含的字段有:

Host,User-Agent,Accept-Encoding,Accept-Language,Connection

响应报头包含的字段:

Location,Server

HTTP请求方法

HTTP请求方法有8种,分别是GET、POST、DELETE、PUT、HEAD、TRACE(trace)、CONNECT(connect) 、OPTIONS(options)。

其中PUTDELETEPOSTGET分别对应着增删改查,对于移动开发最常用的就是POST和GET了。

GET:请求获取Request-URI所标识的资源

POST:在Request-URI所标识的资源后附加新的数据

HEAD:请求获取由Request-URI所标识的资源的响应消息报头

PUT: 请求服务器存储一个资源,并用Request-URI作为其标识

DELETE :请求服务器删除Request-URI所标识的资源

TRACE : 请求服务器回送收到的请求信息,主要用于测试或诊断

CONNECT: HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。

OPTIONS :请求查询服务器的性能,或者查询与资源相关的选项和需求

状态代码

状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:

100~199:指示信息,表示请求已接收,继续处理

200~299:请求成功,表示请求已被成功接收、理解、接受

300~399:重定向,要完成请求必须进行更进一步的操作

400~499:客户端错误,请求有语法错误或请求无法实现

500~599:服务器端错误,服务器未能实现合法的请求

常见的状态码如下:

200 OK:客户端请求成功

400 Bad Request:客户端请求有语法错误,不能被服务器所理解

401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用

403 Forbidden:服务器收到请求,但是拒绝提供服务

500 Internal Server Error:服务器发生不可预期的错误

503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常

HTTP的消息报头

消息报头分为通用报头请求报头响应报头实体报头等。

消息头由键值对组成,每行一对,关键字和值用英文冒号“:”分隔

通用报头

既可以出现在请求报头,也可以出现在响应报头中

Date:表示消息产生的日期和时间

Connection允许发送指定连接的选项

例如指定连接是连续的,或者指定“close”选项,通知服务器,在响应完成后,关闭连接

Cache-Control:用于指定缓存指令,缓存指令是单向的(响应中出现的缓存指令在请求中未必会出现),且是独立的(一个消息的缓存指令不会影响另一个消息处理的缓存机制)

请求报头

请求报头通知服务器关于客户端请求的信息,典型的请求头有:

Host请求的主机名,允许多个域名同处一个IP地址,即虚拟主机

User-Agent:发送请求的浏览器类型、操作系统等信息

Accept:客户端可识别的内容类型列表,用于指定客户端接收那些类型的信息

Accept-Encoding:客户端可识别的数据编码

Accept-Language:表示浏览器所支持的语言类型

Connection:允许客户端和服务器指定与请求/响应连接有关的选项,例如这是为Keep-Alive则表示保持连接。

Transfer-Encoding:告知接收端为了保证报文的可靠传输,对报文采用了什么编码方式

响应报头

用于服务器传递自身信息的响应,常见的响应报头:

Location:用于重定向接受者到一个新的位置,常用在更换域名的时候

Server:包含可服务器用来处理请求的系统信息,与User-Agent请求报头是相对应的

实体报头

实体报头用来定义被传送资源的信息,既可以用于请求也可用于响应

请求和响应消息都可以传送一个实体,常见的实体报头为:

Content-Type:发送给接收者的实体正文的媒体类型

Content-Lenght:实体正文的长度

Content-Language:描述资源所用的自然语言,没有设置则该选项则认为实体内容将提供给所有的语言阅读

Content-Encoding:实体报头被用作媒体类型的修饰符,它的值指示了已经被应用到实体正文的附加内容的编码,因而要获得Content-Type报头域中所引用的媒体类型,必须采用相应的解码机制。

Last-Modified:实体报头用于指示资源的最后修改日期和时间

Expires:实体报头给出响应过期的日期和时间

  1. http的get和post的区别

http是应用层的协议,底层基于TCP/IP协议,所以本质上,get和post请求都是TCP请求。所以二者的区别都是体现在应用层上(HTTP的规定和浏览器/服务器的限制)

1.参数的传输方式:GET参数通过URL传递,POST放在Request body中,当然由于GET/POST都是TCP链接。GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的。但是HTTP规定,当执行GET请求的时候,要贴上GET的标签(设置method为GET),而且要求把传送的数据放在url中以方便记录。如果是POST请求,就要贴上POST的标签,并把数据放在request body里。当然,你也可以在GET的时候往request body内偷偷藏点数据,但不同服务器的处理方式也是不同的,有些服务器会读出数据,有些服务器直接忽略,所以,虽然GET可以带request body,也不能保证一定能被接收处理;也可以在POST的时候在url上也放一些数据,让人觉得傻乎乎的,毕竟不安全。HTTP只是个行为准则,而TCP才是GET和POST怎么实现的基本

2.GET请求在URL中传送的参数是有长度限制的,2k,而POST没有,也有说是2g的。GET请求之所以限制2k,是因为数据量太大对浏览器和服务器都是很大负担。业界不成文的规定是,(大多数)浏览器通常都会限制url长度在2K个字节,而(大多数)服务器最多处理64K大小的url。超过的部分,恕不处理。

3.GET产生一个TCP数据包;POST产生两个TCP数据包,.对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。不过要注意,并不是所有浏览器都会在POST中发送两次包,比如火狐

4.对参数的数据类型,GET只接受ASCII字符,而POST没有限制。

5.GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。

6.GET请求只能进行url编码,而POST支持多种编码方式。

7.GET在浏览器回退时是无害的,而POST会再次提交请求。

8.GET产生的URL地址可以被Bookmark(书签),而POST不可以。

9.GET请求会被浏览器主动cache(缓存),而POST不会,除非手动设置。

  1. GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。

  2. socket和http的区别:

Http协议:简单的对象访问协议,对应于应用层。Http协议是基于TCP链接的。

tcp协议:对应于传输层

ip协议:对应与网络层

TCP/IP是传输层协议,主要解决数据如何在网络中传输;而Http是应用层协议,主要解决如何包装数据。

Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。

Http连接:http连接就是所谓的短连接,及客户端向服务器发送一次请求,服务器端响应后连接即会断掉。

socket连接:socket连接及时所谓的长连接,理论上客户端和服务端一旦建立连接,则不会主动断掉;但是由于各种环境因素可能会是连接断开,比如说:服务器端或客户端主机down了,网络故障,或者两者之间长时间没有数据传输,网络防火墙可能会断开该链接已释放网络资源。所以当一个socket连接中没有数据的传输,那么为了连续的连接需要发送心跳消息,具体心跳消息格式是开发者自己定义的。

10. https

HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。HTTP是应用层协议,位于HTTP协议之下是传输协议TCP。TCP负责传输,HTTP则定义了数据如何进行包装,在HTTP跟TCP中间加多了一层加密层TLS/SSL,SSL是个加密套件,负责对HTTP的数据进行加密。TLS是SSL的升级版。现在提到HTTPS,加密套件基本指的是TLS。

传输加密的流程:http是应用层将数据直接给到TCP进行传输,https是应用层将数据给到TLS/SSL,将数据加密后,再给到TCP进行传输。

HTTPS是如何加密数据的:

一般来说,加密分为对称加密、非对称加密

对称加密:

对称加密的意思就是,加密数据用的密钥,跟解密数据用的密钥是一样的。

对称加密的优点在于加密、解密效率通常比较高。缺点在于,数据发送方、数据接收方需要协商、共享同一把密钥,并确保密钥不泄露给其他人。传输过程中容易被截获。

网上一个很形象的例子:假如现在小客与小服要进行一次私密的对话,他们不希望这次对话内容被其他外人知道。可是,我们平时的数据传输过程中又是明文传输的,万一被某个黑客把他们的对话内容给窃取了,那就难受了。为了解决这个问题,小服这家伙想到了一个方法来加密数据,让黑客看不到具体的内容。该方法是这样子的:在每次数据传输之前,小服会先传输小客一把密钥,然后小服在之后给小客发消息的过程中,会用这把密钥对这些消息进行加密。小客在收到这些消息后,会用之前小服给的那把密钥对这些消息进行解密,这样,小客就能得到密文里面真正的数据了。如果小客要给小服发消息,也同样用这把密钥来对消息进行加密,小服收到后也用这把密钥进行解密。 这样,就保证了数据传输的安全性。

非对称加密

非对称加密的意思就是,加密数据用的密钥(公钥),跟解密数据用的密钥(私钥)是不一样的。

网上一个很形象的例子:小服还是挺聪明的,得意了一会之后,小服意识到了密钥会被截取这个问题。倔强的小服又想到了另外一种方法:用非对称加密的方法来加密数据。该方法是这样的:小服和小客都拥有两把钥匙,一把钥匙的公开的(全世界都知道也没关系),称之为公钥;而另一把钥匙是保密(也就是只有自己才知道),称之为私钥。并且,用公钥加密的数据,只有对应的私钥才能解密;用私钥加密的数据,只有对应的公钥才能解密。所以在传输数据的过程中,小服在给小客传输数据的过程中,会用小客给他的公钥进行加密,然后小客收到后,再用自己的私钥进行解密。小客给小服发消息的时候,也一样会用小服给他的公钥进行加密,然后小服再用自己的私钥进行解密。 这样,数据就能安全着到达双方。是什么原因导致非对称加密这种方法的不安全性呢?它和对称加密方法的不安全性不同。非对称加密之所以不安全,是因为小客收到了公钥之后,无法确定这把公钥是否真的是小服。

解决的办法就是数字证书:小服再给小客发公钥的过程中,会把公钥以及小服的个人信息通过Hash算法生成消息摘要,为了防止摘要被人调换,小服还会用CA提供的私钥对消息摘要进行加密来形成数字签名,当小客拿到这份数字证书之后,就会用CA提供的公钥来对数字证书里面的数字签名进行解密得到消息摘要,然后对数字证书里面小服的公钥和个人信息进行Hash得到另一份消息摘要,然后把两份消息摘要进行对比,如果一样,则证明这些东西确实是小服的,否则就不是。

11. 加密算法

对称加密算法

Data Encryption Standard(DES)

DES 是一种典型的块加密方法:将固定长度的明文通过一系列复杂的操作变成同样长度的密文,块的长度为64位。同时,DES 使用的密钥来自定义变换过程,因此算法认为只有持有加密所用的密钥的用户才能解密密文。 DES 的密钥表面上是64位的,实际有效密钥长度为56位,其余8位可以用于奇偶校验。

DES 现在已经不被视为一种安全的加密算法,主要原因是它使用的56位密钥过短。

为了提供实用所需的安全性,可以使用 DES 的派生算法 3DES 来进行加密 (虽然3DES 也存在理论上的攻击方法)。

Advanced Encryption Standard(AES)

AES 在密码学中又称 Rijndael 加密法,用来替代原先的 DES,已经被多方分析且广泛使用。

DES与AES的比较

自DES 算法公诸于世以来,学术界围绕它的安全性等方面进行了研究并展开了激烈的争论。在技术上,对DES的批评主要集中在以下几个方面:

1、作为分组密码,DES 的加密单位仅有64 位二进制,这对于数据传输来说太小,因为每个分组仅含8 个字符,而且其中某些位还要用于奇偶校验或其他通讯开销。

2、DES 的密钥的位数太短,只有56 比特,而且各次迭代中使用的密钥是递推产生的,这种相关必然降低密码体制的安全性,在现有技术下用穷举法寻找密钥已趋于可行。

3、DES 不能对抗差分和线性密码分析。

4、DES 用户实际使用的密钥长度为56bit,理论上最大加密强度为256。DES 算法要提高加密强度(例如增加密钥长度),则系统开销呈指数增长。除采用提高硬件功能和增加并行处理功能外,从算法本身和软件技术方面都无法提高DES 算法的加密强度。

非对称加密算法

RSA

1977年由 MIT 的 Ron Rivest、Adi Shamir 和 Leonard Adleman 一起提出,以他们三人姓氏开头字母命名,是一种获得广泛使用的非对称加密算法。

对极大整数做因数分解的难度 (The Factoring Problem) 决定了 RSA 算法的可靠性。换言之,对一个极大整数做因数分解愈困难,RSA 算法就愈可靠。假如有人找到一种快速因数分解的算法的话,那么用 RSA 加密的信息的可靠性就肯定会极度下降。目前看来找到这样的算法的可能性非常小。

DES与RSA的比较

RSA算法的密钥很长,具有较好的安全性,但加密的计算量很大,加密速度较慢限制了其应用范围。为减少计算量,在传送信息时,常采用传统加密方法与公开密钥加密方法相结合的方式,即信息采用改进的DES对话密钥加密,然后使用RSA密钥加密对话密钥和信息摘要。对方收到信息后,用不同的密钥解密并可核对信息摘要。

采用DES与RSA相结合的应用,使它们的优缺点正好互补,即DES加密速度快,适合加密较长的报文,可用其加密明文;RSA加密速度慢,安全性好,应用于DES 密钥的加密,可解决DES 密钥分配的问题。

目前这种RSA和DES结合的方法已成为EMAIL保密通信标准。

12. Volley

1、 Volley的特点

Volley是谷歌大会上推出的网络通信框架(2.3之前使用HttpClient,之后使用HttpUrlConnection),它既可以访问网络获取数据,也可以加载图片,并且在性能方面进行了大幅度的调整,它的设计目的就是适合进行数据量不大但通信频繁的网络操作,而对于大数据量的操作,比如文件下载,表现很糟糕,因为volley处理http返回的默认实现是BasicNetwork,它会把返回的流全部导入内存中,下载大文件会发生内存溢出

2、 Volley执行的过程:

默认情况下,Volley中开启四个网络调度线程和一个缓存调度线程,首先请求会加入缓存队列,,缓存调度线程从缓存队列中取出线程,如果找到该请求的缓存就直接读取该缓存并解析,然后回调给主线程,如果没有找到缓存的响应,则将这个请求加入网络队列,然后网络调度线程会轮询取出网络队列中的请求,发起http请求,解析响应并将响应存入缓存,回调给主线程

3、 Volley为什么不适合下载上传大文件?为什么适合数据量小的频率高的请求?

1.volley基于请求队列,Volley的网络请求线程池默认大小为4。意味着可以并发进行4个请求,大于4个,会排在队列中。并发量小所以适合数据量下频率高的请求

2.因为Volley下载文件会将流存入内存中(是一个小于4k的缓存池),大文件会导致内存溢出,所以不能下载大文件,不能上传大文件的原因和1中差不多,设想你上传了四个大文件,同时占用了volley的四个线程,导致其他网络请求都阻塞在队列中,造成反应慢的现象

13. OKHttp
OkHttp是安卓上常用的网络请求框架,不止可以发送http请求,还可以发送socket请求等。

内置了连接池,减少了请求延迟
支持缓存,减少重复的网络请求
支持Cookie存储
支持拦截器,可以对不同的请求做拦截处理
支持get、post等请求
支持文件上传下载
支持json请求
支持同步、异步处理

1.相较于Volley,它的最大并发量为64

2.使用连接池技术,支持5个并发的socket连接,默认keepAlive时间为5分钟,解决TCP握手和挥手的效率问题,减少握手次数

3.支持Gzip压缩,且操作对用户透明,可以通过header设置,在发起请求的时候自动加入header,Accept-Encoding: gzip,而我们的服务器返回的时候header中有Content-Encoding: gzip

4.利用响应缓存来避免重复的网络请求

5.很方便的添加拦截器,通常情况下,拦截器用来添加,移除,转换请求和响应的头部信息,比如添加公参等

6.请求失败,自动重连,发生异常时重连,看源码调用recover方法重连了一次

7.支持SPDY协议(SPDY是Google开发的基于TCP的应用层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。SPDY并不是一种用于替代HTTP的协议,而是对HTTP协议的增强。新协议的功能包括数据流的多路复用、请求优先级以及HTTP报头压缩。谷歌表示,引入SPDY协议后,在实验室测试中页面加载速度比原先快64%)

8.使用Okio来简化数据的访问与存储,提高性能

2、 OkHttp的缺点

1.消息回来需要切到主线程,主线程要自己去写。

2.调用比较复杂,需要自己进行封装。

3.缓存失效:网络请求时一般都会获取手机的一些硬件或网络信息,比如使用的网络环境。同时为了信息传输的安全性,可能还会对请求进行加密。在这些情况下OkHttp的缓存系统就会失效了,导致用户在无网络情况下不能访问缓存。

3、 OkHttp框架中都用到了哪些设计模式

1.最明显的Builder设计模式,如构建对象OkHttpClient,还有单利模式

2.工厂方法模式,如源码中的接口Call

3.观察者模式如EventListener,监听请求和响应

4.策略模式

5.责任链模式,如拦截器

<meta charset="utf-8">

一:okhttp是什么

   大家都知道HTTP是现代应用网络请求的方式,是一个简单的请求-响应协议。在数据和媒体中进行交换。有效地请求使您的东西加载更快,并节省带宽。OKHttp是安卓开发中十分常用的网络请求框架。

二:为什么使用

  1:缓存响应数据来减少重复的网络请求

   2:可以从很多常用的连接问题中自动恢复;(Okhttp处理了很多的网络疑难杂症:会从很多常用的连接问题中自动恢复。如果您的服        务器配置了多个IP地址,当第一个IP连接失败时,OkHttp会尝试连接下一个IP。)

   3:支持gzip压缩响应体

   4:使用简单,可扩展性非常的强;(责任链模式使得很容易添加一个自定义拦截器对请求和返回结果进行处理)

三:用来干什么

  1:支持一般的get请求,post请求

   2:基于Http的文件上传,文件下载,上传下载的进度回调

   3:图片加载

   4:支持请求回调,对象返回

   5:   表单请求

四:请求流程

   1:生成一个OkHttpClient(用以总的控制)

   2:用各种键值对包装我们的Request

   3:将请求加入队列生成一个Call管理请求

   4:若是同步请求直接执行excute等待处理返回Response,若为异步则实现Callback回调,在onResponse里获取Response参数

五:同步与异步的实现

在发起请求时,整个框架主要通过Call来封装每一次的请求。同时Call持有OkHttpClient和一份HttpEngine。而每一次的同步或者异步请求都会有Dispatcher的参与。

Dispatcher

Dispatcher内部实现了懒加载无边界限制的线程池方式,同时该线程池采用了 SynchronousQueue这种阻塞队列。SynchronousQueue每个插入操作必须等待另一个线程的移除操作,同样任何一个移除操作都等待另一个线程的插入操作。因此此队列内部其实没有任何一个元素,或者说容量是0,严格说并不是一种容器。由于队列没有容量,因此不能调用peek操作,因为只有移除元素时才有元素。显然这是一种快速传递元素的方式,也就是说在这种情况下元素总是以最快的方式从插入者(生产者)传递给移除者(消费者),这在多任务队列中是最快处理任务的方式。对于高频繁请求的场景,无疑是最适合的。

同步

Dispatcher会在同步执行任务队列中记录当前被执行过得任务Call,同时在当前线程中去执行Call的getResponseWithInterceptorChain()方法,直接获取当前的返回数据Response;

异步

异步执行是通过Call.enqueue(Callback responseCallback)来执行,在Dispatcher中添加一个封装了Callback的Call的匿名内部类Runnable来执行当前 的Call。这里一定要注意的地方这个AsyncCall是Call的匿名内部类。AsyncCall的execute方法仍然会回调到Call的 getResponseWithInterceptorChain方法来完成请求,同时将返回数据或者状态通过Callback来完成。

六:拦截器

拦截器可以注册为应用拦截器和网络拦截器。假如有一个压缩拦截器和一个检验和拦截器:你需要决定是先数据进行压缩然后检验和,还是先检验和然后进行压缩。OkHttp使用列表来跟踪拦截器,并且拦截器按顺序被调用。

应用拦截器

1:不需要关心像重定向和重试这样的中间响应。

2:总是调用一次,即使HTTP响应从缓存中获取服务。

3:监视应用原始意图。不关心OkHttp注入的像If-None-Match头。

4:允许短路并不调用Chain.proceed()。

5:允许重试并执行多个Chain.proceed()调用。

网络拦截器

1:可以操作像重定向和重试这样的中间响应。

2:对于短路网络的缓存响应不会调用。

3:监视即将要通过网络传输的数据。

4:访问运输请求的Connection。

七:线程池技术

相比我们对于异步任务的需求应该遇到了不少,首先想到的便是Thread,handler等异步机制,Java已经做了很好的封装,但是当我们需要使用许多异步任务,而这些任务只做了一点点事情就完成了它的使命,当我们不断的创建,销毁线程的时候,对系统的开销是相当大的,因为这些过程也是耗费时间的,这个时候我们就需要用到线程池了,我们只要往池子里放任务,他就会自动帮你管理线程的创建与销毁,利用缓存复用减少线程销毁创建,优化系统性能。以下是线程池的优点:

1:通过对线程进行缓存,减少了创建销毁的时间损失

2:通过控制线程数量阀值,减少了当线程过少时带来的CPU闲置(比如说长时间卡在I\O上了)与线程过多时对JVM的内存与线程切换压力而Disatcher内部的核心即线程池

int corePoolSize: 最小并发线程数,这里并发同时包括空闲与活动的线程,如果是0的话,空闲一段时间后所有线程将全部被销毁。

int maximumPoolSize: 最大线程数,当任务进来时可以扩充的线程最大值,当大于了这个值就会根据丢弃处理机制来处理

long keepAliveTime: 当线程数大于corePoolSize时,多余的空闲线程的最大存活时间,类似于HTTP中的Keep-alive

TimeUnit unit: 时间单位,一般用秒

BlockingQueue workQueue: 工作队列

ThreadFactory threadFactory: 单个线程的工厂,可以打Log,设置Daemon(即当JVM退出时,线程自动结束)等

OkHttp内Dispatcher原理其实当我们调用client.newCall(request).enqueue(new callback(){...})的时候实质是下图

反向代理模式

为了解决单生产者多消费者问题,OkHttp采用了反向代理模式,来解决非阻塞,高并发问题

Dispatch模式

OkHttp内Dispatcher原理其实当我们调用client.newCall(request).enqueue(new callback(){...})的时候实质是判断线程池内有无空闲,否则进入等待队列,AsyncCall实质是Runnable,当任务执行完毕后,总会调用finally{client.dispatcher().finished(this);}清除此任务。我们回头看看OkhttpClient初始化内其它一些参数Expires,Cache-Control,Last-Modified,If-Modified-Since,ETag,If-None-Match...这些都牵扯到缓存问题。

Response response = getResponseWithInterceptorChain();

其实这个方法是返回我们的response对象。到这里整个流程已经完毕。

使用OkHttpClient 的时候需要****注意****以下几点:

1:最好只使用一个共享的OkHttpClient 实例,将所有的网络请求都通过这个实例处理。因为每个OkHttpClient 实例都有自己的连接池和线程池,重用这个实例能降低延时,减少内存消耗,而重复创建新实例则会浪费资源。

2:OkHttpClient的线程池和连接池在空闲的时候会自动释放,所以一般情况下不需要手动关闭,但是如果出现极端内存不足的情况,可以使用以下代码释放内存:

** client.dispatcher().executorService().shutdown(); //清除并关闭线程池**

** client.connectionPool().evictAll(); //清除并关闭连接池**

** client.cache().close(); **

3:如果对一些请求需要特殊定制,可以使用

OkHttpClient eagerClient = client.newBuilder()

** .readTimeout(500, TimeUnit.MILLISECONDS)**

** .build();**

这样创建的实例与原实例共享线程池、连接池和其他设置项,只需进行少量配置就可以实现特殊需求。

Request

Request是网络请求的对象,其本身的构造函数是private的,只能通过Request.Builder来构建。下面代码展示了常用的设置项。

Request request = new Request.Builder()

    .url("https://api.github.com/repos/square/okhttp/issues")                         //设置访问url

    .get()  //类似的有post、delete、patch、head、put等方法,对应不同的网络请求方法

    .header("User-Agent", "OkHttp Headers.java")                                       //设置header

    .addHeader("Accept", "application/json; q=0.5")                                     //添加header

    .removeHeader("User-Agent")                                                                //移除header

    .headers(new Headers.Builder().add("User-Agent", "OkHttp Headers.java").build())   

     //移除原有所有header,并设置新header

    .addHeader("Accept", "application/vnd.github.v3+json")

    .build();                                                                                                   //构建request

RequestBody

在Request中使用post、patch等方法时,需要传入一个RequestBody参数,除了上一节讲到的构造方法外,RequestBody还有两个子类:FormBody和MultipartBody。

FromBody用于提交表单键值对,其作用类似于HTML中的<form>标记。

RequestBody formBody = new FormBody.Builder() //提交表单键值对

    .add("platform", "android")                                                            //添加键值对

    .add("name", "XXX")

    .add("subject", "Hello")

    .addEncoded(URLEncoder.encode("详细","GBK"), URLEncoder.encode("无","GBK"))

    //添加已编码的键值对

    .add("描述","你好")          //其实会自动编码,但是无法控制编码格式

    .build();

使用MultipartBody.Builder可以构建与HTML文件上传格式兼容的复杂请求体。多块请求体中每块请求都是一个独立的请求体,都可以定义自己的请求头。这些请求头应该用于描述对应的请求体,例如Content-Disposition,Content-Length,和Content-Type会自动被添加到请求头中。

Call

Call对象表示一个已经准备好可以执行的请求,用这个对象可以查询请求的执行状态,或者取消当前请求。它具有以下方法:

Call call=client.newCall(request); //获取Call对象

Response response=call.execute(); //同步执行网络请求,不要在主线程执行

call.enqueue(new Callback()); //异步执行网络请求

call.cancel(); //取消请求

call.isCanceled(); //查询是否取消

call.isExecuted(); //查询是否被执行过

要注意的是,每个Call对象只能执行一次请求。如果想重复执行相同的请求,可以:

Call reCall=client.newCall(call.request()); //获取另一个相同配置的Call对象

Response

Response是网络请求的结果下面是一些常用方法:

Response response=call.execute(); //获取Response对象

response.code(); //请求的状态码

response.isSuccessful(); //如果状态码为[200..300),则表明请求成功

Headers headers=response.headers(); //获取响应头

List<String> names=response.headers("name"); //获取响应头中的某个字段

ResponseBody body=response.body(); //获取响应体

其中ResponseBody代表响应体,用于操作网络请求返回的内容。常用方法如下:

body.contentLength(); //body的长度

String content=body.string(); //以字符串形式解码bodybyte[] byteContent=body.bytes(); //以字节数组形式解码body

InputStreamReader reader=body.charStream(); //将body以字符流的形式解码

InputStream inputStream=body.byteStream(); //将body以字节流的形式解码

ResponseBody

1:ResponseBody必须关闭,不然可能造成资源泄漏,你可以通过以下方法关闭ResponseBody,对同一个ResponseBody只要关闭一次就可以了。

Response.close();

Response.body().close();

Response.body().source().close();

Response.body().charStream().close();

Response.body().byteString().close();

Response.body().bytes();

Response.body().string();

2:ResponseBody只能被消费一次,也就是string(),bytes(),byteStream()或 charStream()方法只能调用其中一个。

3:如果ResponseBody中的数据很大,则不应该使用bytes() 或 string()方法,它们会将结果一次性读入内存,而应该使用byteStream()或 charStream(),以流的方式读取数据。

八:OkHttp中的设计模式

1:责任链模式:拦截器链

2:单例模式:线程池

3:观察者模式:各种回调监听

4:策略模式:缓存策略

5:Builder模式:OkHttpClient的构建过程

作者:小阿飞的小蝴蝶
链接:Android okhttp详解 - 简书
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

14. Retrofit

Retrofit底层是基于OkHttp实现的,与其他网络框架不同的是,它更多使用运行时注解的方式提供功能

1、 原理

通过java接口以及注解来描述网络请求,并用动态代理的方式生成网络请求的request,然后通过client调用相应的网络框架(默认okhttp)去发起网络请求,并将返回的response通过converterFactorty转换成相应的数据model,最后通过calladapter转换成其他数据方式(如rxjava Observable)

2、 Retrofit流程

(1)通过解析网络请求接口的注解配置网络请求参数

(2)通过 动态代理 生成 网络请求对象

(3)通过 网络请求适配器将网络请求对象 进行平台适配

(4)通过 网络请求执行器 发送网络请求

(5)通过 数据转换器 解析服务器返回的数据

(6)通过 回调执行器 切换线程(子线程 ->>主线程)

(7)用户在主线程处理返回结果

3、 Retrofit优点

1.可以配置不同HTTP client来实现网络请求,如okhttp、httpclient等;

2.请求的方法参数注解都可以定制;

3.支持同步、异步和RxJava;

4.超级解耦;

5.可以配置不同的反序列化工具来解析数据,如json、xml等

6.框架使用了很多设计模式



作者:程序农猿
链接:https://www.jianshu.com/p/1eb2954d6da8
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。




 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值