简述UDP协议和TCP协议一些问题

978 阅读16分钟

短链接和长链接

http/1.0使用的是短链接,客户端向服务器发送请求链接,建立链接,传送数据,关闭链接,每一次的请求都要执行上述过程。如果单个用户无需频繁操作的话,那么用短链接还比较合适,但是对于点对点的通信,而且交换数据很频繁,这时短链接就不合适了,这样非常耗时。所以就引入了长链接,对于长链接就是建立连接之后,双方通信,等到通信完毕的时候才断开,url中会出现关键字Connection:keep-alive这样就可以提高效率。

cookies和session

cookies

当浏览器发送请求到服务器端时,服务器会将请求进行解析,并生成cookies(个人信息)再发送回浏览器,第二次发送请求到服务器时,会携带cookies到服务器,服务器根据cookies来区别不同的用户

session

当浏览器发送请求到服务器时,服务器会生成session来保存相关信息,同时创建一个特殊的cookies来标识浏览器,把这个cookies发送到浏览器,浏览器再次访问服务器时就会携带这个cookies,服务器根据这个信息来找到对应的session。

区别

cookies保存在客户端,session保存在服务端,两者都是用来保存私密信息的,浏览器只要不关闭cookies是不会消失的,cookies不太安全,别人可以截取用户的cookies,然后伪造一个数据包过来,别人入侵你的机器会把你的cookies拷走,信息就会泄露,而session是保存在服务端的,别人难以获取,但是session会占用服务器的性能,服务器可能会删除sessions,对于cookies来说也是有大小限制的,一般来说要小于3kb

http的数据加密

https就是http+TLS/SSL 也就是说https是安全的http,HTTP使用80端口,HTTPS使用443端口,HTTP传输的时候是明文传输,相对于HTTPS的加密传输,速度会更快但是不太安全。使用HTTPS需要申请证书

TLS/SSL

对称加密

对称加密是指只有一个密钥,密文通过密钥加密,也通过密钥解密。对称加密的优点是速度快,操作简单,方便加密大量数据。但是缺点也显而易见,通信双发必须约定好密钥,而常常客户端和服务端在首次通信之前是很难约定私钥,要是通过通信的方式来传递密钥,密钥又很容易泄露。

非对称加密

非对称加密的方法是通信双方各持有一对公钥和私钥,客户端发出被私钥加密的密文给服务端,服务端将这个密文再进行加密再发给客户端,客户端对密文进行一次解密,在将密文发给服务端,服务端再将密文用私钥解密,最终就得到信息了。但是这种方式十分耗费时间。

数字签名

如何防止文本的内容被篡改:计算机会根据文本内容以某种算法形成Hash散列,从而形成一个数据摘要,再通过加密算法形成数字签名,这种数字签名对于改动十分敏感,一旦有小小的改动,签名就会大不相同。

UDP简介

user datagram protocol 用户数据包协议,这个协议为用户提供了无需连接就可以发送封装的数据包的方法。UDP在OSI传输模型中是位于传输层的,它不提供数据包的分组,排序,组装,从而也无法知道数据包是否被安全送达,但是UDP的优势在于当需要传输速度的时候,而不是传输的完整性,它的优势就会很大了。

报文格式

image.png 数据发送方通过源端口发出,通过目的端口接收,校验值是验证数据在传输中是否被修改了,如果校验值不同,那就意味着这个数据是不安全的。 udp是全双工的也就意味着recvfrom和send可以被同时调用。

TCP简介

这是一种面向字节流,可靠的,面向连接的传输层通信协议,

报文格式

