编程进阶网编程进阶网
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 2.1域名规范和解析流程
  • 2.2Mac和IP身份的标识
  • 2.3HTTPDNS设计思想
  • 2.4TCP详细基础介绍
  • 2.5UDP详细基础介绍
  • 2.6Socket套接字设计
  • 2.7传输包的设计和原理
  • 2.8网络七层设计由来

2.4TCP详细基础介绍

目录介绍

  • 01.TCP的特点
  • 02.TCP基本概念
  • 03.TCP报文结构
  • 04.报文重点概念
  • 05.TCP靠谱协议
  • 06.三次握手分析
  • 07.如何保证可靠传输
  • 08.TCP流量控制
  • 09.TCP拥塞控制
  • 10.停止等待协议
  • 11.传输伪代码

01.TCP的特点

  • 具备的特点
    • TCP是面向连接的传输层协议。
    • TCP连接是点对点的(套接字--IP:Port到套接字)。
    • TCP提供可靠交付的服务。
    • TCP提供全双工通信。
    • 面向字节流。

02.TCP基本概念

  • 发送缓存和接受缓存:
    • 用来临时保存双向通信的数据。在发送时,应用程序将数据传送给TCP发送缓存后,就可以做自己的事情,TCP在合适的时候发送数据;在接受数据时,TCP把发送的数据放入缓存,上层应用在合适的时候读取缓存即可。
  • 滑动窗口:
    • TCP的滑动窗口以字节为单位,用3个指针进行表示。当窗口内连续报文段被确认收到后,可以将窗口向前滑动。窗口大小应小于等于缓存区的大小。
  • 滑动窗口协议:
    • 只有在接收窗口向前滑动时(与此同时也发送了确认),发送窗口才有可能向前滑动。收发两端的窗口按照以上规律不断地向前滑动,因此这种协议又称为滑动窗口协议。
      • 当发送窗口和接收窗口的大小都等于 1时,就是停止等待协议。
      • 当发送窗口大于1,接收窗口等于1时,就是回退N步协议。
      • 当发送窗口和接收窗口的大小均大于1时,就是选择重发协议。
  • TCP对应的协议
    • FTP:定义了文件传输协议,使用21端口。常说某某计算机开了FTP服务便是启动了文件传输服务。下载文件,上传主页,都要用到FTP服务。
    • Telnet:它是一种用于远程登陆的端口,用户可以以自己的身份远程连接到计算机上,通过这种端口可以提供一种基于DOS模式下的通信服务。如以前的BBS是-纯字符界面的,支持BBS的服务器将23端口打开,对外提供服务。
    • SMTP:定义了简单邮件传送协议,现在很多邮件服务器都用的是这个协议,用于发送邮件。如常见的免费邮件服务中用的就是这个邮件服务端口,所以在电子邮件设置-中常看到有这么SMTP端口设置这个栏,服务器开放的是25号端口。
    • POP3:它是和SMTP对应,POP3用于接收邮件。通常情况下,POP3协议所用的是110端口。也是说,只要你有相应的使用POP3协议的程序(例如Fo-xmail或Outlook),就可以不以Web方式登陆进邮箱界面,直接用邮件程序就可以收到邮件(如是163邮箱就没有必要先进入网易网站,再进入自己的邮-箱来收信)。
    • HTTP协议:是从Web服务器传输超文本到本地浏览器的传送协议。

03.TCP报文结构

  • 结构图如下所示,摘自网络
    • image
      image
  • 报文结构如下所示
    • 源端口、目的端口:16位长。标识出远端和本地的端口号。
    • 序列号:32位长。表明了发送的数据报的顺序,不一定从0开始。
    • 确认号:32位长。希望收到的下一个数据报的序列号,表明到序列号N-1为止的所有数据已经正确收到。
    • TCP协议数据报头长:4位长。表明TCP头中包含多少个32位字。
    • 接下来的6位未用。
    • ACK:ACK位置1表明确认号是合法的。如果ACK为0,那么数据报不包含确认信息,确认字段被省略。
    • PSH:表示是带有PUSH标志的数据。接收方因此请求数据报一到便可送往应用程序而不必等到缓冲区装满时才传送。
    • RST:用于复位由于主机崩溃或其它原因而出现的错误的连接。还可以用于拒绝非法的数据报或拒绝连接请求。
    • SYN:用于建立连接。
    • FIN:用于释放连接。
    • 窗口大小:16位长。窗口大小字段表示在确认了字节之后还可以发送多少个字节。
    • 校验和:16位长。是为了确保高可靠性而设置的。它校验头部、数据和伪TCP头部之和。
    • 紧急指针:URG=1时才有意义。
    • 可选项:长度可变,最长40个字节。
      • MMS
      • SACK:选择确认。
      • 时间戳:计算往返时间;用于处理TCP序号超过2^32的情况,又称为防止序号回绕(PAWS)。
  • TCP最小长度为20个字节。

