Linux网络——传输层

在我们传输层的下面就是运输层,传输层是整个网络体系结构中最重要的一层之一,因为传输层为数据的传输提供服务,这也是网络最基本的要求,数据的共享就需要数据的传输。当然本层设计的内容复杂且逻辑性强,博文将尽量以通俗易懂的方式传递对知识的个人理解。

目录

端口号

认识端口号

端口号和进程id

端口号的划分

实践

命令

UDP

认识UDP

特点

格式

TCP

认识TCP

特点

格式

连接管理机制

确认应答(ACK)机制

超时重传机制

滑动窗口机制

流量控制机制

拥塞控制机制

延迟应答机制

捎带应答机制

TCP总结

TCP与UDP


端口号

  • 认识端口号

在认识网络阶段,有个概念叫数据包的分用,当数据包来到传输层时。还要继续向上一层继续交付,这个时候,它不知道该向哪个进程交付,于是就有了端口号。端口号用来标识一个进程,用来传输层向上交付数据。

端口号是两个字节十六位的整数,所以最大可以表示位65535,所以最多有65535个端口号。

  • 端口号和进程id

前面说端口号是用来标识一个进程,进程id也是标识一个进程的。为社么不用进程id而要用端口号?

这是因为进程id是动态分配,当一个程序运行起来之后,操作系统为该进程分配一个进程id,如果程序再次运行,进程id就会改变,但是端口号不会,端口号在程序运行前就已经固定,即使程序再怎么重启,端口号也不会改变。这两者的关系,就像网卡上的mac地址和ip地址一样。

例如,我们客户端向服务器发起连接时,会用到IP地址和端口号,如果这里的I端口号是进程号,当服务器重启一次后,客户端第二次连接服务器,就不知道端口号了,总不能每次连接都让给服务器查一下进程号,然后告诉客户端,进程id是多少吧。

  • 端口号的划分

端口号有65535个,但是我们写网络程序时不能随意使用。因为大佬有优先使用权。

0 - 1023:知名端⼝号,HTTP、FTP、SSH等这些广为使⽤的应⽤层协议,他们的端⼝号都是固定的。注意,不是协议分配端口号,而是说如果程序这一层用到了这些协议,写程序时优先绑定这些端口号。当然也可以绑定其他的端口号。

1024-65535:操作系统动态分配和程序员使用,当客户端没有绑定端口号时,操作系统则会动态分配一个。

  • 知名协议

  1. ftp服务器, 使⽤21端⼝

  2. ssh服务器,使用22端口

  3. telnet服务器, 使⽤23端

  4. http服务器, 使⽤80端⼝

  5. https服务器, 使⽤443 端口

虽然这些服务器⼀般使⽤这些端⼝, 但这只是⼀个通⽤的习惯. 并不是说HTTP服务器就不能使⽤其他的端⼝号。

  • 实践

一个进程是否可以绑定多个端口?

可以,端口号是标识进程的,可以有多个端口号。就像一个人可以有多个名字:小名,大名,乳名,曾用名。

一个端口是否可以绑定多个进程?

不可以,如果多个进程用同一个端口号,数据到来时,不知道交付给哪一个进程。还是用上面的例子,如果一个班级有两个人同名,老师抽人回答问题时,叫到这个名字,就不知道谁该站起来。

  • 命令

netstat是⼀个⽤来查看网络状态的重要⼯具.

语法:netstat [选项]

功能:查看⺴络状态

常⽤选项

n 拒绝显⽰别名,能显⽰数字的全部转化成数字

l 仅列出有在 Listen (监听) 的服务状态

p 显⽰建⽴相关链接的程序名

t (tcp)仅显⽰tcp相关选项

u (udp)仅显⽰udp相关选项

a(all)显示所有

在传输层,最重要的两个协议就是UDP和TCP了。坐稳了,要发车了。

  • UDP

  • 认识UDP

UDP 英文名User Datagram Protocol, 中文名叫用户数据报协议。

  • 特点

在官方的定义中,有如下特点:无连接,不可靠,面向数据报。押韵且精辟。下面来解释一下。

  1. 无连接:在UDP中,我们只要知道对端的ip地址和端口号就行了,不用提前建立连接,

  2. 不可靠:UDP只管发送数据,至于收不收得到,那就不知道了,所以不可靠。

  3. 面向数据报:应用层交给传输层多长的报文,UDP原封不动的发送出去。

UDP的传输方式就类似写信。只要知道对方的姓名和地址,填上就行了不会告诉对方“我要给你写信了”(无连接);信件寄送出去后,不期待对方的回信,也不管收不收得到,反正寄出去了就完事儿了(不可靠);你给我多长一封信,就只管信封里塞,不分成多次,原封不多的寄送出去(面向数据报)。

