UDP

udp协议属于传输层协议。

udp报文协议

16位源端口号 16位目的端口号
16位UDP长度 16为UDP校验和
数据内容 数据内容

0 15 16 31

源端口:065535,11024为保留端口,为标准的服务端口

UDP长度:header + data 总长度

UDP校验和:伪头部、头部、data 三部分的校验和

数据内容:上层应用的数据,可以没有数据

  • 若校验和失败,就会丢弃数据

UDP协议的特点

  • 无链接

    只需知道对端的IP和PORT就可以发送数据,不需要建立链接

  • 不可靠

    没有确认机制,没有重传机制。若因为网络原因没有发送到对端,UDP协议层也不会返回任何错误给应用层。

  • 面向数据报

    应用层交给UDP多长的报文,UDP就会直接传送过去,不会进行拆分。接收时也是一次性接受全部传送的数据报。

  • UDP存在接收缓冲区,单步存在发送缓冲区

    UDP在发送数据时没有缓冲区,会直接将数据传递给内核,内核会直接调用网卡进行传输。因为UDP不需要保证可靠机制,所以报文丢失之后不需要再重新发送。而UDP虽然有接受缓冲区,但是不会保证收到的数据顺序,缓冲区满了之后就会将新接收的报文丢弃。

  • 双全工通信

    UDP可同时接收和发送数据报文。

UDP协议首部有一个16位的长度,说明UDP最长可以发送64k数据(包含首部),超过之后就需要我们手工在应用层分包。

常见的UDP协议

  • NFS:网络文件传输协议
  • TFTP:简单文件传输协议
  • DHCP:动态主机配置协议
  • DNS:域名解析协议
  • 自定义的UDP协议等

TCP

TCP全称为传输控制协议,必需对数据的传输进行控制。

tcp协议报文格式

  • 源/目的端口:标识数据从哪儿来要去哪儿

  • 32位序号:序号保证了可靠传输。TCP将传输的每一个字节都编号了,序号是本报文段数据的第一个字节编号。例如:一个报文段序号是300,其数据部分共有100字节,则下一个报文段序号为401。

  • 32位徐仁序列号:每一个ACK对应这个确认号,它指明下一个期待收到的字节序号。其表明之前收到的所有数据都已经正确无误。确认号只有当ACK标志为1的时候才有效。建立连接时,SYN报文的ACK标志为0

  • 4位首部长度(数据偏移):表示该TCP头部有多少个32位bit,所以TCP头部大长度是15*4=60。TCP默认报文大小为20字节。

  • 6个标志位:

    • URG:它为了标志紧急指针是否有效。
    • ACK:标识确认号是否有效。
    • PSH:提示接收端应用程序立即将接收缓冲区的数据拿走。
    • RST:它是为了处理异常连接的, 告诉连接不一致的一方,我们的连接还没有建立好, 要求对方重新建立连接。我们把携带RST标识的称为复位报文段。
    • SYN:请求建立连接;把携带SYN标识的称为同步报文段。
    • FIN:通知对端本段要关闭链接了;把携带FIN标识的称为结束报文段。
  • 16位紧急指针:当有些报文想优先被处理时(原来是按序处理),设置紧急指针指向该报文,同时将紧急指针有效位置1。

  • 16位窗口大小:若发送方发送速度大于接收方发送速度(比如发送方发送大量数据),这时可能会导致大量数据丢失,接收方可以发送消息给发送方让其发送慢一点,这就是流量控制。接收方将自己接受缓冲器剩余空间的大小告诉发送方叫做16位窗口大小。窗口大小最大是2^16^,也就是64k。

  • 16位校验和:发送端填充,CRC校验。

TCP确认应答机制

1
2
3
4
5
6
7
sequenceDiagram
participant 主机A
participant 主机B
主机A->>主机B:数据:1~1000
主机B->>主机A:确认应答,下一个是1001
主机A->>主机B:数据:1001~2000
主机B->>主机A:确认应答,下一个是2001

发送端放一条数据到接收端,接收端在收到一条报文后,向发送端发送一条ACK确认报文,告诉发送端已经成功的接收到了消息,并且希望收到的下一个序列号是多少。这个确认号就是下一个报文的序列号。

