TCP/IP协议知识科普

简介

本文主要介绍了工做中经常使用的TCP/IP对应协议栈相关基础知识,科普文。html

本博客全部文章:http://www.cnblogs.com/xuanku/p/index.htmllinux

TCP/IP网络协议栈

TCP/IP网络协议栈分为四层, 从下至上依次是:编程

  1. 链路层缓存

    其实在链路层下面还有物理层, 指的是电信号的传输方式, 好比常见的双绞线网线, 光纤, 以及早期的同轴电缆等, 物理层的设计决定了电信号传输的带宽, 速率, 传输距离, 抗干扰性等等。服务器

    在链路层自己, 主要负责将数据跟物理层交互, 常见工做包括网卡设备的驱动, 帧同步(检测什么信号算是一个新帧), 冲突检测(若是有冲突就自动重发), 数据差错校验等工做。cookie

    链路层常见的有以太网令牌环网的标准。网络

  2. 网络层并发

    网络层的IP协议是构成Internet的基础。该层次负责将数据发送到对应的目标地址, 网络中有大量的路由器来负责作这个事情, 路由器每每会拆掉链路层和网络层对应的数据头部并从新封装。IP层不负责数据传输的可靠性, 传输的过程当中数据可能会丢失, 须要由上层协议来保证这个事情。tcp

  3. 传输层高并发

    网络层负责的是点到点的协议, 即只到某台主机, 传输层要负责端到端的协议, 即要到达某个进程。

    典型的协议有TCP/UDP两种协议, 其中TCP协议是一种面向链接的, 稳定可靠的协议, 会负责作数据的检测, 分拆和从新按照顺序组装, 自动重发等。而UDP就只负责将数据送到对应进程, 几乎没有任何逻辑, 也就是说须要应用层本身来保证数据传输的可靠性。

  4. 应用层

    即咱们常见的HTTP, FTP协议等。

这四层协议对应的数据包封装以下图:

四层数据包

四层协议对应的通讯过程以下图:

四层通讯过程

链路层 以太网数据帧

以太网数据帧格式以下:

以太网数据帧

说明以下:

  1. 目的地址和源地址是指网卡的硬件地址(即MAC地址), 长度是48位, 出厂的时候固化的。
  2. 类型字段即上层协议类型, 目前有三种值: IP, ARP, RARP。
  3. 数据对应了上层协议传输的数据, 以太网规定数据大小是46~1500字节, 最大值1500即以太网的最大传输单元(MTU), 不一样网络类型有不一样MTU, 若是须要跨不一样类型链路传输的话, 就须要对数据进行从新分片。
  4. CRC是数据的校验码, 确保数据传输正确

ARP协议

在网络通讯过程当中, 源主机的应用程序只知道目的应用程序的IP地址, 并不知道对方主机的硬件地址, 因此在数据发送以前, 须要先找到目标及其的硬件地址, 这就是ARP协议所起的做用了。

每次在创建链接以前, 会在本地网络广播发送目的IP地址, 全部机器都会受到该请求, 目的机器发现该请求中的IP地址跟本身同样, 就把本身的硬件地址返回回去, 不然忽略该请求。

通常来讲, 每台机器都维护的有一个ARP缓存表, 存储了近期的IP地址和硬件地址的映射关系, 能够用arp -a命令来查看缓存表中内容。

若是目的机器和本机器不在同一个网段以内的话, 会将数据发送给网关来处理, 通常网关就是路由器, 此时网关会进行IP路由, 将ARP请求发送到目的网络地址, 而后再依次将应答返回给该发起请求的机器。

IP协议

IP协议数据包格式以下:

IP协议数据格式

几个字段解释以下:

  1. TOS, 一共有8位, 其中3位用来表示该数据包的优先级, 目前已经不用; 还有4位表示可选的服务类型(最小延迟, 最大吞吐, 最大可靠性, 最低成本), 还有一位老是0;
  2. 标志位: 用来对每一个IP包的分片关系进行标识, 用于分片和从新组装数据包;
  3. TTL(Time To Live), 是指一个数据包在网络上的最多通过多少次转发, 若是超过该数字, 就丢弃该包
  4. 8位协议, 上层可选协议为: TCP, UDP, ICMP, IGMP