当然UDP还不止这么不靠谱,再来看看UDP的缓冲区,UDP在发送数据报的时候,会调用sendto系统调用接口,直接交给内核,由内核交给网络层完成后面的传输,UDP的缓冲区主要是接收缓冲区,缓冲区接收到数据报不能保证和发送数据报的顺序一样,而且如果缓冲区满了,就不接收了,后面来的数据报,就丢掉,让它们随风而去。。。

  • 格式

Linux网络——传输层_第1张图片

16位端口号:这个就不解释了,就是端口号,最大也就是65535

16位数据报长度:这个是整个数据报(UDP⾸部+UDP数据)的最⼤⻓度;

16位校验和:如果校验和出错, 就会直接丢弃

UDP的校验和需要计算UDP首部加数据荷载部分,但也需要加上UDP伪首部。伪首部+UDP首部+数据一起计算校验和。伪首部是一个虚拟的数据结构,其中的信息是从数据报所在IP分组头的分组头中提取的,既不向下传送也不向上递交,而仅仅是为计算校验和。

UDP检验和的计算方法是:

按每16位求和得出一个32位的数; 如果这个32位的数,高16位不为0,则高16位加低16位再得到一个32位的数; 重复第2步直到高16位为0,将低16位取反,得到校验和。

注意:

  1. UDP协议⾸部中有⼀个16位的最⼤⻓度。也就是说⼀个UDP能传输的数据最⼤⻓度是64K字节。然⽽64K字节在当今的互联网环境下,是⼀个⾮常⼩的数字。如果我们需要传输的数据超过64K字节,就需要在应⽤层⼿动的分包, 多次发送, 并在接收端⼿动拼装;

  2. 发多少,收多少。如果发送端调用sendto发送100字节,接收端就要用resvfrom接收100字节,不能循环接收,就是说每次接收10字节,循环十次等这样的操作。

TCP

  • 认识TCP

TCP英文名Transmission Control Protocol ,中文名叫传输控制协议。人如其名,要对数据的传输进行一个详细的控制。它是网络协议中的大BOSS。下面来攻克这个大BOOS。

  • 特点

  1. 有连接

  2. 可靠传输

  3. 面向字节流

这三个特点概念对比UDP的三个特点就不难理解了。有连接,就像打电话,我们都会 “ 喂~ ” ,看对方听不听得见,对方则会回复 ”听得见,你说~“,这就是建立连接的过程。这个BOSS最主要的技能便是,可靠传输。我们知道,在网络中,数据的传输是不会可靠的,毕竟丢包的问题很常见,但是可靠传输这便是逆天而行的感觉,所以实现起来非常的繁琐。后面一系列的机制都是为让可靠传输。面向字节流就是说,按字节为单位传输,不像UDP那么”老实“。

  • 格式

Linux网络——传输层_第2张图片

源/目的端⼝号:标识源/目的进程

32位序号/32位确认号:实现可靠传输

4位TCP报头⻓度:这里的单位是4字节,所以最大的4位是15,故TCP头部最⼤⻓度是15 * 4 = 60字节。注意:UDP的头部长度是固定的8字节,而TCP是可变的,因为有选项这个位置,具体干嘛的,后面说。

6位标志位:

  1. URG: 紧急指针是否有效

  2. ACK:确认号是否有效

  3. PSH:提⽰接收端应⽤程序⽴刻从TCP缓冲区把数据读⾛

  4. RST:对⽅要求重新建⽴连接; 我们把携带RST标识的称为复位报⽂段

  5. SYN:请求建⽴连接; 我们把携带SYN标识的称为同步报⽂段

  6. FIN:通知对⽅, 本端要关闭了, 我们称携带FIN标识的为结束报文段

16位窗⼝⼤⼩:实现可靠传输

16位校验和:发送端填充, CRC校验,接收端校验不通过,则认为数据有问题,此处的检验和不光包含TCP

⾸部, 也包含TCP数据部分

16位紧急指针:标识哪部分数据是紧急数据

40字节头部选项:后面说

上面的大部分属性都是为了实现可靠传输,下面就是TCP为了它的三个特性,实现的各种机制。由于机制非常巧妙,一环扣一环,但是不好理解,所以会有很多例子帮助理解。

  • 连接管理机制

首先TCP的第一个特点便是有连接,TCP为了实现连接管理,便有了大名鼎鼎的三次握手,四次挥手。下面看是怎么一回事。首先讲个例子来说明三次握手。

我们知道男女关系的建立,是两情相悦的,所以有下面的情况。

Linux网络——传输层_第3张图片

Linux网络——传输层_第4张图片

