TCP协议

一、起源

TCP 是传输层的协议,全称是叫做 Transmission Control Protocol,这个协议在 IETF RFC 793 进行了定义。在互联网产生之前,我们的电脑都是相互独立的,每台机器都有着自己的操作系统并保持着自己的运行。于是,为了将这些电脑连接起来,并能够基于一种"通道"的形式进行数据、资源的传输及交互,IETF 制定了 TCP 协议。

那么,IETF又是什么?这是一个令人尊敬的技术组织,叫 Internet Engineering Task Force,即互联网工程任务组。这是一个成立于1985年的开放性组织,现在我们所提到的 HTTP、TCP、IP 这些重要的网络协议,都是出自于该组织。可以这么说,IETF 是互联网的始作俑者,没有它就没有现在繁荣的互联网了。

值得一提的是,IETF并非权贵组织,它是一个"来自民间" 的自组织、自管理的团队,非常崇尚于自由平等的精神。

整个互联网的底层机制是由一套标准网络协议组成的,为了更方便于理解,人们便定义了所谓的“网络分层模型"。在学习计算机网络课程的时候,都会提到两种网络模型,如下:

  • OSI 模型,全称为 Open System Interconnection

    即开放系统互联模型,这个是由 ISO(International Organization for Standardization) 国际标准化组织提出的。它主要是用来解决当时各个网络技术供应商在协议上无法统一的问题,通过将整个网络体系结构抽象为 7层,从最底层的物理层、数据链路层一直到最上面的应用层都做了定义。

  • TCP/IP,即 TCP/IP Protocol Suite(协议套件)是一个以TCP协议和IP协议为核心的通信模型,该模型采用协议堆栈的方式来实现许多通信协议,并将通讯体系抽象为4层。TCP/IP 模型最早发源于美国国防部(缩写为DoD)的ARPA网项目,此后就交由IETF组织来维护。

在网络层以下,物理层、数据链路层所涉及的一些技术手段及概念都相对晦涩难懂,就比如光缆、中继器、交换机等需要一些专业背景才能掌握通透。对于大多数的软件应用来说,将网络层以下的部分统称为“网络接口层" 无疑是更加简单的。

因此,OSI 模型尽管非常完善且全面,但已经被 TCP/IP 模型所淘汰,在互联网应用盛行的今天很少被提及。

二、TCP 协议

TCP 是整个 TCP/IP 协议族中最重要的传输层协议,它定义了一种面向连接的、可靠的、基于流的传输方式。HTTP 是基于 TCP 的,所以说 TCP 是整个互联网的协议其一并不为过。同时,我们在使用 HTTP 协议实现应用系统间的交互时,也经常免不了会与 TCP 打上交道。因此有必要了解一些基本机制。

TCP 的特点?

  • 首先,TCP 是基于连接的,也就是在进行数据传输之前,客户端与服务端(或者说是通信的双方)需要先建立一个可信的连接。在数据传输结束后,再通过一种协定的方式断开连接,由通信的双方释放资源。这里涉及到的,就是常说的"三次握手"、"四次挥手"

  • 其次,TCP 是可靠的,它定义了一种数据包的"超时重传机制",简单说,就是每一个数据包在发送出去后的都会等待一个响应。如果指定时间内没有收到响应,由发送方进行一定次数的重传来保证数据的可靠传输。

  • 最后,TCP 是基于流的,这是指在传输数据时应用层不需要关注数据包的边界,TCP在数据传输时会自动根据网络环境将数据进行缓冲、分组、合并这点跟基于报文的协议(UDP)是截然不同的。当然,基于流的传输也保证了数据收发的有序性,因此每个数据包都附带上一个属于当前连接的序列号。

怎么理解全双工?

全双工是通讯上的术语,一般在软件开发领域提到的并不多。这是指数据同时在两个方向上传输,TCP 是基于全双工的可信传输协议。当然 UDP 也可以实现全双工的传输,但 TCP 只能实现点对点的传输,无法支持广播或者多播(分组)

黑板:半双工的区别在于,同一时间只能有一个方向的传输

TCP 的数据包如何组织?

透视一个协议的最原始的方法就是看它的数据包,一个TCP 的报文格式如下:

 

这里面的字段就包括了:

源端口 表明发送端所使用的端口号,用于目标主机回应。

目的端口 表明要连接的目标主机的端口号。

序号 表明发送的数据包的顺序,一般为上次发送包中的顺序号+1。若该数据包是整个TCP连接中的第一个包(SYN包),则该值是随机生成的。

确认号 表明本端TCP已经接收到的数据,其值表示期待对端发送的下一个字节的序号。实际上告诉对方,在这个序号减1以前的字节已正确接收。若该数据包是整个TCP连接中的第一个包(SYN包),则确认号一般为0。

数据偏移 表示以32位(4字节)为单位的TCP分组头的总长度(首部长度),用于确定用户数据区的起始位置。在没有可变内容的情况下,TCP头部的大小为20字节,对应该值为5。

标志位 紧急标志位(URG):开启时表明此数据包处于紧急状态应该优先处理 确认标志位(ACK):开启时表明确认号有效,否则忽略确认号 推送标志位(PSH):开启时表明应该尽快交付给应用进程,而不必等到缓存区填满才推送,比如 telnet 的场景 复位标志位(RST):开启时表明TCP连接出现连接出现错误,数据包非法拒绝连接 同步标志位(SYN):开启时表明连接建立的标志 终止标志位(FIN):开启时表明释放一个连接

