关于网络通信:网络协议笔记四传输层

4次阅读

共计 6701 个字符,预计需要花费 17 分钟才能阅读完成。

九、UDP 协定

TCP 和 UDP 有哪些区别?

TCP 是面向连贯的,UDP 是面向无连贯的。所谓的建设连贯,是为了在客户端和服务端保护连贯,而建设肯定的数据结构来保护单方交互的状态,用这样的数据结构来保障所谓的面向连贯的个性。

TCP 提供牢靠交付。 通过 TCP 连贯传输的数据,无差错、不失落、不反复、并且按序达到。咱们都晓得 IP 包是没有任何可靠性保障的, 一旦收回去只能事在人为,而 UDP 继承了 IP 包的个性,不保障不失落,不保障按程序达到。

TCP 是面向字节流的。发送的时候发的是一个流,没头没尾。 IP 包可不是一个流,而是一个个的 IP 包。之所以变成了流,这也是 TCP 本人的状态保护做的事件。而 UDP 继承了 IP 的个性,基于数据报的,一个一个地发,一个一个地收。

TCP 是能够有拥塞管制的。 它意识到包抛弃了或者网络的环境不好了,就会依据状况调整本人的行为,看看是不是发快了,要不要发慢点。UDP 就不会,利用让我发,我就发,管它洪水滔天。

因此TCP 其实是一个有状态服务, 艰深地讲就是有脑子的,外面准确地记着发送了没有,接管到没有,发送到哪个了,应该接管哪个了,错一点儿都不行。而 UDP 则是无状态服务。 艰深地说是没脑子的,天真无邪的,收回去就收回去了。

咱们能够这样比喻,如果 MAC 层定义了本地局域网的传输行为,IP 层定义了整个网络端到端的传输行为, 这两层根本定义了这样的基因:网络传输是以包为单位的,二层叫帧,网络层叫包,传输层叫段。 咱们抽象地称为包。包独自传输,自行选路,在不同的设施封装解封装,不保障达到。 基于这个基因,生下来的孩子 UDP 齐全继承了这些个性,简直没有本人的思维。

UDP 包头

发送的时候,我晓得我发的是一个 UDP 的包,收到的那台机器咋晓得的呢?所以在 IP 头外面有个 8 位协定,这里会寄存,数据外面到底是 TCP 还是 UDP,当然这里是 UDP。于是,如果咱们晓得 UDP 头的格局,就能从数据外面,将它解析进去。

当我发送的 UDP 包达到指标机器后,发现 MAC 地址匹配,于是就取下来,将剩下的包传给解决 IP 层的代码。把 IP 头取下来,发现指标 IP 匹配,接下来而后就给传输层解决,解决完后,内核的事件根本就干完了,外面的数据应该交给应用程序本人去解决。

UDP 的三大特点

  1. 沟通简略,不须要一肚子花花肠子(大量的数据结构、解决逻辑、包头字段)。
  2. 轻信别人。它不会建设连贯,尽管有端口号,然而监听在这个中央,谁都能够传给他数据,他也能够传给任何人数据,甚至能够同时传给多集体数据。
  3. 愣头青,做事不懂权变。它不会依据网络的状况进行发包的拥塞管制,无论网络丢包丢成啥样了,它该怎么发还怎么发。

UDP 的三大应用场景

  1. 须要资源少,在网络状况比拟好的内网,或者对于丢包不敏感的利用。DHCP 就是基于 UDP 协定的。个别的获取 IP 地址都是内网申请,而且一次获取不到 IP 又没事,过一会儿还有机会。
  2. 不须要一对一沟通,建设连贯,而是能够播送的利用。UDP 的不面向连贯的性能,能够使得能够承载播送或者多播的协定。DHCP 就是一种播送的模式, 就是基于 UDP 协定的,而播送包的格局后面说过了。
  3. 须要处理速度快,时延低,能够容忍多数丢包,然而要求即使网络拥塞,也毫不退缩,裹足不前的时候。