超时重传机制

注:虚线标识未可达,实线表示到达。

1
2
3
4
5
6
7
8
sequenceDiagram
participant 主机A
participant 主机B
主机A-->>主机B:数据:1~1000
Note right of 主机A:数据1到1000未到达主机B
Note left of 主机A:一段特定的时间间隔过后
主机A->>主机B:数据:1~1000
主机B->>主机A:确认应答,下一个是1001

在传输过程中的超时重传机制。A给B发送数据,若A在一定时间没有收到B的确认应答消息,会进行重发。其中可能存在B没有收到A的消息,这时B不会应答;也可能是B的应答A没有收到,如下:

1
2
3
4
5
6
7
8
9
sequenceDiagram
participant A
participant B
A->>B:数据:1~1000
B-->>A:确认应答,下一个是1001
Note right of A:未收到B发送的确认应答消息
Note left of A:一段时间间隔之后
A->>B:数据:1~1000
B->>A:确认应答,下一个是1001

此时主机B就会收到很多重复的包,可以利用前面的16位序列号将重复的包识别出来并丢弃,这样就做到了数据去重的效果。

超时时间的确认:

​ 若超时时间间隔设置的太长,会影响整体重传的效率;若设置的时间间隔太短,就可能会频繁的发送重复的包。TCP为了保证不同的网络传输效率的高效,会动态的计算这个时间间隔。

​ 在linux中,以500ms为一个时间单位进行控制。若超过500ms没有收到应答,则重发一次;若重发一次之后仍未等待到确认应答,则等待2*500ms进行重发,下一次重发为4*500ms。依次类推,成指数级增长。当累计到一定次数,则认为网络或者对端主机出现异常,关闭链接。

三次握手与四次挥手

三次握手

三次握手也就是建立连接的过程

1
2
3
4
5
6
7
8
9
10
11
12
sequenceDiagram
participant Client
participant Server
Note over Client,Server:CLOSED
Note over Server:LISTEN
Client ->> Server:连接请求:SYN=1,seq=x
Note over Client:SYN-SENT
Server ->> Client:SYN=1,ACK=1,seq=y,ack=x+1
Note over Server:SYN-RCVD
Client ->> Server:ACK=1,seq=x+1,ack=y+1
Note over Client,Server:ESTABLISHED
Note left of Server:数据传输开始
  1. 服务器由CLOSED状态转换为LISTEN(监听)状态。
  2. 客户端发送连接请求报文到服务端。此时报文中的同步标志位SYN置为1,选择一个初始序列号seq=x,并将客户端置为SYN-SENT(同步已发送态)状态。注意,TCP规定SYN不能携带数据,但是会消耗一个序列号。
  3. TCP服务端收到报文,如果同意连接则发送应答确认报文。其中,确认报文的SYN=1,ACK=1,确认序列号为x+1,服务端也会初始化生成一个序列号y,此时服务端进入SYN-RCVD(同步收到态)状态。这个报文同样不能携带数据,但是也会消耗一个序列号。
  4. TCP客户端收到确认报文后,还需要向服务器应答一个确认报文(为了链接的可靠性)。其中,ACK=1,确认序列号ack=y+1,自己的序列号req=x+1。
  5. 连接建立,CS之间可以相互通信收发数据。

第三次握手的意义:

​ 主要是为了防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送的第一个请求连接并且没有丢失,只是因为在网络中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时之前滞留的那一次请求连接,因为网络通畅了, 到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的费。

​ 如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

四次挥手

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sequenceDiagram
participant Client
participant Server
Note over Client,Server:ESTABLISHED
Note left of Client:主动关闭链接
Client ->> Server:连接请求:FIN=1,seq=u
Note over Client:FIN-WAIT1
Server ->> Client:ACK=1,seq=v,ack=u+1
Note over Server:CLOSE-WAIT
Note over Client:FIN-WAIT2
Server ->> Client:数据传输
Server ->> Client:FIN=1,ACK=1,seq=w,ack=u+1
Note over Server:LAST-ACK
Client ->> Server:ACK=1,seq=u+1,ack=w+1
Note over Client:TIME-WAIT (2MSL)
Note over Client,Server:CLOSED