我们把收到称为应答,即ACK,把表白称为连接请求,SYN。为什么这里是四次握手?其实三次握手是因为把第二次握手和第三次握手合并了,即就称为了ACK+SYN。由于ACK只是一个bit位,所以可以直接上发送的SYN带上,提高连接的效率。所以就成了下面的图。

Linux网络——传输层_第5张图片

上面的漫画,纯属虚构。看完漫画,来说一下实际上服务器和客户端怎么连接。

Linux网络——传输层_第6张图片

下面来看一下四次挥手是怎么回事。

Linux网络——传输层_第7张图片

对于细节部分,在超时重传部分进行探讨。大致流程如上所述。

  • 确认应答(ACK)机制

根据上面的例子,就很容易理解,确认应答了,但是不仅仅是应答。TCP将每个字节的数据都进⾏了编号,即为序列号。还记得上面TCP格式中的32位序号和32位确认序号吗?就是用在这儿的。

Linux网络——传输层_第8张图片

每个ACK确认应答,就会告诉我已经收到了,下一个应该是啥。这个机制在后面还有很重要的作用。

  • 超时重传机制

通过前面的一系列机制,会带来一定量的可靠性,但是任然存在以下情况,举个例子:

Linux网络——传输层_第9张图片

于是对应客户端和服务器,就会是:

  1. 客户端发送给服务器的数据丢了
  2. 服务器给客户端的确认应答丢了

针对情况1,如果在一段时间内,客户端没有收到服务器发来的ACK,那么客户端会重新给服务器发送这个数据。

针对情况2,服务器回复的ACK丢了,客户端也会认为数据没到,则又会发送数据,但是服务器已经收到了,这就会造成数据重复。于是上一个机制确认应答中有序号这一功能,便帮助服务器去重,因为序号相同数据,服务器会自动舍弃。

因此超时的时间的确定很重要,不能太长,太长会影响传输效率,也不能太短,太短则会重复的给服务器发送数据。

TCP为了保证⽆论在任何环境下都能⽐较⾼性能的通信因此会动态计算这个最⼤超时时间。

于是在各大操作系统中, 均以以500ms为⼀个单位进⾏控制, 每次判定超时重发的超时时间都是500ms的整数倍,或者说是2的幂次方倍。例如:

重发⼀次之后,仍然得不到应答,等待 2*500ms 后再进⾏重传,之后如果仍然得不到应答,等待4*500ms 进⾏重传。依次类推,以2的指数形式递增。累计到⼀定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接。

  • 滑动窗口机制

在上面的机制中,每发送一个数据,对端都会发送一个ACK确认收到了,而且如果丢失了则会引发重传机制。但是这又会引起一个缺点就是性能较差,也就是说传输得太慢了。那么一个一个传的太慢,干脆一次传多条数据。这样你发送数据的时候,我就能给你发送确认应答,你就不用等我的ACK之后再发送数据,节省了一部分时间。

如图,假设窗口大小是400字节。

Linux网络——传输层_第10张图片

  1. 这个滑动窗⼝,其实是操作系统开辟的一个缓冲区,在发送端这就是发送缓冲区,用来来记录当前还有哪些数据没有应答,只有确认应答过的数据才能从缓冲区删掉,删掉之后才会继续填充已经发送的数据。
  2. 在接收端还有一个接收缓冲区,当接收缓冲区的数据被拿走之后,才能将接收到的数据删掉
  3. 窗口的大小就是不用等待ACK,能发送最大数据的最大值,目前是400字节。
  4. 窗口越大,则网络的吞吐率越高。

Linux网络——传输层_第11张图片

对于多条发送,多条接收的机制,会有下面的问题。

  1. 某一条ACK丢失
  2. 某一条数据丢失

对于问题1,如果某一条ACK丢失,只要后续的ACK到达,TCP默认前面的也到达了。

对于问题2,如果某一条数据丢失,这个时候,接收端会一直发送丢失数据的ACK,如果连发三次,则发送端会再发一次该序号的数据。

Linux网络——传输层_第12张图片

  • 流量控制机制

在滑动窗口的机制下,接收端处理数据的速度是有限的,所以如果发送端发送数据太快,就会导致接收端的缓冲区被占满,然后就会丢包,然后就会重发等一系列连锁反应。

因此TCP根据接收端处理数据的能力来控制发送数据的快慢, 这个机制就叫做流量控制。

  1. 接收端将自己可以接收的数据的大小放在TCP首部的窗口大小中。通过ACK通知发送端。又一个头部字段出现。
  2. 接收端⼀旦发现⾃⼰的缓冲区快满了,就会将窗⼝⼤⼩设置成⼀个更⼩的值通知给发送端;
  3. 发送端接受到这个窗⼝之后, 就会减慢⾃⼰的发送速度;
  4. 如果接收端缓冲区满了,就会将窗⼝置为0;这时发送⽅不再发送数据,但是需要定期发送⼀个窗⼝。
  5. 探测数据段, 使接收端把窗⼝⼤⼩告诉发送端

