本文主要介绍了工做中经常使用的TCP/IP对应协议栈相关基础知识,科普文。html
本博客全部文章:http://www.cnblogs.com/xuanku/p/index.htmllinux
TCP/IP网络协议栈分为四层, 从下至上依次是:编程
链路层缓存
其实在链路层下面还有物理层, 指的是电信号的传输方式, 好比常见的双绞线网线, 光纤, 以及早期的同轴电缆等, 物理层的设计决定了电信号传输的带宽, 速率, 传输距离, 抗干扰性等等。服务器
在链路层自己, 主要负责将数据跟物理层交互, 常见工做包括网卡设备的驱动, 帧同步(检测什么信号算是一个新帧), 冲突检测(若是有冲突就自动重发), 数据差错校验等工做。cookie
链路层常见的有以太网
, 令牌环网
的标准。网络
网络层并发
网络层的IP协议是构成Internet的基础。该层次负责将数据发送到对应的目标地址, 网络中有大量的路由器来负责作这个事情, 路由器每每会拆掉链路层和网络层对应的数据头部并从新封装。IP层不负责数据传输的可靠性, 传输的过程当中数据可能会丢失, 须要由上层协议来保证这个事情。tcp
传输层高并发
网络层负责的是点到点的协议, 即只到某台主机, 传输层要负责端到端的协议, 即要到达某个进程。
典型的协议有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的选项, 其值为1460SYN,8000(0),ACK,1001,<mss 1024>
表明: 请求包包含SYN和ACK标记, 32位序号为8000, 不会包含数据, 32位确认序号为1001, 一样带有mss选项那么接下来咱们看TCP协议的交互过程:
创建链接
至此, 链接创建完毕, 能够发送数据了, 该过程包含了客户端和服务器各一次请求和应答, 服务器的请求和应答放到一个包中作了, 一共包含3次包发送, 因此该过程又被称为三次握手。
交换数据
这一段主要是要理解TCP交互的序号管理逻辑, 由于是全双工协议, 即服务器和客户端能够同时像对方发送数据, 因此须要客户端和服务器各维护一个序列号。若是是半双工协议的话, 就只须要一方维护一个序号便可。
关闭链接
在创建链接的时候, 服务器的请求和应答是合并到了一个包当中。可是在关闭链接的过程当中, 就必须分开两个包来, 由于客户端关闭链接以后就不能再发送数据了, 可是服务器还能够发送数据给客户端, 直到服务器也发送FIN标记。
如上讲的都是一来一回的交互, 通常状况下可能会存在一方数据发得特别快, 另外一方数据发得特别慢, 这种时候若是不作控制, 势必会让慢的这方数据处理不过来从而致使丢包。
TCP协议中采用了滑动窗口协议
来解决该问题, 相似上面的mss
, 再增长一个新的选项win
, 告诉对方本身的滑动窗口大小, 对方在发送数据的时候每次发送数据就知道对方到底窗口空间还够不够, 若是不够了就不发了, 从而解决了一快一慢这种问题。
以下图:
其余状态都还好, 在工做中常会碰到TIME_WAIT链接过多的问题, 这里把TIME_WAIT状态单独拿出来讲一下。
TIME_WAIT是主动关闭方在收到被动关闭方发的FIN包以后处于的状态, 这个包是主动关闭方收到的最后一个包了, 在收到这个包以后还不能直接就把链接给关闭了, 还得等待一段时间才能关闭, 等待时间为2MSL。
为何要等待一段时间呢? 主要是两个缘由:
在收到最后一个包以后主动关闭方还得发一个ACK回去, 这个ACK可能会丢包, 若是丢包, 对方还须要从新发最后一个FIN包, 若是收到从新发过来的FIN包的时候这边厢连接已经关闭, 则会致使连接异常终止;
不过第1点也不会形成太大的问题, 毕竟数据已经正常交互了。可是有另一点风险更高, 就是若是不等待2MSL的话, 那么若是正好一个新连接又创建在相同的端口上, 那么上次的FIN包可能由于网络缘由而延时迷途的包这个时候才送达该端口, 致使下一次链接出现问题;
因此必定要有一个TIME_WAIT的状态等待一段时间, 等待的MSL时间RFC上面建议是2分钟, 可是笔者实际工做中测试每每是30秒。
可是若是你的服务是一个高并发短链接服务, TIME_WAIT可能会致使链接句柄被大量占用, 而你又相信服务内部是一个很是稳定的网络服务, 或者即便有两个链接交互出现故障也能够接受或者有应用层处理, 不但愿有那么多的TIME_WAIT状态的链接, 通常有两种方式:
在/etc/sysctl.conf
中加入以下内容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
而后执行/sbin/sysctl -p
生效参数。
UDP协议就简单不少了, 基本上就只包含源地址, 目的地址, 长度, 校验, 数据。
交互过程也再也不像TCP这样通过很复杂的创建链接和关闭链接的过程了, 就直接每次都发送数据了, 这样会有以下的一些问题:
因此如前面所说, UDP协议并不保证数据的可靠性, 他通常用于一些高性能的场景, 且须要应用层再作一些简单的封装处理。