04.报文重点概念

  • 端口号
    • 源端口号和目标端口号是不可少的,这一点和UDP是一样的。如果没有这两个端口号。数据就不知道应该发给哪个应用。
  • 包的序号
    • 为什么要给包编号呢?当然是为了解决乱序的问题。不编好号怎么确认哪个应该先来,哪个应该后到呢。编号是为了解决乱序问题。既然是社会老司机,做事当然要稳重,一件件来,面临再复杂的情况,也临危不乱。
  • 确认序号
    • 确认序号。发出去的包应该有确认,要不然我怎么知道对方有没有收到呢?如果没有收到就应该重新发送,直到送达。这个可以解决不丢包的问题。作为老司机,做事当然要靠谱,答应了就要做到,暂时做不到也要有个回复。
  • 状态位
    • 接下来有一些状态位。例如 SYN 是发起一个连接,ACK 是回复,RST 是重新连接,FIN 是结束连接等。TCP 是面向连接的,因而双方要维护连接的状态,这些带状态位的包的发送,会引起双方的状态变更。
  • 窗口大小
    • 还有一个重要的就是窗口大小。TCP要做流量控制,通信双方各声明一个窗口,标识自己当前能够的处理能力,别发送的太快,撑死我,也别发的太慢,饿死我。
    • 除了做流量控制以外,TCP还会做拥塞控制,对于真正的通路堵车不堵车,它无能为力,唯一能做的就是控制自己,也即控制发送的速度。不能改变世界,就改变自己嘛。

05.TCP靠谱协议

  • TCP 是靠谱的协议,但是这不能说明它面临的网络环境好。从IP层面来讲,如果网络状况的确那么差,是没有任何可靠性保证的,而作为IP的上一层TCP也无能为力,唯一能做的就是更加努力,不断重传,通过各种算法保证。也就是说,对于TCP来讲,IP层你丢不丢包,我管不着,但是我在我的层面上,会努力保证可靠性。
  • 通过对 TCP 头的解析,我们知道要掌握 TCP 协议,重点应该关注以下几个问题:
    • 顺序问题 ,稳重不乱;
    • 丢包问题,承诺靠谱;
    • 连接维护,有始有终;
    • 流量控制,把握分寸;
    • 拥塞控制,知进知退。

06.三次握手分析

6.1三次握手原理图

  • 第一次:发送SNY=1表示此次握手是请求建立连接的,然后seq生成一个客户端的随机数X
  • 第二次:发送SNY=1,ACK=1表示是回复请求建立连接的,然后ack=客户端的seq+1(这样客户端收到后就能确认是之前想要连接的那个服务端),然后把服务端也生成一个代表自己的随机数seq=Y发给客户端。
  • 第三次:ACK=1。 seq=客户端随机数+1,ack=服务端随机数+1(这样服务端就知道是刚刚那个客户端了) image

6.2 为何三次握手

  • 为什么建立连接需要三次握手
    • 首先非常明确的是两次握手是最基本的,第一次握手,C端发了个连接请求消息到S端,S端收到后S端就知道自己与C端是可以连接成功的,但是C端此时并不知道S端是否接收到这个消息,所以S端接收到消息后得应答,C端得到S端的回复后,才能确定自己与S端是可以连接上的,这就是第二次握手。
    • C端只有确定了自己能与S端连接上才能开始发数据。所以两次握手肯定是最基本的。
    • 那么为什么需要第三次握手呢?假设一下如果没有第三次握手,而是两次握手后我们就认为连接建立,那么会发生什么?
    • 第三次握手是为了防止已经失效的连接请求报文段突然又传到服务端,因而产生错误
  • 具体情况就是:博客
    • C端发出去的第一个网络连接请求由于某些原因在网络节点中滞留了,导致延迟,直到连接释放的某个时间点才到达S端,这是一个早已失效的报文,但是此时S端仍然认为这是C端的建立连接请求第一次握手,于是S端回应了C端,第二次握手。
    • 如果只有两次握手,那么到这里,连接就建立了,但是此时C端并没有任何数据要发送,而S端就会傻傻的等待着,造成很大的资源浪费。所以需要第三次握手,只有C端再次回应一下,就可以避免这种情况。