基于 UDP 的五个例子

网页或者 APP 的拜访

QUIC(全称 Quick UDP Internet Connections,疾速 UDP 互联网连贯)是 Google 提出的一种基于 UDP 改良的通信协议,其目标是升高网络通信的提早,提供更好的用户互动体验。

QUIC 在应用层上,会本人实现疾速连贯建设、缩小重传时延,自适应拥塞管制,

流媒体的协定

直播协定多应用 RTMP。而这个 RTMP 协定也是基于 TCP 的。TCP 的严格程序传输要保障前一个收到了,下一个能力确认,如果前一个收不到,下一个就算包曾经收到了,在缓存外面,也须要等着。对于直播来讲,这显然是不适合的,因为老的视频帧丢了其实也就丢了,就算再传过来用户也不在意了,他们要看新的了,如果老是没来就等着,卡顿了,新的也看不了,那就会失落客户,所以直播,实时性比拟比拟重要,宁肯丢包,也不要卡顿的。

实时游戏

实时游戏中客户端和服务端要建设长连贯,来保障实时传输。然而游戏玩家很多,服务器却不多。因为保护 TCP 连贯须要在内核保护一些数据结构,因此一台机器可能撑持的 TCP 连贯数目是无限的,而后 UDP 因为是没有连贯的,在异步 IO 机制引入之前,经常是应答海量客户端连贯的策略。

另外还是 TCP 的强程序问题,对战的游戏,对网络的要求很简略,玩家通过客户端发送给服务器鼠标和键盘行走的地位,服务器会解决每个用户发送过去的所有场景,解决完再返回给客户端,客户端解析响应,渲染最新的场景展现给玩家。
如果呈现一个数据包失落,所有事件都须要停下来期待这个数据包重发。客户端会呈现期待接收数据,然而玩家并不关怀过期的数据,激战中卡 1 秒,等能动了都曾经死了。

游戏对实时要求较为严格的状况下,采纳自定义的牢靠 UDP 协定,自定义重传策略,可能把丢包产生的提早降到最低,尽量减少网络问题对游戏性造成的影响。

IoT 物联网

一方面,物联网畛域终端资源少, 很可能只是个内存十分小的嵌入式零碎,而保护 TCP 协定代价太大;另一方面,物联网对实时性要求也很高, 而 TCP 还是因为下面的那些起因导致时延大。Google 旗下的 Nest 建设 Thread Group,推出了物联网通信协议 Thread,就是基于 UDP 协定的。

挪动通信畛域

在 4G 网络里,挪动流量上网的数据面对的协定 GTP-U 是基于 UDP 的。因为挪动网络协议比较复杂,而 GTP 协定自身就蕴含简单的手机上线下线的通信协议。如果基于 TCP,TCP 的机制就显得十分多余。

十、TCP 协定

TCP 协定。它之所以这么简单,那是因为它秉承的是“性恶论”。它人造认为网络环境是顽劣的,丢包、乱序、重传,拥塞都是常有的事件,一言不合就可能送达不了,因此要 从算法层面来保障可靠性。

TCP 包头格局

  1. 包的序号。 为什么要给包编号呢?当然是为了解决乱序的问题。不编好号怎么确认哪个应该先来,哪个应该后到呢。编号是为了解决乱序问题。
  2. 确认序号。 收回去的包应该有确认,要不然我怎么晓得对方有没有收到呢?如果没有收到就应该从新发送,直到送达。这个能够解决不丢包的问题。
    TCP 是靠谱的协定,然而这不能阐明它面临的网络环境好。从 IP 层面来讲,如果网络情况确实那么差,是没有任何可靠性保障的,而作为 IP 的上一层 TCP 也无能为力,惟一能做的就是更加致力,一直重传,通过各种算法保障。也就是说,对于 TCP 来讲,IP 层你丢不丢包,我管不着,然而我在我的层面上,会致力保障可靠性。
  3. 状态位。 例如 SYN 是发动一个连贯,ACK 是回复,RST 是从新连贯,FIN 是完结连贯等。TCP 是面向连贯的,因此单方要保护连贯的状态,这些带状态位的包的发送,会引起单方的状态变更。
  4. 窗口大小。 TCP 要做流量管制,通信单方各申明一个窗口,标识本人以后可能的解决能力,别发送的太快,撑死我,也别发的太慢,饿死我。除了做流量管制以外,TCP 还会做拥塞管制,对于真正的通路堵车不堵车,它无能为力,惟一能做的就是管制本人,也即管制发送的速度。