在建立连接后,两边都可以断开连接。

此时客户端和服务器都是处于ESTABLISHED状态,然后客户端主动断开连接,服务器被动断开连接。

  1. 客服端发送释放连接报文FIN,这时客户端不再发送数据。此时FIN=1,序列号seq=u(等于前面已发送的数据的最后一个字节的序号加1),客户端进入FIN-WAIT-1状态。TCP规定及时FIN报文不携带数据也要消耗一个序列号。
  2. 服务端收到FIN报文,发出确认报文ACK。其中ACK=1,确认序列号ack=u+1,并且带上自己的序列号v。此时服务端进入CLOSE-WAIT(关闭等待)状态。 此时, TCP服务器会通知上层的应用,客服端向服务器方向释放了连接,处于半关闭状态,即客户端没有数据需要发送了但服务端可以发送数据到客户端让客户端处理。
  3. 客服端收到服务端发送的ACK报文后,客户端进入FIN-WAIT-2状态。等待服务端发送FIN释放报文。
  4. 服务端将所有数据发送完之后,向客户端发送FIN释放报文。其中,FIN=1,ACK=1,此时自己的序列号seq=w,确认序列号ack=u+1。此时服务端进入了LAST-ACK(最终确认)状态,等待客户端的确认报文。
  5. 客户端收到服务端的FIN释放报文,必需发出确认ACK报文。其中ACK=1,确认序列号ack=w+1,自己的序列号req=u+1。此时客户端进入了TIME-WAIT(时间等待)状态,必需经过2*MSL(最长报文寿命时间段)之后才能进入CLOSED状态。

第四次握手为什么要等待2MSL时长

  • ACK报文的可靠性

    保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。

  • 去除无效报文

    防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。

为什么握手要三次,挥手要四次

​ 建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。

​ 而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送,从而导致多了一次。

建链之后,客户端故障了怎么处理

​ TCP设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

此种机制类似心跳检测机制。

TIME-WAIT状态解读

​ 当我们实现一个TCP服务器时,我们把这个服务器运行起来然后再将服务器关闭掉,再次重新启动服务器会发现一个问题:就是不能马上再次绑定这个端口号和ip,需要等一会才可以重新绑定,其实等的这一会就是TIME_WAIT状态。

  • TCP协议规定主动关闭连接的一方要处于TIME_ WAIT状态,等待两个MSL的时间后才能回到CLOSED状态。
  • 当我们使用Ctrl-C终止了server,server是主动关闭连接的一方在TIME_WAIT期间仍然不能再次监听同样的server端口。
  • MSL在RFC1122中规定为两分钟(120s),但是各操作系统的实现不同,在Centos7上默认配置的值是60s可以通过cat /proc/sys/net/ipv4/tcp_fin_timeout查看MSL的值。

解决TIME_WAIT状态引起的bind失败的方法:

​ 在server的TCP连接没有完全断开之前不允许重新绑定,也就是TIME_WAIT时间没有过,但是这样不允许立即绑定在某些情况下是不某些特定场景的:

  • 服务器需要处理非常大量的客户端的连接 (每个连接的生存时间可能很短,但是每秒都有很大数量的客户 端来请求)
  • 这个时候如果由服务器端主动关闭连接(比如某些客户端不活跃,就需要被服务器端主动清理掉),这样服务器端就会产生大量TIME_WAIT状态。
  • 如果客户端的请求量很大,就可能导致TIME_WAIT的连接数很多,每个连接都会占用一个通信五元组(源ip, 源端口, 目的ip, 目的端口, 协议)。其中服务器的ip和端口和协议是固定的,如果新来的客户端连接的ip和端口号和TIME_WAIT占用的连接重复就造成等待。

解决方法:使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1,表示允许创建端口号相同但IP地址不同的多个socket描述符

滑动窗口机制

确认应答策略对每一个发送的数据段都要给一个ACK确认应答,接收方收到ACK后再发送下一个数据段,但是这样做有一个比较大的缺点,就是性能较差,尤其是数据往返的时间较长的时候。

​ 考虑一次发送多条数据,它是将多个段的等待时间重叠在一起。

