一、概述
TCP/IP协议被认为只有四层,只有OSI认为有七层。TCP/IP的重点是软件实现,而OSI包含了整个网络通信过程。
网络层协议有:IP、ICMP、IGMP;ICMP主要是作为链路诊断用,而很少在应用层出现。IGMP是用来做广播的组协议。
FTP网络架构图:
路由器的功能就是为不同类型的物理网络提供连接,比如:以太网、令牌环网、点对点的连接和FDDI(光纤分布式接口)、SLIP;而且这些不同网络可以相互通信,不必要求是相同的链路层;这样就推导出网络层对链路层使用的协议没有感知。
路由器称为网关,它和防火墙的概念容易混淆;路由器上实际上就是运行了一个完整的操作系统,相比于一般主机只是开启了转发功能,可能还有IP分配应用程序。
应用层和网传输出是使用的端到端的协议,而网络层是逐跳协议;按照使用不同协议划分可以分为端系统和中间系统,中间系统就是指路由器这些网管设备。如下图:
连接网络的另一个途径是使用网桥,网桥是在链路层上对网络进行互联,而路由器是在网络层上。网桥可以把多个局域网组合在一起。
TCP/IP协议族的关系如下:
IP会使用ICMP,应用层ping、Traceroute也可以使用它,如何用编程接口获得这些消息呢?
ARP、RARP是以太网和令牌环网使用的特殊协议,用来转换网络地址。
网络地址可以看出很多信息,是局域网还是广域网等;IP地址分为五类,分别增加1的位数,从0个位开始,后面就是网络号依次是7的n倍;如下图:
换算成IP地址,并用点分十进制表示
InternetNIC分配网络号,主机号由系统管理员分配。
单播、多播、广播占了三类IP地址。
协议的封装过程就是不断地增加头,IPv4和TCP头都占20个字节,以太网首部占14个字节,而且在封装过程中内核使用同一个结构sk_buff来避免数据多次拷贝;如下图:
收到网络数据,再解析的过程叫做分用;从驱动程序把数据拷贝到内核物理内存开始,数据都一直在sk_buff中,每层协议的分用都使用函数指针实现。
编程接口有socket和TLI,socket是伯克利实现的,而TLI是X/open实现的;发展历史如下:
一个完整的网络布局图
IP、ARP、RARP是同级别的协议,都属于网络层;链路层应该是以太网、令牌环网、FDDI、SLIP等协议
令牌环网其实就是环回接口,数据需要从链路层驱动程序传回来;广播地址会复制一份放到环回驱动,环回接口、本机IP回传回环回接口。如下:
链路层常见协议的MTU,令牌环速度这么慢吗?MTU与线路吞吐量是两码事。
- 网络层
无连接意思是不维护后续数据报的状态信息;当IP数据报被丢弃的时候(路由器缓冲区满了),会返回给主机一个ICMP消息。IP首部如下:
接收到数据报之后,就需要查找路由表中的地址,看这个数据报应该如何处理;是本机就丢到传输层;是其它地址且配置了路由功能,则转发;否则丢弃;有时候这个功能可以在网卡上做,避免cpu和IP队列的内存浪费。
搜索失败后会发送到默认路由器。这样每个网络只需要一个路由器,而不需要为每个主机配置一个路由器。如果本机没有连接到路由器,就没有其它IP地址,就不能把数据给发送出去。
IP数据报不会给源路径发送一份数据报吗?这是如何做到的?调制解调器怎么与路由器交互?
子网与主机各占多少bit由网络管理员决定
一个总路由器拥有一个网络号,然后下面有好多自路由器构成不同的子网,主机存在于每个子网下。
为1的位是网络号和子网号部分,为0的位是主机号;有了IP地址我们就可以区分A、B、C类地址,并知道属于那个网络号;有了子网掩码就知道属于哪个子网;这就让路由寻路变得简单了。如下:
几类特殊的IP地址,主要是广播相关,如下:
- TCP传输控制协议
重复的确认可以发现分节已经丢失;窗口有一个刻度,窗口大小变化是这个最小刻度的整数倍;TCP不能否认分节,只能期待对方重传。
最大报文长度MSS是为了避免分片。
五、TCP连接的建立与终止
TCP连接与终止过程中会交换7个分节,每次分节没有携带任何数据,但是确认序号都会+1;同步分节中传输了MSS和win,只是图上没标出来。如下:
由于TCP是全双工的,一边关闭了另一端还可以正常通信,因此就造成了半关闭状态,这就需要在TCP终止的时候进行四组交换才能将两端都关闭:
连接超时的时候会尝试3次重发SYN分节,而且间隔时间增长,一共75s后返回连接超时:
网络管理员指定子网是本地还是非本地,有利于TCP选择尽可能大的MSS。MSS要求是512的整数倍,所以一般是1024,而以太网实际可以达到1460字节。
确认FIN的分节必须要应用进程接收了EOF才会发送出去吗?
应该让客户端主动关闭,这样就可以把TIME_WAIT状态留在客户端,这个时间RFC 793规定是两分钟。在这期间,TIME_WAIT是为了确认另一端的最后一个ACK。如果没有这个等待过程,可能主动关闭端崩溃后立即重启,刚连接好,结果对端迟到分节达到,然后连接就被中断了(服务器主动关闭,没有TIME_WAIT,然后客户端立马重启重连,连接成功后可能服务器收到了迟到的分节)。
ICMP端口不可达,TCP会产生RST分节,这代表是对端异常关闭。
当一端突然断电,就会形成半打开状态,如果正常端不发送数据永远无法检测到这个状态。 如果对端一直不打开TCP会自动复位;如果对端已经打开会返回一个RST分节。
两个客户端同时连接彼此也能建立连接
同时关闭两端都会有TIME_WAIT状态
还可以设置TCP选项
六、TCP的交互数据流
应用场景可以分为两类,一类为交互数据流,一类为块数据流,他们的使用频率各占一半;交互数据流小的分组比较多,块数据流每次数据都是最大长度,由于它们有不同的特点,因此需要采用不同的算法来达到各自最优的传输性能。
ACK会被故意时延200ms,以便与数据一起发送;如果TOS设置为0x10,则不会时延。块数据不会时延。
Nagle算法保证整个网络通道中只有一个未收到的ACK,收到越快就发送越快,收到越慢就发送越慢,它的收发频率是自适应的。如果每个ACK发送的时候都有待发送的数据就会一起发送,否则得等到时延定时器溢出。
TCP_NODELAY可以关闭Nagle算法,在需要及时响应的交互式网络程序中有必要。
七、TCP成块数据流
对于发送成块的数据报中,TCP使用了滑动窗口协议。它会连续好几个报文,知道把对方窗口塞满;对端收到报文后不会立马确认,而是等待几个报文后一起确认。窗口塞满后,会ack通告对端窗口为0,不能发数据了;当应用程序从缓冲区拷贝走数据后,窗口右边空出来有可以通告对端可以发数据了,这就是滑动窗口。
实际窗口可能比本次通告窗口大;本次发送数据占据了部分窗口,还可以继续发送部分数据,直到窗口占满;窗口增大后又可以发送数据;如下:
有些数据需要立即提交给应用进程,而不能停留在TCP缓存区中等待其它数据,这就需要设置PSH标识。
滑动窗口在局域网效果非常好,因为网速快,中间路由器基本不会缓存和也不会丢失分节;而在广域网中,这种疯狂发送分节的做法反而会导致网络吞吐量下降。因此必须使用拥塞窗口;它开始初始化为1,代表可以发送一个报文;收到ack后增加为2,可以发送两个报文;在增加为4,以2的指数增加,但发送报文数是个为单位。拥塞窗口是发送端的流量控制,滑动窗口是接收端的流量控制。这种算法叫慢启动。
局域网向广域网发送数据时就容易引起拥塞
紧急分节会被立即给应用进程,即使对端窗口为0;接收进程只会保存一个最新的紧急指针。
八、TCP的超时与重传
TCP使用4个定时器:坚持定时器(等待固定时间才发送数据报)、重传定时器、保活定时器、2MSL定时器。
超时重传的定时器时间并不是固定不变的,如果是这样就会引起网络拥塞。有一种超时时间估计算法,会发生指数级的时间退让;在收到一个ack之后就会更新超时估计。
发生超时、接收到重复的ack预示着拥塞发生了。
慢启动虽然能在启动的时候试探性地增加流量,但是没有减少流量的行为,因此需要配合拥塞避免算法才能动态解决拥塞问题。发生拥塞的时候,将ssthresh设置为当前窗口的一般,然后把慢启动拥塞窗口设置为1个报文段大小,开启慢启动算法,指数增加报文段;直到拥塞窗口大小超过ssthresh后,cwd增加变慢,每次只增加1/cwd,这时候就进入拥塞控制阶段,ssthresh每收到一个ack就呈2指数增加。
等待超时定时器超时才重传太慢了,如果是连续收到3个重复ack基于可以判断分组丢失,只开始重传,这个操作要比定时器溢出快一些,所以叫做快速重传算法;但是没有必要一下就把报文数量降低到1,直接将ssthresh设置为窗口的一半,cwd设置为3ssthresh,这就是快速恢复算法。这些算法也是太简单了点!
九、TCP坚持定时器和保活定时器
坚持定时器原来是做窗口探查的,询问对方接收缓存区是否有空间了,而我开始以为是等待数据一起发送的Nagle算法。
如果接收方总是通告小(比MSS小)窗口,而发送方也总是发送小数据报,这样就导致一直在发送小的数据报,这中现象就是糊涂窗口;与Nagle算法解决的问题似乎类似。