通过对 TCP 头的解析,咱们晓得要把握 TCP 协定,重点应该关注以下几个问题:
程序问题;丢包问题;连贯保护;流量管制;拥塞管制

TCP 的三次握手(连贯保护)

TCP 的连贯建设,咱们经常称为三次握手。

A:您好,我是 A。B:您好 A,我是 B。A:您好 B。

咱们也常称为 “申请 -> 应答 -> 应答之应答” 的三个回合。

三次握手除了单方建设连贯外,次要还是为了沟通一件事件,就是 TCP 包的序号的问题。
A 要通知 B,我这面发动的包的序号起始是从哪个号开始的,B 同样也要通知 A,B 发动的包的序号起始是从哪个号开始的。为什么序号不能都从 1 开始呢?因为这样往往会呈现抵触。

这个序号的起始序号是随着工夫变动的,能够看成一个 32 位的计数器,每 4ms 加一,计算一下,如果到反复,须要 4 个多小时,那个绕路的包早就死翘翘了,因为咱们都晓得 IP 包头外面有个 TTL,也即生存工夫。

咱们还是假如这个通路是十分不牢靠的,A 要发动一个连贯,当发了第一个申请石沉大海的时候,会有很多的可能性,比方第一个申请包丢了,再如没有丢,然而绕了弯路,超时了,还有 B 没有响应,不想和我连贯。A 不能确认后果,于是再发,再发。终于,有一个申请包到了 B,然而申请包到了 B 的这个事件,目前 A 还是不晓得的,A 还有可能再发。B 收到了申请包,就晓得了 A 的存在,并且晓得 A 要和它建设连贯。如果 B 不乐意建设连贯,则 A 会重试一阵后放弃,连贯建设失败,没有问题;如果 B 是乐意建设连贯的,则会发送应答包给 A。

