TCP篇

之前的总结文章:TCP简单版本介绍-三次握手等

基本认识

TCP 是⾯向连接的(⼀定是「⼀对⼀」才能连接)、可靠的、基于字节流的传输层通信协议。

RFC 793 是如何定义「连接」的:⽤于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗⼝⼤⼩称为连接。

建⽴⼀个 TCP 连接是需要客户端与服务器端达成上述三个信息的共识。

Socket:由 IP 地址和端⼝号组成

序列号:⽤来解决乱序问题等

窗⼝⼤⼩:⽤来做流量控制

TCP 四元组可以唯⼀的确定⼀个连接,四元组包括如下:

格式

序列号:在建⽴连接时由计算机⽣成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送⼀次数据,就「累加」⼀次该「数据字节数」的⼤⼩。⽤来解决⽹络包乱序问题。

确认应答号:指下⼀次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。⽤来解决不丢包的问题。

控制位

ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建⽴连接时的 SYN 包之外该位必须设置为 1 。

RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。

SYN:该位为 1 时,表示希望建⽴连接,并在其「序列号」的字段进⾏序列号初始值的设定。

FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双⽅的

主机之间就可以相互交换 FIN 位为 1 的 TCP 段。

TCP 和 UDP 区别

  1. 连接

    TCP 是⾯向连接的传输层协议,传输数据前先要建⽴连接。

UDP 是不需要连接,即刻传输数据。

  1. 服务对象

TCP 是⼀对⼀的两点服务,即⼀条连接只有两个端点。

UDP ⽀持⼀对⼀、⼀对多、多对多的交互通信

  1. 可靠性

TCP 是可靠交付数据的,数据可以⽆差错、不丢失、不重复、按需到达。

UDP 是尽最⼤努⼒交付,不保证可靠交付数据。

  1. 拥塞控制、流量控制

TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。

UDP 则没有,即使⽹络⾮常拥堵了,也不会影响 UDP 的发送速率。

  1. ⾸部开销

TCP ⾸部⻓度较⻓,会有⼀定的开销,⾸部在没有使⽤「选项」字段时是 20 个字节,如果使⽤了「选项」字段则会变⻓的。

UDP ⾸部只有 8 个字节,并且是固定不变的,开销较⼩。

  1. 传输⽅式

TCP 是流式传输,没有边界,但保证顺序和可靠。

UDP 是⼀个包⼀个包的发送,是有边界的,但可能会丢包和乱序。

  1. 分⽚不同

TCP 的数据⼤⼩如果⼤于 MSS ⼤⼩,则会在传输层进⾏分⽚,⽬标主机收到后,

也同样在传输层组装 TCP数据包,如果中途丢失了⼀个分⽚,只需要传输丢失的这个分⽚。

UDP 的数据⼤⼩如果⼤于 MTU ⼤⼩,则会在 IP 层进⾏分⽚,⽬标主机收到后,在 IP 层组装完数据,

接着再传给传输层,但是如果中途丢了⼀个分⽚,在实现可靠传输的 UDP 时则就需要重传所有的数据包,这样传输效率⾮常差,所以通常 UDP 的报⽂应该⼩于 MTU。

TCP 和 UDP 应⽤场景

由于 TCP 是⾯向连接,能保证数据的可靠性交付,因此经常⽤于:

  • FTP ⽂件传输
  • HTTP / HTTPS

由于 UDP ⾯向⽆连接,它可以随时发送数据,再加上UDP本身的处理既简单⼜⾼效,因此经常⽤于:

  • 包总量较少的通信,如 DNS 、 SNMP 等
  • 视频、⾳频等多媒体通信
  • ⼴播通信

TCP 连接建⽴

TCP 是⾯向连接的协议,所以使⽤ TCP 前必须先建⽴连接,⽽建⽴连接是通过三次握⼿来进⾏的。

TCP 的连接状态查看,在 Linux 可以通过 netstat -napt 命令查看。

为什么是三次握⼿?不是两次、四次?

Socket、序列号和窗⼝⼤⼩称为连接。

所以,重要的是为什么三次握⼿才可以初始化Socket、序列号和窗⼝⼤⼩并建⽴ TCP 连接。

接下来以三个⽅⾯分析三次握⼿的原因:

  • 三次握⼿才可以阻⽌重复历史连接的初始化(主要原因)
  • 三次握⼿才可以同步双⽅的初始序列号
  • 三次握⼿才可以避免资源浪费

阻⽌重复历史连接的初始化

同步双⽅初始序列号

四次握⼿其实也能够可靠的同步双⽅的初始化序号,但由于第⼆步和第三步可以优化成⼀步,

所以就成了「三次握⼿」。

避免资源浪费