6.3 四次握手断开连接

  • 如图所示:
    • image
      image
  • 大概流程如下所示
    • 经过上面的建立连接图的解析,这个图应该不难看懂,这里主要有一个问题:为什么比建立连接时多了一次握手?
    • 可以看到这里服务端的ACK(回复客户端)和FIN(终止)消息并不是同时发出的,而是先ACK,然后再FIN,这也很好理解,当客户端要求断开连接时,此时服务端可能还有未发送完的数据,所以先ACK,然后等数据发送完再FIN。
    • 由于TCP连接是全双工的,因此每个方向都必须单独关闭。客户端在数据发送完毕后发送一个结束数据段FIN,且服务端也返回确认数据段ACK,此时结束了客户端到服务端的连接;然后客户端接收到服务端发送的FIN,且服务端也收到了ACK之后,自此双方的数据通信完全结束。
    • 简单说来是“先关读,后关写”,一共需要四个阶段:服务器读通道关闭->客户机写通道关闭->客户机读通道关闭->服务器写通道关闭。这样就变成了四次握手了。

6.4 问题答疑

  • TCP采用四次挥手关闭连接如图所示为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?博客
    • 这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。

07.如何保证可靠传输

  • TCP 协议如何保证可靠传输
    • 1.应用数据被分割成 TCP 认为最适合发送的数据块。
    • 2.TCP 给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。
    • 3.校验和: TCP 将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP 将丢弃这个报文段和不确认收到此报文段。
    • 4.TCP 的接收端会丢弃重复的数据。
    • 5.流量控制: TCP 连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP 使用的流量控制协议是可变大小的滑动窗口协议。 (TCP 利用滑动窗口实现流量控制)
    • 6.拥塞控制: 当网络拥塞时,减少数据的发送。
    • 7.停止等待协议 也是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。 超时重传: 当 TCP 发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。 博客

08.TCP流量控制

  • 流量控制指点对点通信量的控制,是端到端正的问题。流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收。这里是通过滑动窗口机制来实现的。发送方的发送窗口不能超过接收方的接收窗口。TCP的窗口单位是字节,不是报文段。
    • image
      image
    • 这上图中B一共进行了三次流量控制:第一次将窗口减小到300,第二次减小到100,最后减小到0,这时发送方暂停发送知道B发送一个新的窗口值为止。
    • 如果B发送了一个新的窗口值到A,但是A并没有收到,就会造成死锁。为解决这个问题,TCP为每个链接设置有一个持续计时器。只要TCP收到一个0窗口,就启动计时器。若计时器设置的时间到了,就发送一个探测报文,而接收方在确认的时候会给出一个现在的窗口值。

09.TCP拥塞控制

  • 防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。
    • 拥塞控制所要做的都有一个前提:网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机、路由器,以及与降低网络传输性能有关的所有因素。博客

9.1 慢开始和拥塞避免

  • 发送方维持一个拥塞窗口cwnd的状态变量。发送方让自己的发送窗口小于等于拥塞窗口。
    • image
      image
  • 慢开始:由小到大的逐渐增大拥塞窗口。首先将cwnd设置为一个最大报文段MMS,在收到一个对新的报文段的确认后,把拥塞窗口增加一个MMS。——指数增长
  • 拥塞避免:当慢开始到门限值(ssthresh)后,使用拥塞避免算法(cwnd每次加1)。当发现网络拥塞后,将cwnd置为1,ssthresh减半,再次执行慢开始。

9.2 快重传和快恢复

  • 快重传:当接收方收到一个失序报文段后就立即发送重复确认而不要等到自己发送数据时捎带确认。当发送方连续收到三个重复确认时,应立即重传接收方尚未收到的报文段。
  • 快恢复:与快重传结合使用。
    • 在连续收到三个重复确认时,将慢开始的ssthresh减半,这是为了防止网络拥塞( ** 接下来并不执行慢开始 ** )。
    • 由于发送方现在认为 网络很可能没有拥塞,于是接下来不执行慢开始,而是将cwnd值设置为ssthresh减半后的值,然后执行拥塞避免。

