本文主要介绍了工作中常用的TCP/IP对应协议栈相关基础知识,科普文。
本博客所有文章: http://www.cnblogs.com/xuanku/p/index.html
TCP/IP网络协议栈分为四层, 从下至上依次是:
链路层
其实在链路层下面还有物理层, 指的是电信号的传输方式, 比如常见的双绞线网线, 光纤, 以及早期的同轴电缆等, 物理层的设计决定了电信号传输的带宽, 速率, 传输距离, 抗干扰性等等。
在链路层本身, 主要负责将数据跟物理层交互, 常见工作包括网卡设备的驱动, 帧同步(检测什么信号算是一个新帧), 冲突检测(如果有冲突就自动重发), 数据差错校验等工作。
链路层常见的有 以太网
, 令牌环网
的标准。
网络层
网络层的IP协议是构成Internet的基础。该层次负责将数据发送到对应的目标地址, 网络中有大量的路由器来负责做这个事情, 路由器往往会拆掉链路层和网络层对应的数据头部并重新封装。IP层不负责数据传输的可靠性, 传输的过程中数据可能会丢失, 需要由上层协议来保证这个事情。
传输层
网络层负责的是点到点的协议, 即只到某台主机, 传输层要负责端到端的协议, 即要到达某个进程。
典型的协议有TCP/UDP两种协议, 其中TCP协议是一种面向连接的, 稳定可靠的协议, 会负责做数据的检测, 分拆和重新按照顺序组装, 自动重发等。而UDP就只负责将数据送到对应进程, 几乎没有任何逻辑, 也就是说需要应用层自己来保证数据传输的可靠性。
应用层
即我们常见的HTTP, FTP协议等。
这四层协议对应的数据包封装如下图:
四层协议对应的通信过程如下图:
以太网数据帧格式如下:
说明如下:
在网络通信过程中, 源主机的应用程序只知道目的应用程序的IP地址, 并不知道对方主机的硬件地址, 所以在数据发送之前, 需要先找到目标及其的硬件地址, 这就是ARP协议所起的作用了。
每次在建立连接之前, 会在本地网络广播发送目的IP地址, 所有机器都会受到该请求, 目的机器发现该请求中的IP地址跟自己一样, 就把自己的硬件地址返回回去, 否则忽略该请求。
一般来说, 每台机器都维护的有一个ARP缓存表, 存储了近期的IP地址和硬件地址的映射关系, 可以用 arp -a
命令来查看缓存表中内容。
如果目的机器和本机器不在同一个网段之内的话, 会将数据发送给网关来处理, 一般网关就是路由器, 此时网关会进行IP路由, 将ARP请求发送到目的网络地址, 然后再依次将应答返回给该发起请求的机器。
IP协议数据包格式如下:
几个字段解释如下:
IP地址的一共分为如下几类:
在互联网刚出来的时候, 大部分组织都申请的B类网络地址, 导致B类地址很快就用完了, 但是A类又有很多空闲的地址, 而每个路由器又必须掌握所有网络的信息, 随着C类网络的增多, 路由器中的路由表项数也就越来越多了。
针对这种情况, 后来人们发现, 绝大部分内部网络的机器都不需要一个独立的公网IP的, 这些机器通过一个公网IP跟外部连接, 在自己的网络内部为每台机器申请一个私有IP, 内部再建设一个路由器, 做内网IP地址的定位即可。
私有IP的出现大大解决了IP浪费的问题, 所以我们日常中可以看到很多如192.168.xx这样的IP, 这些IP都只是局域网内部IP, 不会浪费IP地址。
于是, RFC1918就规定了组建局域网的私有IP地址规范:
这些私有IP地址虽然没有公网IP, 但是仍然可以通过NAT等技术来跟公网进行连接交互。
除了私有IP之外, 还有几种特殊的IP地址:
TCP协议数据包如下:
部分字段解释如下:
上图中每次连接线上的数字标记了此次数据包中的关键信息, 比如
SYN,1000(0),<mss 1460>
代表: 请求包包含SYN标记, 32位序号为1000, 不包含数据, 带有一个mss的选项, 其值为1460 SYN,8000(0),ACK,1001,<mss 1024>
代表: 请求包包含SYN和ACK标记, 32位序号为8000, 不会包含数据, 32位确认序号为1001, 同样带有mss选项 那么接下来我们看TCP协议的交互过程:
建立连接
至此, 连接建立完毕, 可以发送数据了, 该过程包含了客户端和服务器各一次请求和应答, 服务器的请求和应答放到一个包中做了, 一共包含3次包发送, 所以该过程又被称为三次握手。
交换数据
这一段主要是要理解TCP交互的序号管理逻辑, 因为是全双工协议, 即服务器和客户端可以同时像对方发送数据, 所以需要客户端和服务器各维护一个序列号。如果是半双工协议的话, 就只需要一方维护一个序号即可。
关闭连接
在建立连接的时候, 服务器的请求和应答是合并到了一个包当中。但是在关闭连接的过程中, 就必须分开两个包来, 因为客户端关闭连接之后就不能再发送数据了, 但是服务器还可以发送数据给客户端, 直到服务器也发送FIN标记。
如上讲的都是一来一回的交互, 一般情况下可能会存在一方数据发得特别快, 另一方数据发得特别慢, 这种时候如果不做控制, 势必会让慢的这方数据处理不过来从而导致丢包。
TCP协议中采用了 滑动窗口协议
来解决该问题, 类似上面的 mss
, 再增加一个新的选项 win
, 告诉对方自己的滑动窗口大小, 对方在发送数据的时候每次发送数据就知道对方到底窗口空间还够不够, 如果不够了就不发了, 从而解决了一快一慢这种问题。
如下图:
UDP协议就简单很多了, 基本上就只包含源地址, 目的地址, 长度, 校验, 数据。
交互过程也不再像TCP这样经过很复杂的建立连接和关闭连接的过程了, 就直接每次都发送数据了, 这样会有如下的一些问题:
所以如前面所说, UDP协议并不保证数据的可靠性, 他一般用于一些高性能的场景, 且需要应用层再做一些简单的封装处理。