IP地址的一共分为以下几类:

在互联网刚出来的时候, 大部分组织都申请的B类网络地址, 致使B类地址很快就用完了, 可是A类又有不少空闲的地址, 而每一个路由器又必须掌握全部网络的信息, 随着C类网络的增多, 路由器中的路由表项数也就愈来愈多了。

针对这种状况, 后来人们发现, 绝大部份内部网络的机器都不须要一个独立的公网IP的, 这些机器经过一个公网IP跟外部链接, 在本身的网络内部为每台机器申请一个私有IP, 内部再建设一个路由器, 作内网IP地址的定位便可。

私有IP的出现大大解决了IP浪费的问题, 因此咱们平常中能够看到不少如192.168.xx这样的IP, 这些IP都只是局域网内部IP, 不会浪费IP地址。

因而, RFC1918就规定了组建局域网的私有IP地址规范:

  1. 10.*, 前面8为是网络号, 共16,777,216个私有IP
  2. 172.16.*到172.31.*, 共1,048,576个私有IP
  3. 192.168.*, 共65536个私有IP

这些私有IP地址虽然没有公网IP, 可是仍然能够经过NAT等技术来跟公网进行链接交互。

除了私有IP以外, 还有几种特殊的IP地址:

  1. 127.*的IP地址用于本机环回测试, 这类地址的交互数据不会过网卡, 直接在内核过一遍协议就完成交互了
  2. 255.255.255.255, 这是个特殊IP, 表明在本地路由广播
  3. 主机号部分全是0的地址表明一个网络, 而不能表明某个主机(好比不能用192.168.0.0做为某台机器的IP)
  4. 主机号部分全是1的地址表明在该网络内部广播

TCP协议

数据包格式

TCP协议数据包以下:

TCP协议数据包

部分字段解释以下:

  1. 源端口号和目的端口号: 用来标注数据交互双方进程
  2. 32位序号和32位确认序号: TCP是一个可靠的交互协议, 这两个序号用作传输过程当中数据的标记, 保证数据的传输顺序以及重发
  3. URG/ACK/PSH/RST/SYN/FIN: 用来标记该请求包位于TCP链接中的什么阶段, 这6个字段下面会详细解释

交互过程

TCP数据交互

上图中每次链接线上的数字标记了这次数据包中的关键信息, 好比

  1. SYN,1000(0),<mss 1460>表明: 请求包包含SYN标记, 32位序号为1000, 不包含数据, 带有一个mss的选项, 其值为1460
  2. SYN,8000(0),ACK,1001,<mss 1024>表明: 请求包包含SYN和ACK标记, 32位序号为8000, 不会包含数据, 32位确认序号为1001, 一样带有mss选项

那么接下来咱们看TCP协议的交互过程:

  1. 创建链接

    1. 客户端发送包1, SYN表明请求创建链接, 第一个包序号为1000, 该序号的大小由操做系统内核维护, 每次发送都会自增, 自增数值就是发送的字节数, 其中mss选项表明最大段尺寸, 这是为了不没必要要的底层协议的拆包解包;
    2. 服务器返回包2, 包含的ACK 1001, 表明小于1001序号的包我都收到了, 下次请求发送大于等于1001包; 在该包中同时包含SYN 8000(0), 这段跟客户端交互的时候同样, 只是服务器端这头的序号为8000;
    3. 客户端返回包3, 里面只包含ACK 8001的包, 表明收到服务器的创建链接的包了

    至此, 链接创建完毕, 能够发送数据了, 该过程包含了客户端和服务器各一次请求和应答, 服务器的请求和应答放到一个包中作了, 一共包含3次包发送, 因此该过程又被称为三次握手。

  2. 交换数据

    1. 客户端发送包4, 包含ACK 8001, 以及序号从1001~1020的20个字节的数据
    2. 服务器返回包5, 包含ACK 1021(由于包含20个字节), 以及序号从8001~8010的10个字节数据
    3. 客户端返回包6, 由于数据已经交互完毕, 因此只包含一个ACK 8011

    这一段主要是要理解TCP交互的序号管理逻辑, 由于是全双工协议, 即服务器和客户端能够同时像对方发送数据, 因此须要客户端和服务器各维护一个序列号。若是是半双工协议的话, 就只须要一方维护一个序号便可。

  3. 关闭链接

    1. 客户端发送包7, 包含FIN标记, 1021
    2. 服务器返回包8, 只是应答ACK 1022
    3. 服务器再次返回包9, 包含FIN标记, 8011序列
    4. 客户端返回包10, 包含ACK 8012

    在创建链接的时候, 服务器的请求和应答是合并到了一个包当中。可是在关闭链接的过程当中, 就必须分开两个包来, 由于客户端关闭链接以后就不能再发送数据了, 可是服务器还能够发送数据给客户端, 直到服务器也发送FIN标记。