10.停止等待协议

10.1 无差错情况

  • 发送方发送分组,接收方在规定时间内收到,并且回复确认.发送方再次发送。
    • image
      image

10.2 出现差错情况

  • 停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。因此每发送完一个分组需要设置一个超时计时器,其重转时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为 自动重传请求 ARQ 。另外在停止等待协议中若收到重复分组,就丢弃该分组,但同时还要发送确认。连续 ARQ 协议 可提高信道利用率。发送维持一个发送窗口,凡位于发送窗口内的分组可连续发送出去,而不需要等待对方确认。接收方一般采用累积确认,对按序到达的最后一个分组发送确认,表明到这个分组位置的所有分组都已经正确收到了。
    • image
      image

10.3 确认丢失和确认迟到

  • 确认丢失:确认消息在传输过程丢失
    • 当A发送M1消息,B收到后,B向A发送了一个M1确认消息,但却在传输过程中丢失。而A并不知道,在超时计时过后,A重传M1消息,B再次收到该消息后采取以下两点措施:
    • 1.丢弃这个重复的M1消息,不向上层交付。
    • 2.向A发送确认消息。(不会认为已经发送过了,就不再发送。A能重传,就证明B的确认消息丢失)。
  • 确认迟到 :确认消息在传输过程中迟到
    • A发送M1消息,B收到并发送确认。在超时时间内没有收到确认消息,A重传M1消息,B仍然收到并继续发送确认消息(B收到了2份M1)。此时A收到了B第二次发送的确认消息。接着发送其他数据。过了一会,A收到了B第一次发送的对M1的确认消息(A也收到了2份确认消息)。处理如下:
    • 1.A收到重复的确认后,直接丢弃。博客
    • 2.B收到重复的M1后,也直接丢弃重复的M1。

11.传输伪代码

11.1 客户端发送数据

/**
 * 端口号
 */
private static final int PORT = 8080;

/**
 * @param serverAddress 要发送到服务端的ip
 * 1.创建socket并指定ip和端口号
 * 2.获取输出流,写数据
 * 3.释放资源
 * 4.Tcp一定要先开接收端
 */
public void send_tcp(String serverAddress) {
    try {
        Socket s = new Socket(serverAddress, PORT);
        //为了发送数据,应该获得socket流中的输出流
        OutputStream out = s.getOutputStream();
        String content = "yang";
        out.write(content.getBytes());
        s.close();
    } catch (UnknownHostException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

11.2 服务端接收数据

/**
 * TCP协议接收数据
 * 1.创建接收端的Socket对象
 * 2.监听客户端接收,返回一个Socket对象
 * 3.获取输入流,读取数据显示在控制台
 * 4.释放资源
 */
public void receive_tcp() {
    try {
        //1.建立连接,监听端口
        ServerSocket ss = new ServerSocket(PORT);
        //2.连接客户端对象
        while (true) {
            //阻塞式方法,只有客户端连接了才会继续往下运行
            Socket accept = ss.accept();
            //获取ip
            String ip = accept.getInetAddress().getHostAddress();
            //3.获取客户端发送过来的数据
            InputStream in = accept.getInputStream();
            //4.开始读取,获取输入信息
            BufferedReader bff = new BufferedReader(new InputStreamReader(in));
            //读取信息
            String line;
            final StringBuilder sb = new StringBuilder();
            while ((line = bff.readLine()) != null) {
                sb.append(line);
            }
            Message message = new Message();
            message.obj = sb.toString();
            handler.sendMessage(message);
            //5.关闭
            //ss.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

其他介绍

01.关于博客汇总链接

  • 1.技术博客汇总
  • 2.开源项目汇总
  • 3.生活博客汇总
  • 4.喜马拉雅音频汇总
  • 5.其他汇总

02.关于我的博客

  • github:https://github.com/yangchong211
  • 简书:http://www.jianshu.com/u/b7b2c6ed9284
  • csdn:http://my.csdn.net/m0_37700275
  • 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
  • 开源中国:https://my.oschina.net/zbj1618/blog
  • 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
  • 邮箱:yangchong211@163.com
  • 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
  • segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
  • 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e
贡献者: yangchong211
上一篇
2.3HTTPDNS设计思想
下一篇
2.5UDP详细基础介绍