两次握⼿会造成消息滞留情况下,服务器重复接受⽆⽤的连接请求 SYN 报⽂,⽽造成重复分配资源。

一些问题

为什么客户端和服务端的初始序列号 ISN 是不相同的?

如果⼀个已经失效的连接被重⽤了,但是该旧连接的历史报⽂还残留在⽹络中,

如果序列号相同,那么就⽆法分辨出该报⽂是不是历史报⽂,

如果历史报⽂被新的连接接收了,则会产⽣数据错乱。

每次建⽴连接前重新初始化⼀个序列号主要是为了通信双⽅能够根据序号将不属于本连接的报⽂段丢弃。

另⼀⽅⾯是为了安全性,防⽌⿊客伪造的相同序列号的 TCP 报⽂被对⽅接收。

初始序列号 ISN 是如何随机产⽣的?

起始 ISN 是基于时钟的,每 4 毫秒 + 1,转⼀圈要 4.55 个⼩时。

RFC1948 中提出了⼀个较好的初始化序列号 ISN 随机⽣成算法。

ISN = M + F (localhost, localport, remotehost, remoteport)

既然 IP 层会分⽚,为什么 TCP 层还需要 MSS 呢?

MTU :⼀个⽹络包的最⼤⻓度,以太⽹中⼀般为 1500 字节;

MSS :除去 IP 和 TCP 头部之后,⼀个⽹络包所能容纳的 TCP 数据的最⼤⻓度;

当 IP 层有⼀个超过 MTU ⼤⼩的数据(TCP 头部 + TCP 数据)要发送,那么 IP 层就要进⾏分⽚,

把数据分⽚成若⼲⽚,保证每⼀个分⽚都⼩于 MTU。把⼀份 IP 数据报进⾏分⽚以后,

由⽬标主机的 IP 层来进⾏重新组装后,再交给上⼀层 TCP 传输层。

这看起来井然有序,但这存在隐患的,那么当如果⼀个 IP 分⽚丢失,整个 IP 报⽂的所有分⽚都得重传。

因为 IP 层本身没有超时重传机制,它由传输层的 TCP 来负责超时和重传。

当接收⽅发现 TCP 报⽂(头部 + 数据)的某⼀⽚丢失后,则不会响应 ACK 给对⽅,

那么发送⽅的 TCP 在超时后,就会重发「整个 TCP 报⽂(头部 + 数据)」。

因此,可以得知由 IP 层进⾏分⽚传输,是⾮常没有效率的。

所以,为了达到最佳的传输效能 TCP 协议在建⽴连接的时候通常要协商双⽅的 MSS 值,

当 TCP 层发现数据超过MSS 时,则就先会进⾏分⽚,

当然由它形成的 IP 包的⻓度也就不会⼤于 MTU ,⾃然也就不⽤ IP 分⽚了。

经过 TCP 层分⽚后,如果⼀个 TCP 分⽚丢失后,进⾏重发时也是以 MSS 为单位,

⽽不⽤重传所有的分⽚,⼤⼤增加了重传的效率。

什么是 SYN 攻击?如何避免 SYN 攻击?

SYN 攻击

我们都知道 TCP 连接建⽴是需要三次握⼿,假设攻击者短时间伪造不同 IP 地址的 SYN 报⽂,

服务端每接收到⼀个 SYN 报⽂,就进⼊ SYN_RCVD 状态,但服务端发送出去的 ACK + SYN 报⽂,

⽆法得到未知 IP 主机的ACK 应答,

久⽽久之就会占满服务端的 SYN 接收队列(未连接队列),使得服务器不能为正常⽤户服务。

避免 SYN 攻击⽅式⼀

其中⼀种解决⽅式是通过修改 Linux 内核参数,控制队列⼤⼩和当队列满时应做什么处理。

当⽹卡接收数据包的速度⼤于内核处理的速度时,会有⼀个队列保存这些数据包。

控制该队列的最⼤值如下参数:

  • SYN_RCVD 状态连接的最⼤个数:

    net.core.netdev_max_backlog

    net.ipv4.tcp_max_syn_backlog

  • 超出处理能时,对新的 SYN 直接回报 RST,丢弃连接:

避免 SYN 攻击⽅式⼆

正常流程:

收到攻击之后:

TCP 连接断开

你可以看到,每个⽅向都需要⼀个 FIN 和⼀个 ACK,因此通常被称为四次挥⼿。

这⾥⼀点需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。

为什么挥⼿需要四次?

再来回顾下四次挥⼿双⽅发 FIN 包的过程,就能理解为什么需要四次了。

  • 关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
  • 服务器收到客户端的 FIN 报⽂时,先回⼀个 ACK 应答报⽂,⽽服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报⽂给客户端来表示同意现在关闭连接。