滑动窗口

如上讲的都是一来一回的交互, 通常状况下可能会存在一方数据发得特别快, 另外一方数据发得特别慢, 这种时候若是不作控制, 势必会让慢的这方数据处理不过来从而致使丢包。

TCP协议中采用了滑动窗口协议来解决该问题, 相似上面的mss, 再增长一个新的选项win, 告诉对方本身的滑动窗口大小, 对方在发送数据的时候每次发送数据就知道对方到底窗口空间还够不够, 若是不够了就不发了, 从而解决了一快一慢这种问题。

链接状态

以下图:

TCP链接状态图

其余状态都还好, 在工做中常会碰到TIME_WAIT链接过多的问题, 这里把TIME_WAIT状态单独拿出来讲一下。

TIME_WAIT是主动关闭方在收到被动关闭方发的FIN包以后处于的状态, 这个包是主动关闭方收到的最后一个包了, 在收到这个包以后还不能直接就把链接给关闭了, 还得等待一段时间才能关闭, 等待时间为2MSL。

为何要等待一段时间呢? 主要是两个缘由:

  1. 在收到最后一个包以后主动关闭方还得发一个ACK回去, 这个ACK可能会丢包, 若是丢包, 对方还须要从新发最后一个FIN包, 若是收到从新发过来的FIN包的时候这边厢连接已经关闭, 则会致使连接异常终止;

  2. 不过第1点也不会形成太大的问题, 毕竟数据已经正常交互了。可是有另一点风险更高, 就是若是不等待2MSL的话, 那么若是正好一个新连接又创建在相同的端口上, 那么上次的FIN包可能由于网络缘由而延时迷途的包这个时候才送达该端口, 致使下一次链接出现问题;

因此必定要有一个TIME_WAIT的状态等待一段时间, 等待的MSL时间RFC上面建议是2分钟, 可是笔者实际工做中测试每每是30秒。

可是若是你的服务是一个高并发短链接服务, TIME_WAIT可能会致使链接句柄被大量占用, 而你又相信服务内部是一个很是稳定的网络服务, 或者即便有两个链接交互出现故障也能够接受或者有应用层处理, 不但愿有那么多的TIME_WAIT状态的链接, 通常有两种方式:

  1. 在创建链接的时候使用SO_REUSEADDR选项
  2. /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协议

UDP数据格式

UDP协议就简单不少了, 基本上就只包含源地址, 目的地址, 长度, 校验, 数据。

交互过程也再也不像TCP这样通过很复杂的创建链接和关闭链接的过程了, 就直接每次都发送数据了, 这样会有以下的一些问题:

  1. 发送端只管发送数据, 若是在茫茫路由中该包丢了, 接收端并不知道
  2. 发送的多个包中, 在通过不一样路由的时候, 可能达到时序跟发送的时候并不同, 因此接收端可能拿到的是不一样顺序的包
  3. 若是发送端很快, 而接收端很慢, 接收端处理不过来, 就会丢包

因此如前面所说, UDP协议并不保证数据的可靠性, 他通常用于一些高性能的场景, 且须要应用层再作一些简单的封装处理。

参考

    1. Linux C编程一站式学习. http://docs.linuxtone.org/ebooks/C&CPP/c/index.html
相关文章
相关标签/搜索