image.png

  1. 其中源端口号和目的端口号很好理解,都是16位的应用程序的端口号,

  2. CP序列号(Sequence Number):TCP的通信是会把报文进行编号的,它表示本报文段所发送数据的第一个字节的编号,字节流的每一个字节都会进行编号,当SYN标记不为1时,这是当前数据分段第一个字母的序列号;如果SYN的值是1时,这个字段的值就是初始序列值(ISN),用于对序列号进行同步。这时,第一个字节的序列号比这个字段的值大1,也就是ISN加1。

  3. TCP 确认号(Acknowledgment Number,ACK Number):接收计算机将会收到的下一个序列号,也就是下一个接收到的字节序列号+1。

  4. TCP 首部长度(Header Length):数据段中的数据部分距离首部的长度,也就是告诉接收端的应用程序,数据从何开始。

  5. 保留(Reserved):占 4 位。为 TCP 将来的发展预留空间,目前必须全部为 0。

  6. ACK表示Acknowledgment Number字段有意义 PSH表示Push功能,RST表示复位TCP连接 SYN表示SYN报文(在建立TCP连接的时候使用) FIN表示没有数据需要发送了(在关闭TCP连接的时候使用

  7. 校验位(TCP Checksum):发送根据数据生成一个校验值,接收方根据数据也生成一个校验值,如果校验值相同,意味着没问题,否则数据被第三发修改了。

  8. 窗口大小(Window Size):占 16 位。它表示从 Ack Number 开始还可以接收多少字节的数据量,也表示当前接收端的接收窗口还有多少剩余空间。该字段可以用于 TCP 的流量控制。

TCP的数据可靠性::基于序号的确认应答机制

在传输层,TCP每发送一个报文,就要服务端发送对应的报文来确认这个报文服务端被收到。在实际的服务中,TCP会一次性发送多个报文,所以服务端要有多个报文进行对应,但是如何保证确认应答的与发送的报文是一一对应的呢,原来TCP的报头有序号和确认序号,确认序号是序号加一,通过这个机制,就可以确认每一个报文都被确认应答了。还有一点TCP是全双工的,在发送报文序号的同时也可以携带确认序号。

传输层的发送缓冲区和接收缓冲区

实际上,应用层通过系统调用来把相关数据发送到传输层的发送缓冲区,然后在把发送缓冲区的数据发送到服务端对应的接收缓冲区,然后服务端上层的recvfrom和read进行读取。

为什么要有这些缓冲区

  1. 提高应用层的效率
  2. 这样的方式能让传输层是可以被解耦的,这时只有TCP协议可以决定发送数据的具体问题。而且因为缓冲区的问题可以做到应用层和TCP解耦。

16位窗口大小的具体作用

在TCP包头中一个字段叫16位窗口大小,在客户端向服务端发送一连串消息的时候,万一服务端的接收缓冲区满了,那么剩下的报文不就被丢弃了吗?这时16位窗口大小就起到关键的作用了。服务端会给客户端发送应答报文中会携带剩余缓冲区的大小在16位窗口大小里面,客户端会根据窗口大小来决策要发多少数据。

TCP的三次握手

TCP协议是面向链接的,通信之前要先进行connect,那如何进行connect的呢,那就是三次握手了。 我们以客户端向服务端发起链接为例,客户端会发送报文给服务端,其中包头的SYN标志位被设为1,同步序列编号(Synchronize Sequence Numbers),表示我要向你建立连接,服务端收到消息后,发送一段报文,报文的包头SYN标志位和ACK标志位被设为1,ACK表示对上一个报文的应答,表示我收到了,SYN表示我服务端也要与你建立连接,然后客户端发送报文ACK设置为1,表示我收到了来自服务端的连接请求,至此这三个报文的发送称为三次握手。链接建立成功。

RET的作用

当双方连接出现异常的时候又无法正常的解开连接,那么为了不占用系统资源,都可以发送RET的报文关闭异常的链接

URG的作用

urgent pointer ,一个报文中可能有急需被读到的报文,为了让这个报文被读到,URG被设置为1的时候,16位紧急指针就会指向那个紧急报文开始的地址,这时紧急报文就被读到了。

PSH的作用

当发送一个报文带有PSH时,接收方收到这个报文就会将接收缓冲区的报文马上读取,而不是等满了再读取。

为什么是三次握手呢

TCP建立连接的关键是维护一对发送报文的序号,只有三次握手是最少让通信双方知道各自发送报文开始的序号,从而维护链接。如果只有两次握手,只能有发送方的序号被确认。

四次挥手

当进行通信的双方其中要关闭链接的时候,率先关闭的一方发送带有FIN的报文,然后对方发送ACK的报文,这样确认一方的断开被收到,而另一方也进行同样的操作,这就称为四次挥手。

为什么是四次挥手而不是三次

因为在关闭链接的时候,当发送FIN时就意味着没有其他的报文发给你了,当一方发送FIN时,另一方在接收的时候可能还有一些报文没有被发送,这时就先发送ACK进行应答,再把剩余的数据进行发送,之后再发送FIN报文,所以这时ACK和FIN是不可以同时被设置为1的。

如何理解面向字节流

这里UDP是面向报文的,也就是说,UDP对于应用层交付的报文不进行合并和拆分,直接向下进行交付,一次交付一个完整的报文。而TCP对于应用层的报文数据块,维持字节流顺序,TCP中有缓冲区,这些字节保存在缓冲区里面,当应用程序传输的数据块太长,TCP就可以把它划分端一些再传输,如果应用程序一次只传输一个字节,那么TCP可以等待积累足够多的字节后再构成报文端发送出去,所以TCP的面向字节的。

什么是超时重传机制

在通信过程中,假如客户端向服务端发送数据,可能由于网络拥堵会丢失数据,或者对方发来的ACK无法接收,这样服务端会收到大量重复报文,TCP会根据序列号来丢弃重复报文。如果客户端一段时间没有得到反应,那么就会再次发送数据,这就是超时重传机制,这个一段时间实际上是动态的,比如第一次是500ms,下一次重传的时间间隔就会增长,2倍,或者更多以指数形式进行递增。

连接管理机制

服务端调用Listen后就会进入LISTEN状态,等待客户端连接,一旦监听到连接请求,就将该连接放在内核等待队列,并向客户端发送SYN确认报文。SYN_RCVD状态,一旦收到确认报文就会进入ESTABLISHED,就可以进行读写了。当客户端主动关闭连接,服务器就会收到结束报文段进入CLOSE_WAIT. [CLOSED -> SYN_SENT] 客户端调用connect, 发送同步报文段;

[SYN_SENT -> ESTABLISHED] connect调用成功, 则进入ESTABLISHED状态, 开始读写数据;

[ESTABLISHED -> FIN_WAIT_1] 客户端主动调用close时, 向服务器发送结束报文段, 同时进入

FIN_WAIT_1;

[FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认, 则进入FIN_WAIT_2, 开始等待服

务器的结束报文段;

[FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段就会进入TIME_WAIt,最后再等待两个最大生成时间,才会进入到CLOSED,状态。

理解TIME_WAIT状态

我们会遇到过这种状况,一个服务器绑定了一个端口,当服务停止的时候,在运行,这个端口就不能用了,这是因为率先关闭的一方要进入TIME_WAIT状态然后等待两个MSL才可以回到CLOSED状态。此时这个服务端的状态是FINWAIT2所以就不能再用这个端口提供服务了,一个MSL在centos上是60s。

为什么时间是两个MSL,因为经过两个MSL就可以保证两个传输方向上的数据都已经消失,否则服务器重启就会收到来自上一个数据迟到的报文,这样是不被允许的。

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

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

image.png

理解CLOSE_WAIT状态

服务器上出现大量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭 socket, 导致四次挥手没有正确 完成. 这是一个 BUG. 只需要加上对应的 close 即可解决问题

什么是滑动窗口

我们说过确认应答策略,对于每一个被发送的数据段都要收到ACK才传下一个数据段,尤其是数据往返的时间特别长的时候。这样就会导致性能很差。这样我们一次性发送很多报文,窗口大小是不需要确认应答的报文的大小,当报文被确认时,窗口的左边右移,右边的数据被发走,窗口右侧也拓展大小。

。数据包分为四种

  • 已发送并且已经确认的包。
  • 已发送但是没有确认的包。
  • 未发送但是可以发送的包。
  • 不允许被发送的包。

还有一种滑动窗口其实就是接收方数据报缓冲区的大小,设想在发送方发的很快,但是接收方处理很慢,这样就会导致数据丢失,为了不丢失,就要进行流量控制,滑动窗口告诉发送端对它所发送的数据能提供多大的缓 冲区。由于窗口由16位bit所定义,所以接收端TCP 能最大提供65535个字节的缓冲。由此,可以利用窗口大小和第一个数据的序列号计算出最大可接收的数据序列号。

  1. 窗口合拢 也就是发送发发过数据并被确认时
  2. 窗口扩大 也就是接收方处理数据
  3. 窗口收缩:当窗口的右边沿向左边移动的时候,这种现象不常发生。 TCP就是利用这个窗口来慢慢的把发送缓冲区的数据发出去

拥塞控制

虽然有了滑动窗口来进行流量控制,但是在刚开始就发送大量数据,仍然有可能出现问题,网络中的链路容量和交换结点中的缓存和处理机都有着工作的极限,当网络的需求超过它们的工作极限时,就出现了拥塞。拥塞控制就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。常用的方法就是:

  1. 慢开始、拥塞控制
  2. 快重传、快恢复      一切的基础还是慢开始,这种方法的思路是这样的:引入慢启动机制,先发一部分数据探探路,如果网络通畅,那就把数据量以指数形式快速增长,,然后线性增长,直到临界点,然后再突然降下来。如果是少量的丢包,那就超时重传,如果是大量丢包,那就认为是网络拥塞。

延迟应答

当客户端向服务端发送一批数据时,服务端并不会立即发送ACK响应报文,而是等待一小会,因为这一小会,应用层会从缓冲区里面读取数据,这时响应报文的窗口就会变大,客户端再发送一批报文时,就会发的更多。也就是说窗口越大,网络吞吐量就是越大的。延迟应答的意义在于提升传输效率。

捎带应答

简单的来说就响应的报文中携带了两个信息。比如说返回ACK,但是报文正文还会携带其他的信息。

粘包问题

在传输层的角度上来说,TCP报文是按序放在缓冲区里面的,读取报文发送的时候,会存在发送了一部分的报文的情况,这种情况是不被允许的。如何解决粘包问题呢!也就是在缓冲区字节流里面设置报文的边界。对于定长的包,保证每次都按照固定大小来读取即可,例如Request报文就是定长的。对于边长的报文,在包头上面添加总长度的字段,这样就知道包的结束位置,或者在包之间设计分隔符。

TCP的异常情况

当机器掉线或者断电的时候,接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset. 就算没有写入操作, TCP自己也内置了一个保活定时器, 会定期询问对方是否还在. 如果对方不在, 也会把连接释放。

一个面试题:如何设计一个稳定的UDP协议

也就是按照TCP的一些性质添加到UDP中,引入序列号,保证数据顺序,引入确认应答,保证对端收到了数据,引入超时重传。。。

listen函数的第二个参数是什么?

我们知道TCP建立连接是三次握手,这时服务器变为established状态,这个参数其实就是能有多少个正常握手的客户端-1.