从上⾯过程可知,服务端通常需要等待完成数据的发送和处理,

所以服务端的 ACK 和 FIN ⼀般都会分开发送,从⽽⽐三次握⼿导致多了⼀次。

为什么 TIME_WAIT 等待的时间是 2MSL?

MSL 是 Maximum Segment Lifetime,报⽂最⼤⽣存时间,它是任何报⽂在⽹络上存在的最⻓时间,

超过这个时间报⽂将被丢弃。

因为 TCP 报⽂基于是 IP 协议的,⽽ IP 头中有⼀个 TTL 字段,是 IP 数据报可以经过的最⼤路由数,每经过⼀个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报⽂通知源主机。

MSL 与 TTL 的区别: MSL 的单位是时间,⽽ TTL 是经过路由跳数。

所以 MSL 应该要⼤于等于 TTL 消耗为 0 的时间,以确保报⽂已被⾃然消亡。

TIME_WAIT 等待 2 倍的 MSL

⽐较合理的解释是: ⽹络中可能存在来⾃发送⽅的数据包,当这些发送⽅的数据包被接收⽅处理后⼜会向对⽅发送响应,所以⼀来⼀回需要等待 2 倍的时间。

2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。

如果在 TIME-WAIT 时间内,因为客户端的 ACK没有传输到服务端,客户端⼜接收到了服务端重发的 FIN 报⽂,那么 2MSL 时间将重新计时。

在 Linux 系统⾥ 2MSL 默认是 60 秒,那么⼀个 MSL 也就是 30 秒。Linux 系统停留在 TIME_WAIT 的时

间为固定的 60 秒。

为什么需要 TIME_WAIT 状态?

主动发起关闭连接的⼀⽅,才会有 TIME-WAIT 状态。

需要 TIME-WAIT 状态,主要是两个原因:

  • 防⽌具有相同「四元组」的「旧」数据包被收到;
  • 保证「被动关闭连接」的⼀⽅能被正确的关闭,即保证最后的 ACK 能让被动关闭⽅接收,从⽽帮助其正常关闭;

原因⼀:防⽌旧连接的数据包

TCP 就设计出了这么⼀个机制,经过 2MSL 这个时间,⾜以让两个⽅向上的数据包都被丢弃,

使得原来连接的数据包在⽹络中都⾃然消失,再出现的数据包⼀定都是新建⽴连接所产⽣的。

原因⼆:保证连接正确关闭

TIME-WAIT 作⽤是等待⾜够的时间以确保最后的 ACK 能让被动关闭⽅接收,从⽽帮助其正常关闭。

TIME_WAIT 过多有什么危害

过多的 TIME-WAIT 状态主要的危害有两种:

  • 第⼀是内存资源占⽤;
  • 第⼆是对端⼝资源的占⽤,⼀个 TCP 连接⾄少消耗⼀个本地端⼝;

第⼆个危害是会造成严重的后果的,要知道,端⼝资源也是有限的,⼀般可以开启的端⼝为 32768~61000 ,也可以通过如下参数设置指定

如果发起连接⼀⽅的 TIME_WAIT 状态过多,占满了所有端⼝资源,则会导致⽆法创建新连接。

如何优化 TIME_WAIT?

更新中…………

Socket 编程

所以,监听的 socket 和真正⽤来传送数据的 socket,是「两个」 socket,⼀个叫作监听 socket,⼀个叫作已完成连接 socket。

成功连接建⽴之后,双⽅开始通过 read 和 write 函数来读写数据,就像往⼀个⽂件流⾥⾯写东⻄⼀样。

listen 时候参数 backlog 的意义?

Linux内核中会维护两个队列:

  • 未完成连接队列(SYN 队列):接收到⼀个 SYN 建⽴连接请求,处于 SYN_RCVD 状态;
  • 已完成连接队列(Accpet 队列):已完成 TCP 三次握⼿过程,处于 ESTABLISHED 状态;

accept 发⽣在三次握⼿的哪⼀步?

客户端连接服务端时,发送了什么?

客户端 connect 成功返回是在第⼆次握⼿,服务端 accept 成功返回是在三次握⼿成功之后。

正所谓知道的越多,不知道的也越多。

参考:图解网络