​ 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。若需要发送12个报文段。发送前四个段的时候,不需要等待任何ACK直接发送即可。当收到第一个ACK后滑动窗口向后移动,继续发送第五个段的数据,然后依次类推。操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答。只有确认应答过的数据,才能从缓冲区删掉,窗口越大,则网络的吞吐率就越高。滑动窗口左边代表已经发送过并且确认,可以从发送缓冲区中删除了,滑动窗口里边代表发送出去但是没有确认,滑动窗口右边代表还没有发送的数据。

快重传

若出现丢包现象该怎么处理呢?

  1. 数据到达接收方但丢失了应答报文?

    ​ 可以根据后面的应答ACK确认。假设发送了1~1000的数据,接收方接收到了但是返回的应答报文丢失。发送方继续发送1001——2000收到确认ACK 2001,则认为1-1000发送成功并成功接收了。

  2. 数据包之间丢失报文?

    当某一段报文1001-2000丢失后,发送端会一直收到1001的这样的ACK确认(就像在提醒发送端我想要的报文是1001开头),若发送端连续三次收到这样的确认ACK,就会将1001-2000报文重发。这个时候接收端收到1001报文之后就会收到当前的报文段6001(假设前面发送到6000了,被放到了内核缓冲区中)。这种机制就叫做快重传

    ​ 快重传要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。由于不需要等待设置的重传计时器到期,能尽早重传未被确认的报文段,能提高整个网络的吞吐量。

流量控制

​ 因为接收端的处理数据能力有限,因各种原因接收端的缓冲区被填满,如果这个时候发送端继续发送就会发生丢包现象,然后引起丢包重传等一系列连锁反应。因此要根据接收端处理数据的能力来控制发送端的发送速度。这就叫流量控制

  1. 接收端将自己可以接受的缓冲区大小放在TCP首部的窗口大小字段,通过ACK确认应答报文通知发送端。
  2. 窗口大小越大,说明吞吐量越大,当缓冲区快满了的时候,就会将窗口值设置为一个更小的值通知给发送端。
  3. 当窗口大小为0之后,发送端在接收到ACK确认报文之后就不会发送数据了。但是会定期发送一个窗口探测数据端(防止缓冲区满了之后无法继续通信),使接收端把窗口大小发送给发送端。

象,然后引起丢包重传等一系列连锁反应。因此要根据接收端处理数据的能力来控制发送端的发送速度。这就叫流量控制

  1. 接收端将自己可以接受的缓冲区大小放在TCP首部的窗口大小字段,通过ACK确认应答报文通知发送端。
  2. 窗口大小越大,说明吞吐量越大,当缓冲区快满了的时候,就会将窗口值设置为一个更小的值通知给发送端。
  3. 当窗口大小为0之后,发送端在接收到ACK确认报文之后就不会发送数据了。但是会定期发送一个窗口探测数据端(防止缓冲区满了之后无法继续通信),使接收端把窗口大小发送给发送端。

​ 在TCP首部的窗口大小字段,大小为16位,数据大小为65535,但由于TCP首部还有40字节的选项中包含了一个窗口扩大因子M,所以实际实际大小是窗口字段左移M位。

拥塞控制

​ 拥塞控制是TCP通信的每一方需要执行的一系列行为。这些行为是由一些特定的算法规定,用于防止网络因为大规模的通信而瘫痪。其基本方法是可以认为网络即将进入拥塞状态或已经拥塞而出现的丢包情况出现时,减缓TCP传输数据。其难点在于怎样精准的判断何时需要减缓且如何减缓TCP传输、何时恢复原有的速度。

TCP拥塞检测

​ 典型的TCP拥塞检测通常看是否有丢包出现,丢包在TCP中被用作判断拥塞发生与否指标。当然也有其他的一些方法,如时延测量、显式拥塞通知等等。

慢启动算法

​ 若网络上有很多的计算机,可能当前的网络状态就已经比较拥堵,在不清楚当前网络状态下,贸然发送大量的数据是很有可能引起雪上加霜的,造成网络更加堵塞,所以需要缓慢探测可用传输资源,防止短时间内大量数据注入导致拥塞甚至瘫痪。慢启动算法就是为了这一目的设计而出的。在数据传输之初或重传计时器检测到丢包后执行慢启动,直到有丢包时执行拥塞避免算法。