(注意:虽然头部信息中的窗口大小是16位,也就是65535字节,其实,头部中还有一个选项,选项中有一个扩大因子M,实际窗口大小是头部中的大小左移M位的值。)

Linux网络——传输层_第13张图片

  • 拥塞控制机制

在这之前,感觉TCP的可靠传输已经基本成型了,是的。后面就是优化了。因为在前面还有一些值得优化的地方。比如:

TCP引⼊慢启动机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多⼤的速度传输数据。如果当前网络的状态不是很好,但我们不知道,还是一下子就传很大的数据,这样会加重拥堵。

  1. 因此引⼊⼀个概念程为拥塞窗口。
  2. 发送开始的时候, 定义拥塞窗⼝⼤⼩为1,先发送一个字节的数据。
  3. 每次收到⼀个ACK应答,拥塞窗⼝加1。
  4. 每次发送数据包的时候,将拥塞窗⼝和接收端主机反馈的窗⼝⼤⼩做⽐较,取较⼩的值作为实际发送的窗⼝。

像上⾯这样的拥塞窗⼝增⻓速度,是指数级别的。"慢启动" 只是指初使时慢,但是增⻓速度⾮常快。开始拥塞窗口为1,然后收到发送的第一个的ACK后,拥塞窗口加一,这个时候拥塞窗口就是2。然后就会发两个报文出去,然后就会收到两个ACK,现在就会又加二,拥塞窗口就会变成四,因此拥塞窗口的变化就是:1、2、4、8、16……。也就是指数型增长。

因此为了不让拥塞窗口增加的太快,因此增加一个标记,也可以叫做水位,也可以叫做阈值。反正就是起一个标记的作用。当窗口大小增加都这个阈值时,就变成线性增长。然后,在后面如果遇到了网络阻塞,那则TCP又开始慢启动,循环这个拥塞控制机制,但是这个阈值会变成遇到网络拥塞时拥塞窗口大小的一半。

什么时候是网络拥塞呢?当少量丢包时就触发重传机制,大量的丢包就触发拥塞控制机制。

Linux网络——传输层_第14张图片

现在用一个例子来解释这个拥塞控制机制。

  一对情侣一开始确定关系时,都很羞涩,感情升温比较慢(慢开始),但是随着时间的推移,进入了热恋期,感情呈指数增长(指数增长)。慢慢的新鲜感过去之后,有归于平淡,感情呈线性增长(阈值之后)。突然有一天,所有的委屈,不公平,忍耐爆发了,大吵了一架,互相都不理谁,于是感情归于0(网络拥塞)。时间慢慢过去,两个人突然觉得自己做的是不太对,于是两个人互相认错,慢慢的又开始坠入爱河(慢开始),因为吵了一架,再一次深入了解了自己和对方,爱的更加深沉(指数增长)。但是这次的新鲜感并没有一开始持久(拥塞窗口减小),于是感情有归于平淡(阈值之后),有一天,又因为一件小事,吵了一架,又开始了循环网络拥塞),知道没有了慢开始,没有了指数增长的感情,成了线性增长……

所以拥塞窗口机制告诉了我们,爱情并不是风华雪月,而是柴米油盐。

  • 延迟应答机制

这个就比较简单了,就是说,当接收端进行ACK应答的时候,因为会带上窗口的大小,所以干脆等一会在回答,等接收缓冲区的数据被取走一部分后,再回答。这个时候窗口也变大了。

窗口越大,传输效率就越高。

  • 捎带应答机制

我们知道,接收端不仅仅只是发送给一个ACK,有时候也要发送一些,发送端要的资源。例如客户端发起HTTP请求,要一个HTML网页时,可以让ACK做一个顺丰车,毕竟ACK只是一个bit位。

  • TCP总结

到这儿,基本上就把TCP这个boss解决了,本章博文用时10个小时。大部分都是TCP,可见TCP是多么复杂。复杂的就是它的可靠性。

有连接:

  1. 连接管理机制

可靠性:

  1. 校验和
  2. 序列号(按序到达)
  3. 确认应答
  4. 超时重发
  5. 连接管理
  6. 流量控制
  7. 拥塞控制

性能优化:

  1. 滑动窗⼝
  2. 快速重传
  3. 延迟应答
  4. 捎带应答

TCP与UDP

TCP用于可靠传输,例如文件传输等

UDP用于高速传输和实时性较高,例如游戏。

你可能感兴趣的:(Linux网络编程系列)