当然对于 B 来说,这个应答包也是一入网络深似海,不晓得能不能到达 A。这个时候 B 天然不能认为连贯是建设好了,因为应答包依然会丢,会绕弯路,或者 A 曾经挂了都有可能。而且这个时候 B 还能碰到一个诡异的景象就是,A 和 B 原来建设了连贯,做了简略通信后,完结了连贯。还记得吗?A 建设连贯的时候,申请包反复发了几次,有的申请包绕了一大圈又回来了,B 会认为这也是一个失常的的申请的话,因而建设了连贯,能够设想,这个连贯不会进行上来,也没有个终结的时候,纯属单相思了。因此两次握手必定不行。B 发送的应答可能会发送屡次,然而只有一次达到 A,A 就认为连贯曾经建设了,因为对于 A 来讲,他的音讯有去有回。A 会给 B 发送应答之应答,而 B 也在等这个音讯,能力确认连贯的建设,只有等到了这个音讯,对于 B 来讲,才算它的音讯有去有回。当然 A 发给 B 的应答之应答也会丢,也会绕路,甚至 B 挂了。按理来说,还应该有个应答之应答之应答,这样上来就没底了。所以四次握手是能够的,四十次都能够,要害四百次也不能保障就真的牢靠了。只有单方的音讯都有去有回,就根本能够了。好在大部分状况下,A 和 B 建设了连贯之后,A 会马上发送数据的,一旦 A 发送数据,则很多问题都失去了解决。例如 A 发给 B 的应答丢了,当 A 后续发送的数据达到的时候,B 能够认为这个连贯曾经建设,或者 B 压根就挂了,A 发送的数据,会报错,说 B 不可达,A 就晓得 B 出事件了。当然你能够说 A 比拟坏,就是不发数据,建设连贯后空着。咱们在程序设计的时候,能够要求开启 keepalive 机制,即便没有实在的数据包,也有探活包。另外,你作为服务端 B 的程序设计者,对于 A 这种长时间不发包的客户端,能够被动敞开,从而空出资源来给其余客户端应用。三次握手除了单方建设连贯外,次要还是为了沟通一件事件,就是 TCP 包的序号的问题。A 要通知 B,我这面发动的包的序号起始是从哪个号开始的,B 同样也要通知 A,B 发动的包的序号起始是从哪个号开始的。为什么序号不能都从 1 开始呢?因为这样往往会呈现抵触。例如,A 连上 B 之后,发送了 1、2、3 三个包,然而发送 3 的时候,两头丢了,或者绕路了,于是从新发送,起初 A 掉线了,从新连上 B 后,序号又从 1 开始,而后发送 2,然而压根没想发送 3,然而上次绕路的那个 3 又回来了,发给了 B,B 天然认为,这就是下一个包,于是产生了谬误。因此,每个连贯都要有不同的序号。这个序号的起始序号是随着工夫变动的,能够看成一个 32 位的计数器,每 4ms 加一,如果计算一下,如果到反复,须要 4 个多小时,那个绕路的包早就死翘翘了,因为咱们都晓得 IP 包头外面有个 TTL,也即生存工夫。好了,单方终于建设了信赖,建设了连贯。后面也说过,为了保护这个连贯,单方都要保护一个状态机,在连贯建设的过程中,单方的状态变动时序图就像这样。

一开始,客户端和服务端都处于 CLOSED 状态。先是服务端被动监听某个端口,处于 LISTEN 状态。而后客户端被动发动连贯 SYN,之后处于 SYN-SENT 状态。服务端收到发动的连贯,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。客户端收到服务端发送的 SYN 和 ACK 之后,发送 ACK 的 ACK,之后处于 ESTABLISHED 状态,因为它一发一收胜利了。服务端收到 ACK 的 ACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。

TCP 的四次挥手

A:B 啊,我不想玩了。
B:哦,你不想玩了啊,我晓得了。
这个时候,还只是 A 不想玩了,也即 A 不会再发送数据,然而 B 能不能在 ACK 的时候,间接敞开呢?当然不能够了,很有可能 A 是发完了最初的数据就筹备不玩了,然而 B 还没做完本人的事件,还是能够发送数据的,所以称为半敞开的状态。

这个时候 A 能够抉择不再接收数据了,也能够抉择最初再接管一段数据,期待 B 也被动敞开。

B:A 啊,好吧,我也不玩了,拜拜。
A:好的,拜拜。
这样整个连贯就敞开了。然而这个过程有没有异常情况呢?当然有,下面是战争离别的局面。

A 开始说“不玩了”,B 说“晓得了”,这个回合,是没什么问题的,因为在此之前,单方还处于单干的状态,如果 A 说“不玩了”,没有收到回复,则 A 会从新发送“不玩了”。然而这个回合完结之后,就有可能出现异常状况了,因为曾经有一方率先撕破脸。

一种状况是,A 说完“不玩了”之后,间接跑路,是会有问题的,因为 B 还没有发动完结,而如果 A 跑路,B 就算发动完结,也得不到答复,B 就不晓得该怎么办了。另一种状况是,A 说完“不玩了”,B 间接跑路,也是有问题的,因为 A 不晓得 B 是还有事件要解决,还是过一会儿会发送完结。