​ 注:慢启动算法和拥塞避免算法在同一时刻只会运行其中一个,但两者可相互切换。

cwnd为拥塞窗口大小,在发送开始的时候定义拥塞窗口大小为1,每次收到一个ACK应答拥塞窗口加1。每次发送数据包的时候,将拥塞窗口接收端主机反馈的窗口大小做比较,取较小的值作为实际发送的窗口。cwnd是随时间以指数增长的。

"慢启动"只是指初使时慢,但是增长速度非常快。为了不增长的那么快,因此不能使拥塞窗口单纯的加倍,此处引入一个叫做慢启动的阈值当拥塞窗口超过这个阈值的时候,不再按照指数方式增长, 而是按照线性方式增长。这个阈值是慢启动阶段至拥塞避免阶段的转折点。

  • TCP开始启动的时候,慢启动阈值等于窗口最大值
  • 在每次超时重发的时候,慢启动阈值会变成原来的一半同时拥塞窗口置回1

拥塞避免算法

​ 在慢启动阶段,cwnd会快速增长,帮助确定一个慢启动阈值。一旦到达这阈值,意味着可能有更多的资源可被传输。若全部占用这些资源,将会使共享路由器队列的其他链接出现严重的丢包和重传情况,从而导致整个网络的不稳定。为了获取更多资源而不影响其他的连接传输,引入了拥塞避免算法。一旦进入慢启动阈值,TCP就会进入拥塞避免阶段,cwnd每次增长值近似于成功传输的数据段大小。这种随时间接近线性增长。

拥塞控制与流量控制

​ 拥塞控制是防止过多的流量进入网络中,使网络中的路由器或链路不至于过载,是一个全局性的过程。流量是点对点的通信量的控制,是一个端到端的控制问题,主要权衡发送端发送数据的速度,以便接收端能够接收。

​ 无论是在慢启动阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认,虽然没有收到确认可能是其他原因的分组丢失,但是因为无法判定,所以都当做拥塞来处理),这时就把慢启动门限设置为出现拥塞时的门限的一半。然后把拥塞窗口设置为1,执行慢启动算法。

* 加法增大:执行`拥塞避免`算法后,拥塞窗口`线性缓慢`增大,防止网络过早出现拥塞
* 乘法减小:无论是慢启动阶段还是拥塞避免,只要出现了网络拥塞(超时),那就把慢启动门限值`ssthresh`减半

延迟应答

​ 如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小。假设接收端缓冲区为1M 一次收到了500K的数据。如果立刻应答,返回的窗口就是500K。 但实际上可能处理端处理的速度很快,10ms之内就把500K数据从缓冲区消费掉了,在这种情况下,接收端处理还远没有达到自己的极限,即使窗口再放大一些也能处理过来。如果接收端稍微等一会再应答,比如等待200ms再应答,那么这个时候返回的窗口大小就是1M。

​ 窗口越大,网络吞吐量就越大,传输效率就越高。我们的目标是在保证网络不拥塞的情况下尽量提高传输效率。

  • 数量限制: 每隔N个包就应答一次

  • 时间限制: 超过大延迟时间就应答一次

    捎带应答:

    ​ 在延迟应答的基础上,存在很多情况下,客户端服务器在应用层也是”一发一收” 的。 意味着客户端给服务器说了”How are you”, 服务器也会给客户端回一个”Fine, thank you”。那么这个时候ACK就可以搭顺风车,和服务器回应的 “Fine, thank you” 一起回给客户端。

面向字节流

​ 当我们创建一个TCP的socket,同时在内核中创建一个发送缓冲区和一个接收缓冲区。

  • 调用write时,内核将数据会先写入发送缓冲区中,如果发送的字节数太长,会被拆分成多个TCP的数据包发出,如果发送的字节数太短,就会先在缓冲区里等待, 等到缓冲区长度达到设置长度,然后等到其他合适的时机发送出去。

  • 调用read接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区。然后应用程序可以调用read从接收缓冲区拿数据。TCP的一个连接,既有发送缓冲区, 也有接收缓冲区,那么对于这一个连接,既可以读数据,也可以写数据。所以是全双工的。