计算机网络-TCP篇的更多相关文章

  1. 计算机网络-HTTP篇

    目录 计算机网络-HTTP篇 HTTP的一些问题 HTTP 基本概念 常见状态码 常见字段 Get 与 Post HTTP 特性 HTTP(1.1) HTTP/1.1 HTTPS 与 HTTP HTT ...

  2. C++写Socket——TCP篇(0)建立连接及双方传输数据

    满山的红叶--飘落之时-- 最近接触了点关于用C++写socket的东西,这里总结下. 这里主要是关于TCP的,TCP的特点什么的相关介绍在我另一篇博文里,所以这里直接动手吧. 我们先在windows ...

  3. -1-7 java 网络编程基本知识点 计算机网络 TCP/IP协议栈 通信必备 tcp udp

    计算机网络 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来, 在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统. 网络编程 ...

  4. 计算机网络TCP“三次握手”

    终于有时间写这篇文章了,最近真的比较忙! TCP协议  之 “三次握手” 引言:我们知道,TCP是面向连接的协议(相较于UDP无连接的协议),会在传送数据之前先在 发送端 & 接收端 之间建立 ...

  5. [计算机网络] TCP的拥塞控制

    引言 计算机网络中的带宽.交换结点中的缓存和处理机等,都是网络的资源.在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就会变坏.这种情况就叫做拥塞. 拥塞控制就是防止过多 ...

  6. 计算机网络--TCP协议深入理解

    在近期学习计算机网络的过程中,由于知识点过于零散,琐碎,从而学习起来痛苦不堪,此贴只是总结了基于传输层的TCP协议相关的知识细节,并加入一点自己的理解,并无创新,若有理解不当之处,敬请提出,感谢! 首 ...

  7. 计算机网络-TCP连接

    TCP首部20个字节 1.为什么TCP要三次握手建立连接? TCP连接过程,客户端发送请求到服务器,服务器确认请求发送到客户端,客户端再发送确认请求到服务器 原因:简言之,为了防止失效的连接请求发送到 ...

  8. 基本套接字编程(1) -- tcp篇

    1. Socket简介 Socket是进程通讯的一种方式,即调用这个网络库的一些API函数实现分布在不同主机的相关进程之间的数据交换. 几个定义: (1)IP地址:即依照TCP/IP协议分配给本地主机 ...

  9. 计算机网络——TCP三次、四次握手详解

    三次握手:建立TCP连接 连接建立过程: B的TCP服务器进程先创建传输控制块TCB(存储了每一个连接中的一些重要信息,如:TCP连接表,到发送和接收缓存的指针,到重传队列的指针,当前的发送和接收序号 ...

随机推荐

  1. Commons-Beanutils利用链分析

    前言 本篇开始介绍 commons-beanutils 利用链,注意Commons-Beanutils 不是Commons-Collections 不要看混了,首先来看一下,什么是 commons-b ...

  2. [TensorFlow2.0]-Fashion-MNIST本地数据集及fit_generator()的使用

    本人人工智能初学者,现在在学习TensorFlow2.0,对一些学习内容做一下笔记.笔记中,有些内容理解可能较为肤浅.有偏差等,各位在阅读时如有发现问题,请评论或者邮箱(右侧边栏有邮箱地址)提醒. 若 ...

  3. Markdown插入LaTex数学公式

    本文转载自Nautilus_sailing的试试LaTeX插入数学公式,内容有所改动 今天写了一篇随笔,其中需要写几个数学式子,但是我又不想直接将公式做成图片后插入,我觉得很不美观还麻烦.但是我也不会 ...

  4. 实战爬取拷背漫画-Python

    ​  一.抓包获取链接 以爬取<前科者>为例 获取搜索链接 https://api.copymanga.com/api/v3/search/comic?limit=5&q=前科者 ...

  5. setsockopt中参数之SO_REUSEADDR的意义

    1.setsockopt中参数之SO_REUSEADDR的意义 1.一般来说,一个端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用. SO_REUSE ...

  6. 【监控】Zabbix安装

    目录 一.监控目的 二.监控方式 三.主流监控系统 四.Zabbix介绍 五.Zabbix服务端安装 5.1 环境介绍 5.2 准备系统环境 5.3 安装Nginx(源码编译安装) 5.3.1 配置N ...

  7. sqli-labs lesson5-6 布尔盲注 报错注入 延时注入

    LESSON 5: 典型的布尔盲注. 盲注:sql注入过程中,sql语句的执行结果不回显到前端,这个时候就只能用一些别的方法进行判断或者尝试,这个判断或者尝试就叫做盲注.盲注又分为:1.基于布尔SQL ...

  8. 常见web中间件漏洞(五)weblogic漏洞

    继续整理有关中间件漏洞思路(仅做简单思路整理,不是复现,复现请参考大佬们的长篇好文,会在文章中列举部分操作) WebLogic是Oracle公司出品的一个application server,确切的说 ...

  9. shell 函数返回值与字典

    shell的函数只能返回整数值,如果想让函数返回字符串可以在函数调用处为变量赋值. # 定义函数function test() { name=$1 echo "123213" } ...

  10. springcloud starter(一)

    Spring Cloud - Getting Started Example, 转载自:https://www.logicbig.com/tutorials/spring-framework/spri ...