断开的时候,咱们能够看到,当 A 说“不玩了”,就进入 FIN_WAIT_1 的状态,B 收到“A 不玩”的音讯后,发送晓得了,就进入 CLOSE_WAIT 的状态。A 收到“B 说晓得了”,就进入 FIN_WAIT_2 的状态,如果这个时候 B 间接跑路,则 A 将永远在这个状态。TCP 协定外面并没有对这个状态的解决,然而 Linux 有,能够调整 tcp_fin_timeout 这个参数,设置一个超时工夫。
如果 B 没有跑路,发送了“B 也不玩了”的申请达到 A 时,A 发送“晓得 B 也不玩了”的 ACK 后,从 FIN_WAIT_2 状态完结,按说 A 能够跑路了,然而最初的这个 ACK 万一 B 收不到呢?则 B 会从新发一个“B 不玩了”,这个时候 A 曾经跑路了的话,B 就再也收不到 ACK 了,因此 TCP 协定要求 A 最初期待一段时间 TIME_WAIT,这个工夫要足够长,长到如果 B 没收到 ACK 的话,“B 说不玩了”会重发的,A 会从新发一个 ACK 并且足够工夫达到 B。A 间接跑路还有一个问题是,A 的端口就间接空进去了,然而 B 不晓得,B 原来发过的很多包很可能还在路上,如果 A 的端口被一个新的利用占用了,这个新的利用会收到上个连贯中 B 发过来的包,尽管序列号是从新生成的,然而这里要上一个双保险,避免产生凌乱,因此也须要等足够长的工夫,等到原来 B 发送的所有的包都死翘翘,再空出端口来。期待的工夫设为 2MSL,MSL 是 Maximum Segment Lifetime,报文最大生存工夫,它是任何报文在网络上存在的最长工夫,超过这个工夫报文将被抛弃。因为 TCP 报文基于是 IP 协定的,而 IP 头中有一个 TTL 域,是 IP 数据报能够通过的最大路由数,每通过一个解决他的路由器此值就减 1,当此值为 0 则数据报将被抛弃,同时发送 ICMP 报文告诉源主机。协定规定 MSL 为 2 分钟,理论利用中罕用的是 30 秒,1 分钟和 2 分钟等。还有一个异常情况就是,B 超过了 2MSL 的工夫,仍然没有收到它发的 FIN 的 ACK,怎么办呢?依照 TCP 的原理,B 当然还会重发 FIN,这个时候 A 再收到这个包之后,A 就示意,我曾经在这里等了这么长时间了,曾经惨绝人寰了,之后的我就都不认了,于是就间接发送 RST,B 就晓得 A 早就跑了。

TCP 状态机

将连贯建设和连贯断开的两个时序状态图综合起来,就是这个驰名的 TCP 的状态机。

如何实现一个靠谱的协定?

程序问题与丢包问题

流量管制问题

拥塞管制问题

十一、套接字 Socket

基于 TCP 和 UDP 协定的 Socket 编程。
Socket 编程进行的是端到端的通信,往往意识不到两头通过多少局域网,多少路由器,因此可能设置的参数,也只能是端到端协定之上网络层和传输层的。

在网络层,Socket 函数须要指定到底是 IPv4 还是 IPv6,别离对应设置为 AF_INET 和 AF_INET6。另外,还要指定到底是 TCP 还是 UDP。还记得咱们后面讲过的,TCP 协定是基于数据流的,所以设置为 SOCK_STREAM,而 UDP 是基于数据报的,因此设置为 SOCK_DGRAM。

基于 TCP 协定的 Socket 程序函数调用过程

基于 UDP 协定的 Socket 程序函数调用过程

服务器如何接更多的我的项目?

多过程形式

多线程形式

IO 多路复用,一个线程保护多个 Socket

IO 多路复用,从“派人盯着”到“有事告诉”)

正文完
 0