​ 由于缓冲区的存在,TCP程序的读和写不需要一一匹配。例如: 写100个字节数据时, 可以调用一次write写100个字节, 也可以调用100次write, 每次写一个字节; 读100个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以一次read 100个字节, 也可以一次 read一个字节, 重复100次。

粘包问题

​ 粘包问题中的 “包”是指的应用层的数据包。在TCP的协议头中,没有如同UDP一样的 “报文长度”这样的字段,但是有一个序号这样的字段。站在传输层的角度, TCP是一个一个报文过来的,按照序号排好序放在缓冲区中,但是站在应用层的角度,它看到的只是一串连续的字节数据。应用程序看到了这么一连串的字节数据, 就不知道从哪个部分开始到哪个部分结束是一个完整的应用层数据包,这就是粘包问题。

​ 如何解决:

  • 对于定长的包,保证每次都按固定大小读取即可。例如一个Request结构, 是固定大小的, 那么就从缓冲区从头开始按sizeof(Request)依次读取即可。
  • 对于变长的包,可以在包头的位置,约定一个包总长度的字段,从而就知道了包的结束位置。
  • 对于变长的包,还可以在包和包之间使用明确的分隔符(应用层协议是程序员自己来定义的, 只要保证分隔符不和正文冲突即可)。

层的角度, TCP是一个一个报文过来的,按照序号排好序放在缓冲区中,但是站在应用层的角度,它看到的只是一串连续的字节数据。应用程序看到了这么一连串的字节数据, 就不知道从哪个部分开始到哪个部分结束是一个完整的应用层数据包,这就是粘包问题。

​ 如何解决:

  • 对于定长的包,保证每次都按固定大小读取即可。例如一个Request结构, 是固定大小的, 那么就从缓冲区从头开始按sizeof(Request)依次读取即可。
  • 对于变长的包,可以在包头的位置,约定一个包总长度的字段,从而就知道了包的结束位置。
  • 对于变长的包,还可以在包和包之间使用明确的分隔符(应用层协议是程序员自己来定义的, 只要保证分隔符不和正文冲突即可)。

对于UDP协议,如果还没有上层交付数据, UDP的报文长度仍然在。 同时UDP是一个一个把数据交付给应用层,这样就有存在明确的数据边界,站在应用层的角度, 使用UDP的时候要么收到完整的UDP报文要么不收,不会出现”半个”的情况。

HTTP与HTTPS区别

  1. https需要到ca申请证书,申请证书需要money;
  2. http是超文本传输协议,信息是明文传输,https则是使用SSL/STL加密传输协议,安全性较高;
  3. http使用80端口,https使用443端口;
  4. http是无状态连接,https是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

HTTPS工作原理

  1. 客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。
  2. Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
  3. 客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
  4. 客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
  5. Web服务器利用自己的私钥解密出会话密钥。
  6. Web服务器利用会话密钥加密与客户端之间的通信。

https缺点:

  • HTTPS协议握手阶段比较费时,会使页面的加载时间延长近50%,增加10%到20%的耗电;

  • HTTPS连接缓存不如HTTP高效,会增加数据开销和功耗,甚至已有的安全措施也会因此而受到影响;

  • SSL证书需要钱,功能越强大的证书费用越高,个人网站、小网站没有必要一般不会用。

  • SSL证书通常需要绑定IP,不能在同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗。

  • HTTPS协议的加密范围也比较有限,在黑客攻击、拒绝服务攻击(dos)、服务器劫持等方面几乎起不到什么作用。最关键的,SSL证书的信用链体系并不安全,特别是在某些国家可以控制CA根证书的情况下,中间人攻击一样可行。

session与cookie

cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式

session机制。session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发送给服务器。

区别:

  1. cookie数据放在客户的浏览器上,session数据放在服务器上;
  2. cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,考虑到安全应当使用session;
  3. session会在一定时间内保存在服务器上。当访问增多,会比较占用服务器的性能,考虑到服务器性能应当使用cookie;
  4. 单个cookie保存的数据不能超过4k,很多浏览器都限制一个站点最多保存20个cookie;

建议:将登录等重要信息存放在session中,其他的一些信息若需要保留,就存放在cookie中