TCP/IP协议总结
TCP/IP网络协议栈分为四层, 从下至上依次是:
链路层
其实在链路层下面还有物理层, 指的是电信号的传输方式, 比如常见的双绞线网线, 光纤, 以及早期的同轴电缆等, 物理层的设计决定了电信号传输的带宽, 速率, 传输距离, 抗干扰性等等。
在链路层本身, 主要负责将数据跟物理层交互, 常见工作包括网卡设备的驱动, 帧同步(检测什么信号算是一个新帧), 冲突检测(如果有冲突就自动重发), 数据差错校验等工作。
链路层常见的有
以太网
,令牌环网
的标准。网络层
网络层的IP协议是构成Internet的基础。该层次负责将数据发送到对应的目标地址, 网络中有大量的路由器来负责做这个事情, 路由器往往会拆掉链路层和网络层对应的数据头部并重新封装。IP层不负责数据传输的可靠性, 传输的过程中数据可能会丢失, 需要由上层协议来保证这个事情。
传输层
网络层负责的是点到点的协议, 即只到某台主机, 传输层要负责端到端的协议, 即要到达某个进程。
典型的协议有TCP/UDP两种协议, 其中TCP协议是一种面向连接的, 稳定可靠的协议, 会负责做数据的检测, 分拆和重新按照顺序组装, 自动重发等。而UDP就只负责将数据送到对应进程, 几乎没有任何逻辑, 也就是说需要应用层自己来保证数据传输的可靠性。
应用层
即我们常见的HTTP, FTP协议等。
这四层协议对应的数据包封装如下图:
四层协议对应的通信过程如下图:
链路层 以太网数据帧
以太网数据帧格式如下:
说明如下:
- 目的地址和源地址是指网卡的硬件地址(即MAC地址), 长度是48位, 出厂的时候固化的。
- 类型字段即上层协议类型, 目前有三种值: IP, ARP, RARP。
- 数据对应了上层协议传输的数据, 以太网规定数据大小是46~1500字节, 最大值1500即以太网的最大传输单元(MTU), 不同网络类型有不同MTU, 如果需要跨不同类型链路传输的话, 就需要对数据进行重新分片。
- CRC是数据的校验码, 确保数据传输正确
ARP协议
在网络通信过程中, 源主机的应用程序只知道目的应用程序的IP地址, 并不知道对方主机的硬件地址, 所以在数据发送之前, 需要先找到目标及其的硬件地址, 这就是ARP协议所起的作用了。
每次在建立连接之前, 会在本地网络广播发送目的IP地址, 所有机器都会受到该请求, 目的机器发现该请求中的IP地址跟自己一样, 就把自己的硬件地址返回回去, 否则忽略该请求。
一般来说, 每台机器都维护的有一个ARP缓存表, 存储了近期的IP地址和硬件地址的映射关系, 可以用arp -a
命令来查看缓存表中内容。
如果目的机器和本机器不在同一个网段之内的话, 会将数据发送给网关来处理, 一般网关就是路由器, 此时网关会进行IP路由, 将ARP请求发送到目的网络地址, 然后再依次将应答返回给该发起请求的机器。
IP协议
IP协议数据包格式如下:
几个字段解释如下:
- TOS, 一共有8位, 其中3位用来表示该数据包的优先级, 目前已经不用; 还有4位表示可选的服务类型(最小延迟, 最大吞吐, 最大可靠性, 最低成本), 还有一位总是0;
- 标志位: 用来对每个IP包的分片关系进行标识, 用于分片和重新组装数据包;
- TTL(Time To Live), 是指一个数据包在网络上的最多经过多少次转发, 如果超过该数字, 就丢弃该包
- 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地址规范:
- 10.*, 前面8为是网络号, 共16,777,216个私有IP
- 172.16.*到172.31.*, 共1,048,576个私有IP
- 192.168.*, 共65536个私有IP
这些私有IP地址虽然没有公网IP, 但是仍然可以通过NAT等技术来跟公网进行连接交互。
除了私有IP之外, 还有几种特殊的IP地址:
- 127.*的IP地址用于本机环回测试, 这类地址的交互数据不会过网卡, 直接在内核过一遍协议就完成交互了
- 255.255.255.255, 这是个特殊IP, 代表在本地路由广播
- 主机号部分全是0的地址代表一个网络, 而不能代表某个主机(比如不能用192.168.0.0作为某台机器的IP)
- 主机号部分全是1的地址代表在该网络内部广播
TCP协议
数据包格式
TCP协议数据包如下:
部分字段解释如下:
- 源端口号和目的端口号: 用来标注数据交互双方进程
- 32位序号和32位确认序号: TCP是一个可靠的交互协议, 这两个序号用做传输过程中数据的标记, 保证数据的传输顺序以及重发
- URG/ACK/PSH/RST/SYN/FIN: 用来标记该请求包位于TCP连接中的什么阶段, 这6个字段下面会详细解释
交互过程
上图中每次连接线上的数字标记了此次数据包中的关键信息, 比如
SYN,1000(0),<mss 1460>
代表: 请求包包含SYN标记, 32位序号为1000, 不包含数据, 带有一个mss的选项, 其值为1460SYN,8000(0),ACK,1001,<mss 1024>
代表: 请求包包含SYN和ACK标记, 32位序号为8000, 不会包含数据, 32位确认序号为1001, 同样带有mss选项
那么接下来我们看TCP协议的交互过程:
建立连接
- 客户端发送包1, SYN代表请求建立连接, 第一个包序号为1000, 该序号的大小由操作系统内核维护, 每次发送都会自增, 自增数值就是发送的字节数, 其中mss选项代表最大段尺寸, 这是为了避免不必要的底层协议的拆包解包;
- 服务器返回包2, 包含的ACK 1001, 代表小于1001序号的包我都收到了, 下次请求发送大于等于1001包; 在该包中同时包含SYN 8000(0), 这段跟客户端交互的时候一样, 只是服务器端这头的序号为8000;
- 客户端返回包3, 里面只包含ACK 8001的包, 代表收到服务器的建立连接的包了
至此, 连接建立完毕, 可以发送数据了, 该过程包含了客户端和服务器各一次请求和应答, 服务器的请求和应答放到一个包中做了, 一共包含3次包发送, 所以该过程又被称为三次握手。
交换数据
- 客户端发送包4, 包含ACK 8001, 以及序号从1001~1020的20个字节的数据
- 服务器返回包5, 包含ACK 1021(因为包含20个字节), 以及序号从8001~8010的10个字节数据
- 客户端返回包6, 因为数据已经交互完毕, 所以只包含一个ACK 8011
这一段主要是要理解TCP交互的序号管理逻辑, 因为是全双工协议, 即服务器和客户端可以同时像对方发送数据, 所以需要客户端和服务器各维护一个序列号。如果是半双工协议的话, 就只需要一方维护一个序号即可。
关闭连接
- 客户端发送包7, 包含FIN标记, 1021
- 服务器返回包8, 只是应答ACK 1022
- 服务器再次返回包9, 包含FIN标记, 8011序列
- 客户端返回包10, 包含ACK 8012
在建立连接的时候, 服务器的请求和应答是合并到了一个包当中。但是在关闭连接的过程中, 就必须分开两个包来, 因为客户端关闭连接之后就不能再发送数据了, 但是服务器还可以发送数据给客户端, 直到服务器也发送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状态的连接, 一般有两种方式:
- 在建立连接的时候使用SO_REUSEADDR选项
在
/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协议就简单很多了, 基本上就只包含源地址, 目的地址, 长度, 校验, 数据。
交互过程也不再像TCP这样经过很复杂的建立连接和关闭连接的过程了, 就直接每次都发送数据了, 这样会有如下的一些问题:
- 发送端只管发送数据, 如果在茫茫路由中该包丢了, 接收端并不知道
- 发送的多个包中, 在经过不同路由的时候, 可能达到时序跟发送的时候并不一样, 所以接收端可能拿到的是不同顺序的包
- 如果发送端很快, 而接收端很慢, 接收端处理不过来, 就会丢包
所以如前面所说, UDP协议并不保证数据的可靠性, 他一般用于一些高性能的场景, 且需要应用层再做一些简单的封装处理。
TCP/IP协议总结的更多相关文章
- 门面模式的典型应用 Socket 和 Http(post,get)、TCP/IP 协议的关系总结
门面模式的一个典型应用:Socket 套接字(Socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元.它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息: 连接使用的 ...
- OSI七层模型详解 TCP/IP协议
总结 OSI中的层 功能 TCP/IP协议族 应用层 文件传输,电子邮件,文件服务,虚拟终端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等 表示层 数据格式化,代码转 ...
- TCP/IP协议(二)tcp/ip基础知识
今天凌晨时候看书,突然想到一个问题:怎样做到持续学习?然后得出这样一个结论:放弃不必要的社交,控制欲望,克服懒惰... 然后又有了新的问题:学习效率时高时低,状态不好怎么解决?这也是我最近在思考的问题 ...
- TCP/IP协议(一)网络基础知识
参考书籍为<图解tcp/ip>-第五版.这篇随笔,主要内容还是TCP/IP所必备的基础知识,包括计算机与网络发展的历史及标准化过程(简述).OSI参考模型.网络概念的本质.网络构建的设备等 ...
- TCP/IP协议三次握手与四次握手流程解析
原文链接地址:http://www.2cto.com/net/201310/251896.html TCP/IP协议三次握手与四次握手流程解析 TCP/IP协议的详细信息参看<TCP/IP协议详 ...
- 【原创】技术往事:改变世界的TCP/IP协议(珍贵多图、手机慎点)
1.前言 作为应用层开发人员,接触最多的网络协议通常都是传输层的TCP(与之同处一层的另一个重要协议是UDP协议),但对于IP协议,对于应用程序员来说更多的印象还是IP地址这个东西,再往深一点也就很难 ...
- HTTP协议—— 简单认识TCP/IP协议
大学没读计算机专业,所以很多的专业知识都不知道.既然已经从事了IT这个行业,就势必要去了解下网络底层,虽然实际工作中这些东西用不到.高楼大厦,起于平川.不积跬步,无以至千里,不积小流,无以成江海.我现 ...
- Http TCP/IP 协议的关系
转自:http://www.cnblogs.com/ymy124/archive/2012/03/18/2404958.html 项目要求Web服务是高安全级别,在选择.net remoting,we ...
- TCP/IP协议学习(四) 基于C# Socket的Web服务器---静态资源处理
目录 1. C# Socket通讯 2. HTTP 解析引擎 3. 资源读取和返回 4. 服务器测试和代码下载 Web服务器是Web资源的宿主,它需要处理用户端浏览器的请求,并指定对应的Web资源返回 ...
- TCP/IP协议学习(五) 基于C# Socket的C/S模型
TCP/IP协议作为现代网络通讯的基石,内容包罗万象,直接去理解理论是比较困难的:然而通过实践先理解网络通讯的理解,在反过来理解学习TCP/IP协议栈就相对简单很多.C#通过提供的Socket API ...
随机推荐
- hdu6546 Function
Function \(\text{Alice}\) 有 \(n\) 个二次函数 \(F_i(x)=a_ix^2+b_ix+c_i(i \in [1,n])\). 现在他想在 \(\sum_{i=1}^ ...
- microbit之mpython的API
附录:常用API函数汇总 一.显示 display.scroll("Hello, World!") 在micro:bit点阵上滚动显示Hello, World!,其中Hello, ...
- Django的学习——全局的static和templates的使用
一.问题 首先我们在进行Django框架搭建的时候我们需要建立一个全局的变量,一是为了实现代码的复用,二是为了方便管理,如下图的样式 二.解决 1.修改setting里面的配置文件①templates ...
- WPF 高级篇 MVVM (MVVMlight) 依赖注入使用Messagebox
原文:WPF 高级篇 MVVM (MVVMlight) 依赖注入使用Messagebox MVVMlight 实现依赖注入 把弹框功能 和接口功能注入到各个插件中 使用依赖注入 先把所有的ViewMo ...
- 剑指Offer_栈的压入序列是否有对应的弹出序列
题目: 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序. 如:假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是 ...
- EF Core中的DB First与Code First
前言: 大家都习惯在程序中生成对应的model来对数据库进行操作,所以如何快速的生成数据库表的对应model,是基础之一.总结了一下在我的认知中大概是这个结构: Db first方式: 先创建好对应的 ...
- 从零开始一步一步搭建Ubuntu Server服务器、修改数据源、安装Docker、配置镜像加速器、Compose部署Gitlab服务
场景 最终目的是使用Docker Compose部署一个Gitlab服务. 效果 注: 博客: https://blog.csdn.net/badao_liumang_qizhi关注公众号 霸道的程序 ...
- 【开发笔记】- Grails框架定义一个不是数据库字段得属性
实体类 class Book{ String name String author // myfiled 我不想他在数据库中生成book表的字段 String myfield } 添加声明 class ...
- .NetCore之基础
.NetCore几大特点 这篇文章主要从.NetCore全面开源.依赖注入.包引入.跨平台.应用服务器的特点来入手.大约需要10分钟的阅读时间. 与.Net的区别 在.Net与.NetCore在代码编 ...
- uni-app插件ColorUI步骤条
1. uni-app插件ColorUI步骤条 1.1. 前言 uni-app就不介绍了,前面几篇已经有所介绍,不知道的可以翻看我前面几篇博客 ColorUI-uniApp是uni-app的一款ui组件 ...