窗口大小 表明期望接受到的数据包字节数,用于拥塞控制。

校验和 实现对TCP报文头以及数据区进行校验。

紧急指针 在紧急状态下(URG打开),指出窗口中紧急数据的位置(末端)。

选项(可变) 用于支持一些特殊的变量,比如最大分组长度(MSS)。

填充 用于保证可变选项为32 bit的整数倍。

三、TCP 工作流程

链是指链路,这个是物理层的概念,比如光缆光纤,或是无线的电磁波。但这里所说的链路其实是网络连接的意思,即 IP 上层的概念。

那么,一个TCP 正常的通讯流程,会包含建链(建立连接)、传输数据、拆链(关闭连接),

如下图所示:

(图来自网络)

据上图所示,在进行 TCP 进行数据传输时,都不可避免的会经过这两个阶段:

  • 三次握手建立连接

  • 执行数据传输、双方读写

  • 四次挥手释放连接

下面,重点说明下建链与拆链的过程

四、 三次握手

在建立TCP连接时,需要经过三次交互,也成为三次握手(HandShake)。

1、客户端发起连接请求,发送 SYN包(SYN=i)到服务器,并进入到SYN-SEND状态,等待服务器确认 2、服务器收到SYN包后,必须确认客户的 SYN(ack=i+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器进入SYN-RECV状态 3、客户端收到服务器的SYN+ACK包,向服务器发送确认报ACK(ack=k+1),此后客户端和服务器进入ESTABLISHED状态,双方可以开始传送数据。

在谈论三次握手的时候,有几个问题是需要关注的:

问题1. 为什么是三次握手

这个问题在技术面试时屡试不爽,原话是能不能两次,或者是四次握手呢?答案就是,TCP 是可靠的传输,在建立连接时就应该经过两端的确认过程,如上面的流程, 只有在三次握手的情况下,客户端和服务端都经过了一次真正(SYN+ACK)的确认过程。这样的连接便认为是可信的。此外,如果仅仅只是两次握手,一旦网络不稳定造成 SYN 包重传则会直接导致重复建立连接,浪费资源。

问题2. 什么是syn flood攻击

syn flood 是一种经典的 ddos攻击手段,这里面用到了TCP 三次握手存在的漏洞。在上面的图中,可以看到当服务端接收到 SYN 后进入 SYN-RECV 状态,此时的连接称为半连接,同时会被服务端写入一个 半连接队列。想象一下,如果攻击者在短时间内不断的向服务端发送大量的 SYN 包而不响应,那么服务器的 半连接队列很快会被写满,从而导致无法工作。实现 syn flood 的手段,可以通过伪造源 IP 的方式,这样服务器的响应就永远到达不了客户端(握手无法完成);当然,通过设定客户端防火墙规则也可以达到同样的目的。

对 syn flood 实现拦截是比较困难的,可以通过启用 syn_cookies 的方式实现缓解,但这通常不是最佳方案。最好的办法是通过专业的防火墙来解决,基本上所有的云计算大T 都具备这个能力。关于 syn flood 可以看看这篇文章

问题3. 半连接队列和全连接队列如何调优

这里提到了一个"半连接队列"(syns queue),与其对应的还有一个 "全连接队列"(accept queue) 前者用于暂存未建立完全的连接,后者是连接在成功建立后进入的一个队列。半连接队列默认大小可以通过内核参数调整:

 
  1. sys/net/ipv4/tcp_max_syn_backlog

 

黑板:tcpmaxsynbacklog 在 syncookies 开启时是无效的,这两个选项存在冲突

 

对于全连接队列,如果服务器未能及时通过 accept 调用将其中的连接取走,会导致队列溢出(连接失效)

全连接队列的大小的内核调优方式:

  1. echo 4096 > /proc/sys/net/core/somaxconn

那么,是不是只有内核调优这种方法能影响这两个参数呢?答案是否定的。实际上,在应用层调用 socket listen 时也支持设置一个 backlog参数,这几个之间的关系如下:

  1. 半连接队列长度 = min(backlog,内核 net.core.somaxconn,内核 tcp_max_syn_backlog)

  2. 全连接队列长度 = min(backlog,内核 net.core.somaxconn)

 

黑板:一般的应用服务器如 netty、tomcat 都支持设置 backlog 参数,但是在真正进行调优时还需要配合考虑内核参数的配置。

五、 四次挥手

 

 

在释放连接时,由于TCP是全双工的,因此最后要由两端分别进行关闭,这个流程如下:

1、客户端发送一个FIN,用来关闭客户端到服务器的数据传送,客户端进入FINWAIT1状态。2、服务器收到FIN后,发送一个ACK给客户端,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),服务器进入CLOSEWAIT状态,而客户端进入FINWAIT2状态。3、服务器发送一个FIN,用来关闭服务器到客户端的数据传送,服务器进入LASTACK状态。4、客户端收到FIN后,客户端进入TIMEWAIT状态,接着发送一个ACK给服务器,确认序号为收到序号+1,服务器进入CLOSED状态,完成释放。

 

关闭连接有主动关闭和被动关闭一说,这里为了简化理解,我们以客户端作为主动关闭方,服务器为被动关闭方。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值