网络 tcp

转载参考:

https://www.cnblogs.com/zhangyinhua/p/7617027.html

https://blog.csdn.net/weixin_45649763/article/details/104067836

OSI七层和TCP/IP四层的关系

OSI引入了服务、接口、协议、分层的概念,TCP/IP借鉴了OSI的这些概念建立TCP/IP模型。

OSI先有模型,后有协议,先有标准,后进行实践;而TCP/IP则相反,先有协议和应用再提出了模型,且是参照的OSI模型。

OSI是一种理论下的模型,而TCP/IP已被广泛使用,成为网络互联事实上的标准。

在这里有一个问题,有的书上说TCP/IP是四层有的却说是五层。其实这个问题我也上网查了一下资料。

  tcp/ip是事实标准,分4层。osi模型是国际标准,分7层。讲课的时候,一般把他们综合起来讲,就说是5层。他把网络接口层分开为数据链路层和物理层了。

OSI七层网络模型

TCP/IP四层概念模型  

对应网络协议

应用层(Application)

应用层

HTTP、TFTP, FTP, NFS, WAIS、SMTP

表示层(Presentation)

Telnet, Rlogin, SNMP, Gopher

会话层(Session)

SMTP, DNS

传输层(Transport)

传输层

TCP, UDP

网络层(Network)

网络层

IP, ICMP, ARP, RARP, AKP, UUCP

数据链路层(Data Link)

数据链路层

FDDI, Ethernet, Arpanet, PDN, SLIP, PPP

物理层(Physical)

IEEE 802.1A, IEEE 802.2到IEEE 802.11

计算机网络各层涉及协议

计算机各层网络协议 


应用层: (典型设备:应用程序,如FTP,SMTP ,HTTP) 


DHCP(Dynamic Host Configuration Protocol)动态主机分配协议,使用 UDP 协议工作,主要有两个用途:给内部网络或网络服务供应商自动分配 IP 地址,给用户或者内部网络管理员作为对所有计算机作中央管理的手段。实 现即插即用连网。 

BOOTP (BOOTstrapProtocol) 引导程序协议/ 自举协议,使用UDP 来使 一个无盘工作站自动获取配置信息。静态的配置协议  DNS    (Domain Name System )域名解析<端口号53> 

FTP   (File Transfer Protocol )文件传输协议<端口号21>减少或消除不同操作系统下处理文件的不兼容性。 

Gopher   (The Internet Gopher Protocol )网际Gopher 协议 

HTTP    (Hypertext Transfer Protocol )超文本传输协议 <端口号 80>, 面向事务的应用层协议。 

IMAP4 (Internet Message Access Protocol 4) Internet 信息访问协议的第 4 版本 

IRC   (Internet Relay Chat )网络聊天协议 

NNTP    (Network News Transport Protocol )网络新闻传输协议 

XMPP 可扩展消息处理现场协议 

POP3 (Post Office Protocol 3) 即邮局协议的第3 个版本,用于接受邮件。 

SIP()信令控制协议 

SMTP (Simple Mail Transfer Protocol )简单邮件传输协议 <端口号25> 用于发送邮件。 


SNMP (Simple Network Management Protocol),简单网络管理协议 

SSH   (Secure Shell )安全外壳协议 

TELNET     远程登录协议 <端口号23> 

RPC   (Remote Procedure Call Protocol )(RFC- 1831)远程过程调用协 议 

RTCP    (RTP Control Protocol )RTP   控制协议 

RTSP   (Real Time Streaming Protocol )实时流传输协议 

TLS   (Transport Layer Security Protocol )安全传输层协议 

SDP( Session Description Protocol )会话描述协议 

SOAP   (Simple Object Access Protocol )简单对象访问协议 

GTP 通用数据传输平台 

STUN   (Simple Traversal of UDP over NATs ,NAT      的UDP 简单穿越) 是一种网络协议 

 NTP   (Network Time Protocol )网络校时协议。 


传输层:  (典型设备:  进程和端口)       数据单元:数据段 (Segment) 

 TCP  (Transmission Control Protocol )传输控制协议提供可靠的面向连接的服务,传输数据前须先建立连接,结束后释放。可靠的全双工信道。可靠、有序、无丢失、不重复。 

 UDP (User Datagram Protocol )用户数据报协议发送数据前无需建立连接,不使用拥塞控制,不保证可靠交付,最大努力交付。 

 DCCP    (Datagram Congestion Control Protocol )数据报拥塞控制协议 

 SCTP  (STREAM CONTROL TRANSMISSION PROTOCOL )流控制传 输协议 

RTP(Real-time Transport Protocol )实时传送协议 

 RSVP   (Resource ReSer Vation Protocol )资源预留协议 

 PPTP ( Point to Point Tunneling Protocol )点对点隧道协议 

网络层: (典型设备:路由器,防火墙、多层交换机) 数据单元:数据包(Packet ) 

 IP (IPv4 · IPv6) (Internet Protocol) 网络之间互连的协议 

ARP (Address Resolution Protocol) 即地址解析协议,实现通过IP 地址得 知其物理地址。 

RARP (Reverse Address Resolution Protocol)反向地址转换协议允许局域 网的物理机器从网关服务器的 ARP 表或者缓存上请求其 IP地址。 

 ICMP (Internet Control Message Protocol )Internet 控制报文协议。它是TCP/IP 协议族的一个子协议,用于在IP 主机、路由器之间传递控制消息。 

ICMPv6 : 

 IGMP (Internet Group Management Protocol) Internet 组管理协议,是因特 网协议家族中的一个组播协议,用于 IP  主机向任一个直接相邻的路由器报 告他们的组成员情况。 

 RIP (Router information protocol) 路由信息协议是一种在网关与主机之间交换路由选择信息的标准。 

OSPF (Open Shortest Path Firs)开放式最短路径优先,分布式链路状态协议。 

 BGP(Border Gateway Protocol )边界网关协议,用来连接Internet 上独立系统的路由选择协议.采用路径向量路由选择协议。 

 IS-IS (Intermediate System to Intermediate System Routing Protocol )中间系统到中间系统的路由选择协议. 


 IPsec (IP Secure) “Internet  协议安全性”是一种开放标准的框架结构,通过使用加密的安全服务以确保在 Internet  协议 (IP)  网络上进行保密而安全的通讯。 


数据链路层: (典型设备:  网卡,网桥,交换机)            数据单元:帧 (Frame) 

ARQ(Automatic Repeat-reQuest )自动重传请求协议,错误纠正协议之一,包括停止等待ARQ 协议和连续ARQ 协议,错误侦测、正面确认、逾时重传与负面确认继以重传等机制。 

 停止等待协议: 
 CSMA/CD(Carrrier Sense Multiple Access with Collision Detection)载波监听多点接入/碰撞检测协议。总线型网络,协议的实质是载波监听和碰撞检测。载波监听即发数据前先检测总线上是否有其他计算机在发送数据,如暂时不发数据,避免碰撞。碰撞检测为计算机边发送数据边检测信道上的信号电压大小。 

PPP(Point-to-Ponit Protocol)点对点协议面向字节,由三部分组成:一个将IP 数据报封装到串行链路的方法;一个用于建立、配置和测试数据链路连接的链路控制协议

LCP(Link Control Protocol) :一套网络控制协议NCP 。 

HDLC  (High-Level Data Link Control )高级数据链路控制同步网上传输数据、面向比特的数据链路层协议。 

 ATM  (Asynchronous Transfer Mode )异步传递方式,建立在电路交换和分组交换的基础上的一种面向连接的快速分组交换技术。 “异步”是指将ATM 信元“异步插入”到同步的 SDH 比特流中。如同步插入则用户在每帧中所占的时隙相对位置固定不变。“同步”是指网络中各链路上的比特流都是受同一非常精确的主时钟的控制。Wi-Fi 、WiMAX 、DTM 、令牌环、以太网、FDDI 、帧中继、 GPRS 、 EVDO 、HSPA 、L2TP 、ISDN 


物理层:(典型设备:中继器,集线器、网线、HUB)                           数据单元:比特 (Bit) 

以太网物理层、调制解调器、PLC 、SONET/SDH 、G.709 、光导纤维、 同轴电缆、双绞线 

对于这两者,有一些简单且重要的区别,在面试中也经常被问到:

「TCP 3 次握手的过程」,「TCP 4 次挥手的过程」等考点大家可能已经比较清楚了,在此就不多做叙述。在字节跳动的面试中还问了这么一道题:

TCP 的拥塞控制原理是什么?UDP 有对应的拥塞控制功能嘛?

这个问题似乎看起来比较冷门,前段时间「BBR」这个概念很火,大家都给自己的服务器跟风加上了「BBR」并感受到了传输数据效率的提升,但是也许并不是很清楚具体的原理。那么,TCP 的拥塞控制究竟是什么呢?

什么是 TCP 拥塞控制

TCP 拥塞控制的目标是最大化利用网络上瓶颈链路的带宽。

简单来说是将网络链路比喻成一根水管,如果我们希望尽可能地使用网络传输数据,方法就是给水管注水,就有如下公式:

水管内的水的数量 = 水管的容积 = 水管粗细 × 水管长度

对应的网络名词就是:

网络内尚未被确认收到的数据包数量 = 网络链路上能容纳的数据包数量 = 链路带宽 × 往返延迟

为了保证水管不会爆管,TCP 维护一个拥塞窗口cwnd(congestion window),用来估计在一段时间内这条链路(水管中)可以承载和运输的数据(水)的数量,拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化,但是为了达到最大的传输效率,我们该如何知道这条水管的运送效率是多少呢?

一个简单的方法就是不断增加传输的水量,直到水管破裂为止(对应到网络上就是发生丢包),用 TCP 的描述就是:

只要网络中没有出现拥塞,拥塞窗口的值就可以再增大一些,以便把更多的数据包发送出去,但只要网络出现拥塞,拥塞窗口的值就应该减小一些,以减少注入到网络中的数据包数。

常见的 TCP 拥塞控制算法

本文将例举目前 Linux 内核默认的 Reno 算法和 Google 的 BBR 算法进行说明,其中基于丢包的拥塞控制算法 Reno 由于非常著名,所以常常作为教材的重点说明对象。

Reno

Reno 被许多教材(例如:《计算机网络——自顶向下的方法》)所介绍,适用于低延时、低带宽的网络,它将拥塞控制的过程分为四个阶段:慢启动、拥塞避免、快重传和快恢复,对应的状态如下所示:

  • 慢启动阶段思路是不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小,在没有出现丢包时每收到一个 ACK 就将拥塞窗口大小加一(单位是 MSS,最大单个报文段长度),每轮次发送窗口增加一倍,呈指数增长,若出现丢包,则将拥塞窗口减半,进入拥塞避免阶段;
  • 当窗口达到慢启动阈值或出现丢包时,进入拥塞避免阶段,窗口每轮次加一,呈线性增长;当收到对一个报文的三个重复的 ACK 时,认为这个报文的下一个报文丢失了,进入快重传阶段,要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方,可提高网络吞吐量约20%)而不要等到自己发送数据时捎带确认;
  • 快重传完成后进入快恢复阶段,将慢启动阈值修改为当前拥塞窗口值的一半,同时拥塞窗口值等于慢启动阈值,然后进入拥塞避免阶段,重复上述过程。

BBR

BBR 是谷歌在 2016 年提出的一种新的拥塞控制算法,已经在 Youtube 服务器和谷歌跨数据中心广域网上部署,据 Youtube 官方数据称,部署 BBR 后,在全球范围内访问 Youtube 的延迟降低了 53%,在时延较高的发展中国家,延迟降低了 80%。

BBR 算法不将出现丢包或时延增加作为拥塞的信号,而是认为当网络上的数据包总量大于瓶颈链路带宽和时延的乘积时才出现了拥塞,所以 BBR 也称为基于拥塞的拥塞控制算法(Congestion-Based Congestion Control),其适用网络为高带宽、高时延、有一定丢包率的长肥网络,可以有效降低传输时延,并保证较高的吞吐量,与其他两个常见算法发包速率对比如下:

BBR 算法周期性地探测网络的容量,交替测量一段时间内的带宽极大值和时延极小值,将其乘积作为作为拥塞窗口大小,使得拥塞窗口始的值始终与网络的容量保持一致。

所以 BBR 算法解决了两个比较主要的问题:

  • 在有一定丢包率的网络链路上充分利用带宽。
    适合高延迟、高带宽的网络链路。
  • 降低网络链路上的 buffer 占用率,从而降低延迟。
    适合慢速接入网络的用户。

总结

目前有非常多的 TCP 的拥塞控制协议,例如:

  • 基于丢包的拥塞控制:将丢包视为出现拥塞,采取缓慢探测的方式,逐渐增大拥塞窗口,当出现丢包时,将拥塞窗口减小,如 Reno、Cubic 等。
  • 基于时延的拥塞控制:将时延增加视为出现拥塞,延时增加时增大拥塞窗口,延时减小时减小拥塞窗口,如 Vegas、FastTCP 等。
  • 基于链路容量的拥塞控制:实时测量网络带宽和时延,认为网络上报文总量大于带宽时延乘积时出现了拥塞,如 BBR。
  • 基于学习的拥塞控制:没有特定的拥塞信号,而是借助评价函数,基于训练数据,使用机器学习的方法形成一个控制策略,如 Remy。

从使用的角度来说,我们应该根据自身的实际情况来选择自己机器的拥塞控制协议(而不是跟风 BBR),同时对于拥塞控制原理的掌握(尤其是掌握 Reno 的控制机理和几个重要阶段)可以加强对于网络发包机制的了解,在排查问题或面对面试的时候有更好的表现。

此外,拥塞控制只是 TCP 相关考点中的一个部分,还有许多的常见的高频考点可以顺带去看看,例如:

  • OSI 模型是什么
  • 有哪些协议是基于 TCP 的,哪些是基于 UDP 的
  • 为什么建立连接需要三次握手,而断开连接需要四次握手
  • TCP首部长度,有哪些字段
  • 三次握手过程中有哪些不安全性

tcp:

二、TCP/IP四层功能概述 

  在TCP/IP参考模型中,去掉了OSI参考模型中的会话层和表示层(这两层的功能被合并到应用层实现)。同时将OSI参考模型中的数据链路层和物理层合并为主机到网络层。下面,分别介绍各层的主要功能。

2.1、主机到网络层  

  网络接入层与OSI参考模型中的物理层和数据链路层相对应。它负责监视数据在主机和网络之间的交换。事实上,TCP/IP本身并未定义该层的协议,而由参与互连的各网络使用自己的物理层和数据链路层协议,

  然后与TCP/IP的网络接入层进行连接。地址解析协议(ARP)工作在此层,即OSI参考模型的数据链路层。
  实际上TCP/IP参考模型没有真正描述这一层的实现,只是要求能够提供给其上层-网络互连层一个访问接口,以便在其上传递IP分组。由于这一层次未被定义,所以其具体的实现方法将随着网络类型的不同而不同。 

2.2、网络互连层  

  网络互连层是整个TCP/IP协议栈的核心。它的功能是把分组发往目标网络或主机。同时,为了尽快地发送分组,可能需要沿不同的路径同时进行分组传递。因此,分组到达的顺序和发送的顺序可能不同,这就需要上层必须对分组进行排序。  
  网络互连层定义了分组格式和协议,即IP协议(Internet Protocol)。  
  网络互连层除了需要完成路由的功能外,也可以完成将不同类型的网络(异构网)互连的任务。除此之外,网络互连层还需要完成拥塞控制的功能。

2.3、传输层  

    在TCP/IP模型中,传输层的功能是使源端主机和目标端主机上的对等实体可以进行会话。在传输层定义了两种服务质量不同的协议。即:传输控制协议TCP(transmission control protocol)和用户数据报协议UDP(user datagram protocol)。  
  TCP协议是一个面向连接的、可靠的协议。它将一台主机发出的字节流无差错地发往互联网上的其他主机。在发送端,它负责把上层传送下来的字节流分成报文段并传递给下层。

  在接收端,它负责把收到的报文进行重组后递交给上层。TCP协议还要处理端到端的流量控制,以避免缓慢接收的接收方没有足够的缓冲区接收发送方发送的大量数据。  
  UDP协议是一个不可靠的、无连接协议,主要适用于不需要对报文进行排序和流量控制的场合。  

2.3、应用层 

   TCP/IP模型将OSI参考模型中的会话层和表示层的功能合并到应用层实现。  
  应用层面向不同的网络应用引入了不同的应用层协议。其中,有基于TCP协议的,如文件传输协议(File Transfer Protocol,FTP)、虚拟终端协议(TELNET)、超文本链接协议(Hyper Text Transfer Protocol,HTTP),也有基于UDP协议的。

IP报文格式

  IP协议是TCP/IP协议族中最为核心的协议。它提供不可靠、无连接的服务,也即依赖其他层的协议进行差错控制。

  在局域网环境,IP协议往往被封装在以太网帧中传送。

TCP/ip :

而所有的TCP、UDP、ICMP、IGMP数据都被封装在IP数据报中传送。

分析:   

    1)版本(Version)字段:占4比特。用来表明IP协议实现的版本号,当前一般为IPv4,即0100。  
    2)报头长度(Internet Header Length,IHL)字段:占4比特。是头部占32比特的数字,包括可选项。普通IP数据报(没有任何选项),该字段的值是5,即160比特=20字节。此字段最大值为60字节。        

    3)服务类型(Type of Service ,TOS)字段:占8比特。其中前3比特为优先权子字段(Precedence,现已被忽略)。第8比特保留未用。第4至第7比特分别代表延迟、吞吐量、可靠性和花费。
      当它们取值为1时分别代表要求最小时延、最大吞吐量、最高可靠性和最小费用。这4比特的服务类型中只能置其中1比特为1。可以全为0,若全为0则表示一般服务。服务类型字段声明了数据报被网络系统传输时可以被怎样处理。
      例如:TELNET协议可能要求有最小的延迟,FTP协议(数据)可能要求有最大吞吐量,SNMP协议可能要求有最高可靠性,NNTP(Network News Transfer Protocol,网络新闻传输协议)可能要求最小费用,而ICMP协议可能无特殊要求(4比特全为0)。
      实际上,大部分主机会忽略这个字段,但一些动态路由协议如OSPF(Open Shortest Path First Protocol)、IS-IS(Intermediate System to Intermediate System Protocol)可以根据这些字段的值进行路由决策。

    4)总长度字段:占16比特。指明整个数据报的长度(以字节为单位)。最大长度为65535字节。  
    5)标志字段:占16比特。用来唯一地标识主机发送的每一份数据报。通常每发一份报文,它的值会加1。  
    6)标志位字段:占3比特。标志一份数据报是否要求分段。  
    7)段偏移字段:占13比特。如果一份数据报要求分段的话,此字段指明该段偏移距原始数据报开始的位置。  
    8)生存期(TTL:Time to Live)字段:占8比特。用来设置数据报最多可以经过的路由器数。由发送数据的源主机设置,通常为32、64、128等。每经过一个路由器,其值减1,直到0时该数据报被丢弃。  
    9)协议字段:占8比特。指明IP层所封装的上层协议类型,如ICMP(1)、IGMP(2) 、TCP(6)、UDP(17)等。  
    10)头部校验和字段:占16比特。内容是根据IP头部计算得到的校验和码。计算方法是:对头部中每个16比特进行二进制反码求和。(和ICMP、IGMP、TCP、UDP不同,IP不对头部后的数据进行校验)。  
    11)源IP地址、目标IP地址字段:各占32比特。用来标明发送IP数据报文的源主机地址和接收IP报文的目标主机地址。  
    12)可选项字段:占32比特。用来定义一些任选项:如记录路径、时间戳等。这些选项很少被使用,同时并不是所有主机和路由器都支持这些选项。可选项字段的长度必须是32比特的整数倍,如果不足,必须填充0以达到此长度要求。

TCP数据段格式

  TCP是一种可靠的、面向连接的字节流服务。源主机在传送数据前需要先和目标主机建立连接。然后,在此连接上,被编号的数据段按序收发。同时,要求对每个数据段进行确认,保证了可靠性。

  如果在指定的时间内没有收到目标主机对所发数据段的确认,源主机将再次发送该数据段。

分析:

    1)源、目标端口号字段:占16比特。TCP协议通过使用"端口"来标识源端和目标端的应用进程。端口号可以使用0到65535之间的任何数字。在收到服务请求时,操作系统动态地为客户端的应用程序分配端口号。

      在服务器端,每种服务在"众所周知的端口"(Well-Know Port)为用户提供服务。
    2)顺序号字段:占32比特。用来标识从TCP源端向TCP目标端发送的数据字节流,它表示在这个报文段中的第一个数据字节。  
    3)确认号字段:占32比特。只有ACK标志为1时,确认号字段才有效。它包含目标端所期望收到源端的下一个数据字节。  
    4)头部长度字段:占4比特。给出头部占32比特的数目。没有任何选项字段的TCP头部长度为20字节;最多可以有60字节的TCP头部。  
    5)标志位字段(U、A、P、R、S、F):占6比特。各比特的含义如下:  
      URG:紧急指针(urgent pointer)有效。  
      ACK:确认序号有效。  
      PSH:接收方应该尽快将这个报文段交给应用层。  
      RST:重建连接。  
      SYN:发起一个连接。  
      FIN:释放一个连接。  
    6)窗口大小字段:占16比特。此字段用来进行流量控制。单位为字节数,这个值是本机期望一次接收的字节数。  
    7)TCP校验和字段:占16比特。对整个TCP报文段,即TCP头部和TCP数据进行校验和计算,并由目标端进行验证。  
    8)紧急指针字段:占16比特。它是一个偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。  
    9)选项字段:占32比特。可能包括"窗口扩大因子"、"时间戳"等选项

在TCP层,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG.

其中,对于我们日常的分析有用的就是前面的五个字段。

 它们的含义是:

SYN表示建立连接,

FIN表示关闭连接,

ACK表示响应,

PSH表示有 DATA数据传输,

RST表示连接重置。

 其中,ACK是可能与SYN,FIN等同时使用的,比如SYN和ACK可能同时为1,它表示的就是建立连接之后的响应,

 如果只是单个的一个SYN,它表示的只是建立连接。

TCP的几次握手就是通过这样的ACK表现出来的。

 但SYN与FIN是不会同时为1的,因为前者表示的是建立连接,而后者表示的是断开连接。

RST一般是在FIN之后才会出现为1的情况,表示的是连接重置。

 一般地,当出现FIN包或RST包时,我们便认为客户端与服务器端断开了连接;而当出现SYN和SYN+ACK包时,我们认为客户端与服务器建立了一个连接。

PSH为1的情况,一般只出现在 DATA内容不为0的包中,也就是说PSH为1表示的是有真正的TCP数据包内容被传递。

TCP的连接建立和连接关闭,都是通过请求-响应的模式完成的。

套接字

  在每个TCP、UDP数据段中都包含源端口和目标端口字段。有时,我们把一个IP地址和一个端口号合称为一个套接字(Socket),而一个套接字对(Socket pair)可以唯一地确定互连网络中每个TCP连接的双方(客户IP地址、客户端口号、服务器IP地址、服务器端口号)。
  
    
           常见协议和对应的服务端口号

  注意:不同的应用层协议可能基于不同的传输层协议,如FTP、TELNET、SMTP协议基于可靠的TCP协议。TFTP、SNMP、RIP基于不可靠的UDP协议。  
  同时,有些应用层协议占用了两个不同的端口号,如FTP的20、21端口,SNMP的161、162端口。这些应用层协议在不同的端口提供不同的功能。如FTP的21端口用来侦听用户的连接请求,而20端口用来传送用户的文件数据。

  再如,SNMP的161端口用于SNMP管理进程获取SNMP代理的数据,而162端口用于SNMP代理主动向SNMP管理进程发送数据。  
  还有一些协议使用了传输层的不同协议提供的服务。如DNS协议同时使用了TCP 53端口和UDP 53端口。DNS协议在UDP的53端口提供域名解析服务,在TCP的53端口提供DNS区域文件传输服务。

wireshark 抓包

tcp握手与挥手

Packlist List(数据包列表)、Packet Details(数据包细节)、Packet Bytes(数据包字节)

package list

Packet Details(数据包细节)、Packet Bytes(数据包字节)

frame: 帧。 wireshark分析基础,单元。

包含:

网卡mac地址信息。mac to mac

IP:ip to ip

TCP:port to port

seq:sequence,win:windowssize, mss:maximum segment size。len还不清楚。把len也补充了,len是发送文件TCP报文段Datas段的长度

为什么建立连接是三次握手,而关闭连接却是四次挥手呢?

这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方是否现在关闭发送数据通道,需要上层应用来决定,因此,己方ACK和FIN一般都会分开发送。

四次挥手

前提条件 ,数据传输完成了。

TCP连接是双向传输的对等的模式,就是说双方都可以同时向对方发送或接收数据。当有一方要关闭连接时,会发送指令告知对方,我要关闭连接了。这时对方会回一个ACK,此时一个方向的连接关闭。但是另一个方向仍然可以继续传输数据,等到发送完了所有的数据后,会发送一个FIN段来关闭此方向上的连接。接收方发送ACK确认关闭连接。注意,接收到FIN报文的一方只能回复一个ACK, 它是无法马上返回对方一个FIN报文段的,因为结束数据传输的“指令”是上层应用层给出的,我只是一个“搬运工”,我无法了解“上层的意志”

感觉有可能三次握手,抓包发现

流量控制和阻塞控制实例:

       可以用一个例子来说明这种区别。设某个光纤网络的链路传输速率为1000Gbit/s。有一台巨型计算机向一台个人电脑以1Gbit/s的速率传送文件。显然,网络本身的带宽是足够大的,因而不存在产生拥塞的问题。但流量控制却是必须的,因为巨型计算机必须经常停下来,以便使个人电脑来得及接收。(流量控制

       但如果有另一个网络,其链路传输速率为1Mbit/s,而有1000台大型计算机连接在这个网络上,假定其中的500台计算机分别向其余的500台计算机以100kbit/s的速率发送文件。那么现在的问题已不是接收端的大型计算机是否来得及接收,而是整个网络的输入负载是否超过网络所能承受的。(阻塞控制

tcp 协议小结

转载:https://luoguochun.cn/post/2016-09-23-tcp-fuck/

tcp报文SYN ACK的计算如下:

1
2
3
     A -> B SYN J ACK K LEN L
     B -> A SYN K ACK J+L LEN M
     A -> B SYN J+L ACK K+M

需要注意到的是,注意,对于DATA LEN为0的,发送的SYN包和FIN包,需要消耗一个序号。为了提高传送的效率,ACK是支持累计的,也就是说没必要对每个SYN进行ACK。如:发送端连续发送3个报文,那么接收端收到3个报文后,可以直接应答一个ACK

tcp标志位

  1. CWR(Congestion Window Reduced) & ECN(ECN-Echo, Explicit Congestion Notification) CWR 阻塞窗口已减少,意思是告诉对端我已经按照你的要求,进行阻塞窗口减少了,并启动阻塞算法来控制我的发包速度; ECN 显式阻塞窗口通知,意思通知发送方,我接收的报文出现了阻塞,请控制发包速度。也就是说,CWR 和 ECN 必须配合使用,CWR 是收到 ECN 的应答。此外,在tcp三次握手时,这两个标志表明tcp端是否支持ECN。如果建立连接一方支持,则在发送的SYN包,将 ECN 标志置为1,如果服务端也支持,则在ACK包只设置ECN。缘由:tcp建立连接后,报文经过经过路由或网关等网络设备后,在路由器或网关等网络设备出现阻塞时,路由器或网关等设备设置IP层的某个标志表明出现阻塞,这样接收可以明确知道报文出现了阻塞。然而,需要知道阻塞进行阻塞控制的是报文发送方而非接收方。所以接收方会在ACK报文中设置ECN标志,同时发送方在ACK中设置CWR标志,表明已经收到ECN,并进行了减少阻塞窗口操作和启用阻塞算法。

  2. URG(Urgent) 这就是传说中的带外数据。因为tcp是没有消息边界的,假如有一种情况,你已经发送了一些数据,但是此时,你要发送一些数据优先处理,就可以设置这些标志。同时如果设置了这个标志,紧急指针(报文头中, Urgent Pointer(16Bit)部分)也会设置为相应的偏移。当接受方收到URG数据时,不缓存在接收窗口,直接往上传给上层。具体的使用带外数据大体的方法,就是,调用sendrecv是要加上MSG_OOB参数。同时接收方要处理SIGURG信号。使用MSG_OOB是需要注意: 1) 紧急指针只能标示一个字节数据,所以如果发送带外数据多于一个字节,其他数据将当成是正常的数据。 2) 接收端需要调用fcntl(sockfd,F_SETOWN, getpid());,对socket描述符号进行宿主设置,否则无法捕获SIGURG信号。 3) 如果设置选项SO_OOBINLINE,那么将不能使用MSG_OOB参数接收的报文(调用报错),紧急指针的字符将被正常读出来,如果需要判断是否紧急数据,则需要提前判断:ioctl (fd,SIOCATMARK,&flag);if (flag) {read(sockfd,&ch,1);。 不过,据说这个带外数据在实际上,用得很少。

  3. PSH(Push) tcp报文的流动,先是发送方塞进发送方的缓存再发送;同样接收方是先塞到接收方的缓存再投递到应用。PSH标志的意思是,无论接收或发送方,都不用缓存报文,直接接收投递给上层应用或直接发送。PSH标志可以提供报文发送的实时性。如果设置了SO_NODELAY选项(也就是关闭Nagle算法),可以强制设置这个标志。

  4. SYN(Synchronize), ACK(Acknowledgement), FIN(Finish)和 RST(Reset) 这几个标记比较容易理解。SYN, Synchronize sequence numbers。ACKAcknowledgement Number有效,应答标记。FIN,发送端结束发送。RST连接不可达。

tcp 选项(不完全)

tcp 除了20字节基本数据外,后面还包括了最多40个字节的tcp的选项。tcp选项一般存储为kind/type(1byte) length(1byte) value的格式式,不同的选项具体格式有所不同。这里简单罗列一些常见的tcp选项并做简单介绍。

  1. MSS(Maximum Segment Size) tcp报文最大传输长读,tcp在三次握手建立阶段,在SYN报文交互该值,注意的是,这个数值并非协商出来的,而是由网络设备属性得出。MSS一个常见的值是1460(MTU1500 - IP头部 - TCP头部)。

  2. SACK(Selective Acknowledgements) 选择ACK,用于处理segment不连续的情况,这样可以减少报文重传。比如: A 向B发送4个segment,B收到了1,2,4个segment,网络丢失了3这个segment。B收到1,2segment后,回应ACK 3,表示1,2这两个ACK已经收到,同时在选项字段里面,包括4这个段,表示4这个segment也收到了。于是A就重传3这个segment,不必重传4这个segment。B收到3这个segment后,直接ACK 5,表明3,4都收到了。

  3. WS(Window Scale) 在tcp头部,Window Size(16Bit)表面接收窗口大小,但是对于现代网络而言,这个值太小了。所以tcp通过选项来增加这个窗口的值。WS值的范围0~14,表示Window Size(16Bit)数值先向左移动的位数。这样实际上窗口的大小可达31位。在程序网络设计时,有个SO_RECVBUF,表示设置接收缓冲的大小,然而需要注意的是,这个值和接收窗口的大小不完全相等,但是这个数值和接收窗口存在一定的关系,在内核配置的范围内,大小比较接近。

  4. TS(Timestamps) Timestamps在tcp选项中包括两个32位的timestamp: TSval(Timestamp value)和TSecr(Timestamp Echo Reply)。如果设置了TS这个选项,发送方发送时,将当前时间填入TSval,接收方回应时,将发送方的TSval填入TSecr即可(注意发送或接收都有设置TSvalTSecr )。TS 选项的存在有两个重要作用:一是可以更加精确计算RTT(Round-Trip-Time),只需要在回应报文里面用当前时间减去TSecr即可;二是PAWS(Protection Against Wrapped Sequence number, 防止sequence回绕),什么意思呢?比如说,发送大量的数据:0-10G,假设segment比较大为1G而且sequence比较小为5G,接收端接收1,3,4,5数据段正常接收,收到的发送时间分别1,3,4,5,第2 segment丢失了,由于SACK,导致2被重传,在接收6时,sequence由于回绕变成了1,这时收到的发送时间为6,然后又收到迷途的2,seq为2,发送时间为2,这个时间比6小,是不合法的,tcp直接丢弃这个迷途的报文。

  5. UTO(User Timeout) UTO指的是发送SYN,收到ACK的超时时间,如果在UTO内没有收到,则认为对端已挂。 在网络程序设计的时候,为了探测对端是否存活,经常涉及心跳报文,通过tcp的keepalive和UTO机制也可以实现,两者的区别是,前者可以通过心跳报文实时知道对端是否存活,二后者只有等待下次调用发送或接收函数才可以断定: 1) SO_KEEPALIVE相关选项 设置SO_KEEPALIVE 选项,打开keepalive机制。 设置TCP_KEEPIDLE 选项,空闲时间间隔启动keepalive机制,默认为2小时。 设置TCP_KEEPINTVL选项,keepalive机制启动后,每隔多长时间发送一个keepalive报文。默认为75秒。 设置TCP_KEEPCNT选项,设置发送多少个keepalive数据包都没有正常响应,则断定对端已经崩溃。默认为9。 由于tcp有超时重传机制,如果对于ACK丢失的情况,keepalive机制将有可能失效。

2) TCP_USER_TIMEOUT相关选项 TCP_USER_TIMEOUT选项的函义是多久没有收到ACK则认为对端已经挂了。

配合SO_KEEPALIVETCP_USER_TIMEOUT选项,可以利用tcp机制实现探测对端存活。

tcp状态机

tcp在每个时刻都存在于一个特定的状态(CLOSED状态为假想状态),这里的状态和netstat显示的状态是一致的,各个状态以及状态转换如下: TCP状态机

 TIME_WAIT也称为2MSL(Maximum Segment Lifetime)状态,它可以保证对端发送最后的FIN(重发的),能够响应ACK,另外一个含是,保证端口在2MSL端口不被重发使用。在服务端编程的时候,我们通常会使用SO_REUSEADDR这个选项,这样可以避免如果服务端进入TIME_WAIT状态后,可以及时重启。 FIN_WAIT_2状态是在发送FIN,接收到ACK时,进入的状态,如果对端没有发送FIN那么,将无法进入TIME_WAIT状态,这时对端一直是CLOSE_WAIT状态,当服务器出现大量的FIN_WAIT_2 或 CLOSE_WAIT状态时,一般都是被动关闭那端忘记了调用close函数关闭socket。

在这里插入图片描述

在这里插入图片描述

TCP数据传输

  1. 滑动窗口 在tcp头部,窗口大小占用16位,再加上WS选项,实际上可以达31位。已经建立连接的TCP双方,都维护两个窗口,分别为接收窗口和接收窗口。

1.1 发送方窗口 发送方窗口 如图示,在任意一时刻,发送方的发送窗口数据可分4大类:1) 已经发送并收到ACK的,2) 已经发送并未收到ACK的,3) 准备发送的,4) 未发送的。第1和第2类数据之间的边界称为左边界;第3和第4类之间的数据称之为右边界。第2和第3类数据之间的窗口称之为Offered Window,是接收方通告的窗口大小。左边界向做右移动,称为窗口合拢;右边界向右移动,称为窗口张开;右边界向左移动,称为窗口的收缩(实际tcp实现不一定有)。如果左边界到达右边界,那么窗口为0,不能发送任何数据。当窗口变为0时,这里存在一个问题:因为接收方的窗口大小是通过ACK告知的,如果窗口为0了,那么哪里来的ACK呢?解决的办法是,发送方会发送ZWP(Zero Windonw Probe)的报文给接收端,让接收端应答ACK告知窗口大小,当发送方发送3次ZWP后(一般设置3次,每次给30~60秒),如果窗口依然是0,那么有些tcp实现将关闭连接。在当发送方收到发送数据的ACK时,左边界向右合拢或窗口向右移动。注意到,只有收到ACK时,左边界才会右移。

1.2 接收方窗口 接收方窗口 如图示,类似发送方窗口,接收方窗口分为3大类,1) 已经接收并发送ACK的,2) 将接收存储的,3) 不能接收的。第1和2类之间的边界称为左边界,第2和第3类数据之间的边界称之为右边界。当接收方收到报文的SEQ小与左边界,则当做是重发报文直接丢弃;当接收的报文的SEQ大于右边界,则认为是溢出也直接丢弃,只有报文在左边界右边界 之间的报文才允许接收。如果在左边界右边界 之间收到的非连续的报文(由于SACK,报文将缓存),那么左边界并不会向右移动,等待重传数据连续后,才移动。

由于滑动窗口的这些特性,接收方可以进行窗口的控制,通过告知对方窗口大大小,让发送方进行控制调整,从而具备流量控制功能。

  1. 糊涂窗口综合症(SWS) 如果建立连接的双方,当发送方产生的数据速度很慢,或者接收发消耗的数据很慢或者两者都有,这样会导致发送方向接收方发送极少量的数据,接收方回应很小的窗口。这样就会导致网络上存在大量的小数据包,tcp头部至少占20字节,加上IP头部的20字节,这个传输一个小包,耗费这么大的网络资源,这样是很不经济的,出现的这种情况称之为糊涂窗口综合症Silly Window Syndrome(SWS)SWS导致网络利用的低效率。SWS可由发送方或接收方造成,解决方法: 2.1 发送方Nagle算法 Nagle算法最多允许有一个为确认的未完成小分组(小于MSS),在该分组的ACK到达之前,不能发送其他小分组。也就是说,如果对端ACK回应的很快的话,Nagle算法并不会合并多少数据包(也就是说并不会启用),在“低速”网络环境里面才会出现更多的小分组合并发送。tcp协议默认是打开 Nagle算法的,发送报文条件是: 1) 如果包长度达到MSS,则允许发送; 2) 如果该包含有FIN,则允许发送; 3) 所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送; 4) 上述条件都未满足,但发生了超时(一般设置延迟ACK,一般为200ms),则立即发送。

TCP_NODELAY选项可以关闭Nagle算法。关闭后,只要有数据就立刻进行发送。

2.2 接收方Cork算法或延长ACK 如果接收方处理比较慢,ACK的窗口很小,这样接收端就引起SWSCork算法就是只要有数据达到,就回应,但是回应的窗口大小为0,直到空闲窗口已经可以放入MSS的报文长度,或者窗口的空间一半已经变为可用,这时才回应真实的可用窗口。TCP_CORK选项可以启用这个算法。另外的一种机制是延迟确认,接收端收到报文时,并不立刻进行确认,等到有足够的窗口空间,才进行确认。延迟确认会引入另外一个问题,就是会导致tcp发送方进行报文重发,现在延迟确认的数据定义为500毫秒。TCP_QUICKACK选项可以关闭延迟ACK,然而这个选项并非是永久的,需要每次接收数据后,重新设置一次。

TCP阻塞控制

在复杂和经常变化的网络环境中,当网络程序阻塞时,tcp不是一味的发送数据加塞网络,而是进行自我调整。传统的阻塞控制算法有4种:慢启动(Slow Start),阻塞避免(Congestion Avoidance),快速重传(Fast Retransmit),快速恢复(Fast Recovery)。 1. 慢启动 慢启动算法启动发生在建立tcp连接后,或者报文重传超时(Retransmit Timeout, RTO)后,也有可能是tcp空闲某段时间后。慢启动为TCP增加了一个阻塞窗口(Congestion Window, cwnd)不在报文中cwndMSS 为单位,发送时,取 cwnd和通告的窗口大小最小值为发送上限。慢启动算法: 1) cwnd通常初始化为1个MSS(Linux 3.0或以上,设置为10MSS) 2) 在没有出现丢包的情况下,每收到一个ACKcwnd = cwnd * 2 3) 当cwnd增长的一个阈值,(slow start threshold, ssthresh),即cwnd >= ssthresh,tcp 进入避免阻塞算法

RTO时,ssthresh = cwnd / 2,cwnd = 1,重启慢启动算法。

由上可知,当 ACK 很快时,慢启动算法的增长速率是很快的。如果存在延迟确认,那么增长速率并不快,所以在建立连接的慢启动算法启动期间,延迟确认是关闭的。

  1. 阻塞避免 当cwnd >= ssthresh时,就会启用避免阻塞算法,算法如下: 1) 每收到一个非重复的ACKcwnd = cwnd + 1/cwnd 避免阻塞算法,避免了慢启动那种指数级别的快速增长,而变成了缓慢的线性增长,慢慢调整到网络最佳值。

  2. 快速重传 当收到一连串3个或以上的重复ACK时,则可以认为有报文段丢失了,这个时候,不需要得等RTO,直接重传丢失的报文。这就是快速重传算法。但启用快速重传算法后,tcp并不启用慢启动算法,因为接收端已经在高速接收数据了,tcp不想突然减速,而是启动另个一个算法,快速恢复。

  3. 快速恢复 当收到3个或以上重发ACK,快速恢复算法启动,算法如下: 1) ssthresh = cwnd / 2 2) 每次收到重复的ACK, cwnd = cwnd + 1 3) 收到新数据ACKcwnd = ssthresh

由上,网络出现阻塞时,快速恢复算法将发送速率降低了。

RTT(Round Trip Time):一个连接的往返时间,即数据发送时刻到接收到确认的时刻的差值;
RTO(Retransmission Time Out):重传超时时间,即从数据发送时刻算起,超过这个时间便执行重传。
RTT和RTO 的关系是:由于网络波动的不确定性,每个RTT都是动态变化的,所以RTO也应随着RTT动态变化。
在最初的RTO算法中,RTO等于一个值为2的时延离散因子与RTT估计值的乘积

第二次挥手与第三次挥手为什么不能一起挥?

因为服务器可能还在给客户端传输数据,只能先告诉客户端,我收到了你的断开请求,但此时断开会造成数据丢失,就违反了TCP的安全可靠传输的初衷,当服务器传输完数据后,再告诉客户端做好断开连接的准备了

腾讯面试题
TCP的拥塞控制机制是什么?请简单说说。
答:我们知道TCP通过一个定时器(timer)采样了RTT并计算RTO,但是,如果网络上的延时突然增加,那么,TCP对这个事做出的应对只有重传数据,然而重传会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这就导致了恶性循环,最终形成“网络风暴” —— TCP的拥塞控制机制就是用于应对这种情况。
首先需要了解一个概念,为了在发送端调节所要发送的数据量,定义了一个“拥塞窗口”(Congestion Window),在发送数据时,将拥塞窗口的大小与接收端ack的窗口大小做比较,取较小者作为发送数据量的上限。

小结

这里小结只是tcp的很小一部分,没有涉及到tcp的方方面面,也没有涉及到内核调优,编程技巧等方方面面。另外tcp目前还是发展中的协议,随着时间推移,有很多新的功能特性添加进来,这里也没有涉及到。对于tcp的熟悉,必须通过抓包实践才能进行一步了解,停留在读书计理论,永远无法理解。期待以后有更全面的认识!

tcpdump 使用参考: https://luoguochun.cn/2015/07/25/tcpdump-usage/ 完。

一些参考: TCP_DEFER_ACCEPT TFO–TCP Fast Open – 由于存在安全隐患而没有广泛使用。

MSL 

MSL是Maximum Segment Lifetime的英文缩写,可译为“最长报文段寿命”,它是任何报文在网络上存在的最长的最长时间,超过这个时间报文将被丢弃。我们都知道IP头部中有个TTL字段,TTL是time to live的缩写,可译为“生存时间”,这个生存时间是由源主机设置设置初始值但不是但不是存在的具体时间,而是一个IP数据报可以经过的最大路由数,每经过一个路由器,它的值就减1,当此值为0则数据报被丢弃,同时发送ICMP报文通知源主机。RFC793中规定MSL为2分钟,但这完全是从工程上来考虑,对于现在的网络,MSL=2分钟可能太长了一些。因此TCP允许不同的实现可根据具体情况使用更小的MSL值。TTL与MSL是有关系的但不是简单的相等关系,MSL要大于TTL

wKiom1c_C3zTY5p7AAIQhDP5xCk816.png

 从上图我们注意到,在TCP连接释放的过程中,从TIME_WAIT状态到CLOSED状态有一个超时设置,这个超时设置是2MSL(RFC793定义MSL为2分钟),那么为什么在TIME_WAIT后必须等待2MSL时间呢?主要原因有两点(在我的上一篇博客中有讲,我们再来说下吧):

       1.为了保证客户端(我们记为A端)发送的最后一个ACK报文段能够到达服务器端。这个ACK报文段有可能丢失,因而使处在LASK—ACK端的服务器端(我们记为B端)收不到对已发送的FIN+ACK报文段。B会超时重传这个FIN+ACK报文段,而A就能在2MSL时间内收到这个重传的FIN+ACK报文段。接着A重传一次确认,重新启动2MSL计时器。最后,A和B都正常进入到CLOSED状态。如果A在TIME_WAIT状态不等待一段时间,而是在发送完ACK确认后立即释放连接,那么就无法收到B重传的FIN+ACK报文段,因而也不会再发送一次确认报文段,这样,B就无法正常进入CLOSED状态。

      2.我们都知道,假如A发送的第一个请求连接报文段丢失而未收到确认,A就会重传一次连接请求,后来B收到了确认,建立了连接。数据传输完毕后,就释放了连接。A共发送了两个连接请求报文段,其中第一个丢失,第二个到达了B。假如现在A发送的第一个连接请求报文段没有丢失,而是在某些网络节点长时间都留了,以至于延误到连接释放后的某个时间才到达B,这本来是已失效的报文段,但B并不知道,就会又建立一次连接。而等待的这2MSL就是为了解决这个问题的,A在发送完最后一个确认报后,在经过时间2MSL,就可以使本链接持续时间内所产生的所有报文段都从网络中消失,这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。

       我们回到MSL,在2MSL时间内,该地址上的连接(客户端地址,端口和服务器的端口地址)不能被使用,比如我们在建立一个连接后关闭连接然后迅速重启连接,那么就会出现端口不可用的情况。

一、  msl、ttl及rtt的区别

   1、 MSL 是Maximum Segment Lifetime英文的缩写,中文可以译为“报文最大生存时间”,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为tcp报文 (segment)是ip数据报(datagram)的数据部分,具体称谓请参见《数据在网络各层中的称呼》一文;

   2、ip头中有一个TTL域,TTL是 time to live的缩写,中文可以译为“生存时间”,这个生存时间是由源主机设置初始值但不是存的具体时间,而是存储了一个ip数据报可以经过的最大路由数,每经 过一个处理他的路由器此值就减1,当此值为0则数据报将被丢弃,同时发送ICMP报文通知源主机。RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等。

    TTL与MSL是有关系的但不是简单的相等的关系,MSL要大于等于TTL。

   3、 RTT是客户到服务器往返所花时间(round-trip time,简称RTT),TCP含有动态估算RTT的算法。TCP还持续估算一个给定连接的RTT,这是因为RTT受网络传输拥塞程序的变化而变化

   4、2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态,当TCP的一端发起主动关闭,在发出最后一个ACK包后,即第3次握 手完成后发送了第四次握手的ACK包后就进入了TIME_WAIT状态,必须在此状态上停留两倍的MSL时间,等待2MSL时间主要目的是怕最后一个 ACK包对方没收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包。在TIME_WAIT状态 时两端的端口不能使用,要等到2MSL时间结束才可继续使用。当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。不过在实际应用中可以通过设置 SO_REUSEADDR选项达到不必等待2MSL时间结束再使用此端口。对于TCP中的各种控制字段,接下来进行具体说明。

RTT(Round Trip Time):一个连接的往返时间,即数据发送时刻到接收到确认的时刻的差值;
RTO(Retransmission Time Out):重传超时时间,即从数据发送时刻算起,超过这个时间便执行重传。
RTT和RTO 的关系是:由于网络波动的不确定性,每个RTT都是动态变化的,所以RTO也应随着RTT动态变化。
在最初的RTO算法中,RTO等于一个值为2的时延离散因子与RTT估计值的乘积

tcp侦测异常断开

1. TCP保活的必要性

1) 很多防火墙等对于空闲socket自动关闭

2) 对于非正常断开, 服务器并不能检测到. 为了回收资源, 必须提供一种检测机制.

2. 导致TCP断连的因素

如果网络正常, socket也通过close操作来进行优雅的关闭, 那么一切完美. 可是有很多情况, 比如网线故障, 客户端一侧突然断电或者崩溃等等, 这些情况server并不能正常检测到连接的断开. 

3. 保活的两种方式:

1) 应用层面的心跳机制

自定义心跳消息头. 一般客户端主动发送, 服务器接收后进行回应(也可以不回应). 这里不进行详述.

PS: 有人从软件的功能角度列出第三种方式, 就是通过第三方软件来进行探测, 确定连接的有效性. 这种方式局限性很大, 而且不属于软件内部的功能实现. 不进行讨论.

2) TCP协议自带的保活功能

打开keep-alive功能即可. 具体属性也可以通过API设定.

4. 两种方式的优劣性

TCP协议自带的保活功能, 使用起来简单, 减少了应用层代码的复杂度. 推测也会更节省流量, 因为一般来说应用层的数据传输到协议层时都会被加上额外的包头包尾. 由TCP协议提供的检活, 其发的探测包, 理论上实现的会更精妙(用更少的字节完成更多的目标), 耗费更少的流量.

由应用自己实现的应用层的心跳, 为心跳消息额外定义一个消息类型就可以了. 就是应用正常的消息包, 只是这个包特殊点, 专门用来检活而已, 通常比较小, 可能只有消息头就可以了, 除非需要额外的信息. 

应用层心跳的好处我个人的理解有两点: 

一是比较灵活, 因为协议层的心跳只能提供最纯粹的检活功能, 但是应用层自己可以随意控制, 包括协议可能提供的是秒级的, 但是你想做成毫秒级的都任意(虽然实际几乎不会有这种时间级别的心跳), 包里还甚至可以携带额外的信息, 这些都是灵活之处.

二是通用, 应用层的心跳不依赖协议. 如果有一天不用TCP要改为UDP了, 协议层不提供心跳机制了, 但是你应用层的心跳依旧是通用的, 可能只需要做少许改动就可以继续使用.

应用层心跳的不好的地方也很显而易见, 增加开发工作量, 由于应用特定的网络框架, 还可能很增加代码结构的复杂度. 再就是根据上面的推测, 应用层心跳的流量消耗还是更大的, 毕竟这本质上还是个普通的数据包.

5. 到底选用那种心跳方式?

优劣点第4节已经进行了阐述, 因此如果能确定你们更换协议的可能性非常小, 同时只是需要检活的功能, 那么用协议自带的就绝对OK了, 使用简单而且高效. 有些自负的人总喜欢用自己搞的, 来代替成熟协议自带的东西, 代替系统内核提供的东西, 其实往往你应用层实现的东西, 都是更拙劣的. 网上看了一些关于协议的Keep-alive不靠谱的说法, 也都比较空想和想当然, 都没有拿出任何事实论据或实验数据. 这点大家有见解, 欢迎交流哈~

6. 类Unix平台如何使用Keep-alive

keepalive默认是关闭的, 因为虽然流量极小, 毕竟是开销. 因此需要用户手动开启. 有两种方式开启.

1) 在代码里针对每个socket进行单独设定, 使用起来灵活.

除了keepAlive 开关, 还有keepIdle, keepInterval, keepCount 3个属性, 使用简单, 如下:

  1. int keepAlive = 1;   // 开启keepalive属性. 缺省值: 0(关闭)  
  2. int keepIdle = 60;   // 如果在60秒内没有任何数据交互,则进行探测. 缺省值:7200(s)  
  3. int keepInterval = 5;   // 探测时发探测包的时间间隔为5秒. 缺省值:75(s)  
  4. int keepCount = 2;   // 探测重试的次数. 全部超时则认定连接失效..缺省值:9(次)  
  5. setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));  
  6. setsockopt(s, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));  
  7. setsockopt(s, SOL_TCP, TCP_KEEPINTVL, (void*)&keepInterval, sizeof(keepInterval));  
  8. setsockopt(s, SOL_TCP, TCP_KEEPCNT, (void*)&keepCount, sizeof(keepCount));  

使用时需要#include <netinet/tcp.h>, 否则SOL_TCP和TCP_KEEPIDLE等3个宏找不到.

ps: 忍不住吐槽一下, 网上大量毫不负责的转载, 千篇一律的搜索结果, 很多人根本都没进行过任何验证吧. 为了找这么个头文件都费了不小的事. 大多数帖子里的说的都是不可用的.

2) 修改配置文件, 对整个系统所有的socket有效.

我们可以用cat命令查看到系统中这几个默认的值.

#cat /proc/sys/net/ipv4/tcp_keepalive_time  7200  

#cat /proc/sys/net/ipv4/tcp_keepalive_intvl  75  

#cat /proc/sys/net/ipv4/tcp_keepalive_probes  9

修改它们:

#echo 60 > /proc/sys/net/ipv4/tcp_keepalive_time  

#echo 5 > /proc/sys/net/ipv4/tcp_keepalive_intvl  

#echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes

TCP异常连接的检测方法

背景

  在平时的开发中,经常会碰到一些需要检测tcp连接是否正常的场景。比如一个分布式的应用,一个调度任务的节点管理一堆用来跑业务的节点。当调度节点进行调度的时候,需要把任务分发给它认为正常的业务节点去执行。业务节点是否正常,一个重要的参考依据就是调度节点和业务节点之间的tcp连接是否正常。这时候就需要调度节点主动地去检测tcp连接。常见的检测方法有以下几种

方案一、通过TCP协议的返回值进行判断

    <1> 利用select,把socket设置为非阻塞。然后使用select等待该socket的可读事件。如果socket可读,但是recv的返回值是0,则说明socket已经被对端断开,这时候就可以调用close关闭socket。这里还要注意一点,recv还可能返回负数,这个代表socket操作出错。但是仍然应该判断一下errno是否为EINTR。如果errno是EINTR,则说明recv函数是被信号中断返回的,这时候不能判断socket的连接是否正常,也不应该调用close关闭socket。

    <2> 利用poll的事件。poll本身提供了POLLHUP,POLLERR,  POLLNVAL三个事件。如果文件描述符是socket,则POLLHUP代表socket已经断开了连接,在TCP底层就是已经收到了FIN报文。POLLERR表示socket出现了错误,一般情况下是收到了rst报文,或者已经发送了rst报文。这两种情况都应该调用close关闭socket。POLLNVAL代表socket没有打开,这时不能使用close关闭它,而应该根据自己的业务做一些其他的操作。因为关闭一个未打开的socket会出错。

    这两种方法都可以很精确地判断tcp连接是否正常,但是仍然有很明显的缺陷。就是它只可以根据TCP操作的返回值来进行判断。如果TCP四次握手没有正常被执行呢?比如连接对端机器直接挂了,那么就不会发送FIN报文给这一端,select不会返回socket可读,poll不会返回socket异常。那么这个死链接将会永远检测不到。直到写这个socket的时候,对端直接返回一个ret报文,这时才知道这个连接已经断掉了。这就意味着tcp连接异常可能永远检测不到,或者检测到的延迟非常大。这对于一些资源宝贵而且要求高性能的服务器是不能接受的,比如游戏服务器,比如搜索服务器。

  

方案二、在第一种方案的基础上设置socket的 keep alive 机制

    方案一的主要缺陷在于检测不及时,或者根本检测不到。TCP协议提供了keep alive机制。如果开启了这个特性(暂时称开启了keep alive的一端为开启端),在默认情况下,开启的着一端的socket相关结构中会维护一个定时器,默认是2小时。如果在2小时内两端没有数据往来,那么开启端就会给另一端发送一个ack空报文。这时候分几种情况:

    <1> 对端机器可达,而且TCP相关组件运行正常。那么对端就会给开启端发送一个ack空报文。这时开启端就知道对端是正常的,意味着tcp连接也没有问题。开启端会重新初始化定时器,等待下一个超时的到来。需要注意的是,如果两端之间有数据往来,定时器也会被重新初始化为2个小时。  

    <2> 对端挂了,或者正在重启,还没有完全起来。或者对端服务器不可达。 这种状态的对端是不会响应这个ack的。开启端的 keep alive 机制会把这种情况当探测超时来处理,并且重新发送ack到对端。当超时次数超过一定限制,keep alive 就认为这个tcp连接有问题。典型值是每次75秒,超时9次。

    <3> 对端挂过,但是已经重启完成。这时候发送这个ack和写已经关闭的socket是一种情况,对端会返回一个rst报文,这样开启端就知道tcp连接出问题了。

    可以看出 keep alive 机制弥补了方案一种不能判断没有进行正常四次挥手连接出现问题的缺陷。默认的发送超时和发送间隔都是可以调整的。    

    tcp_keepalive_time: KeepAlive的空闲时长,默认是2小时

    tcp_keepalive_intvl: KeepAlive探测包的发送间隔,默认是75s 

    tcp_keepalive_probes: 在tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包次数,默认是9次

    这3个参数使用 setsockopt函数都是可以配置的。

    方案二看似已经完美了,能够比较精确而且及时地发现有问题的连接。但是还有2个缺点。第一个是 keep alive 机制看似牛逼,但是很多人不建议使用。因为上面说的3个参数很难根据业务场景给出合适的值,设置不好很容易对tcp连接状态发生误判,关闭了一个本来正常的连接。而且没有一个主动通知应用层的方式。比如socket连接出错了,TCP协议接到了rst,fin,或者keep alive判断出socket有问题了,但是并不会主动去通知应用层,必须我们自己 recv socket或者等待错误事件才能得到这个错误。第二个是很多场景下,keep alive 检测仍然不够及时,比如对端挂了,最长需要等待 tcp_keepalive_intvl * tcp_keepalive_probes时间才可以检测出来,而且这两个值还不能设置得太小,太小了容易误判。

方案三、应用层的心跳

    这种形式的心跳设计就比较多样化了,而且灵活,可以很好地适应业务场景。唯一的缺点就是要自己写代码。我目前接触到的就是定期进行RPC调用。看RPC调用是否正常,如果返回错误或者抛出异常,就说明连接有问题。

一文搞懂 HTTP、TCP 的长连接和短连接

1

HTTP 协议与 TCP/IP 协议的关系

HTTP 的长连接和短连接本质上是 TCP 长连接和短连接。HTTP 属于应用层协议,在传输层使用 TCP 协议,在网络层使用 IP 协议。IP 协议主要解决网络路由和寻址问题,TCP 协议主要解决如何在 IP 层之上可靠的传递数据包,使在网络上的另一端收到发端发出的所有包,并且顺序与发出顺序一致。TCP 有可靠,面向连接的特点。

2

如何理解HTTP协议是无状态的

HTTP 协议是无状态的,指的是协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。也就是说,打开一个服务器上的网页和你之前打开这个服务器上的网页之间没有任何联系。HTTP 是一个无状态的面向连接的协议,无状态不代表 HTTP 不能保持 TCP 连接,更不能代表 HTTP 使用的是 UDP 协议(无连接)。

3

什么是长连接、短连接?

短连接 

连接->传输数据->关闭连接 

HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。 

也可以这样说:短连接是指Socket连接后发送后接收完数据后马上断开连接。 

长连接 

连接->传输数据->保持连接 -> 传输数据-> 。。。->关闭连接。 

长连接指建立Socket连接后不管是否使用都保持连接。 

在 HTTP/1.0 中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次 HTTP 操作,就建立一次连接,但任务结束就中断连接。如果客户端浏览器访问的某个 HTML 或其他类型的 Web 页中包含有其他的 Web 资源,如JavaScript 文件、图像文件、CSS 文件等;当浏览器每遇到这样一个 Web 资源,就会建立一个 HTTP 会话。

但从 HTTP/1.1 起,默认使用长连接,用以保持连接特性。使用长连接的 HTTP 协议,会在响应头有加入这行代码:

Connection:keep-alive

在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。Keep-Alive 不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如 Apache )中设定这个时间。实现长连接要客户端和服务端都支持长连接。

HTTP 协议的长连接和短连接,实质上是 TCP 协议的长连接和短连接。

3.2 TCP短连接

我们模拟一下 TCP 短连接的情况,client 向 server 发起连接请求,server 接到请求,然后双方建立连接。client 向 server 发送消息,server 回应client,然后一次读写就完成了,这时候双方任何一个都可以发起 close 操作,不过一般都是 client 先发起 close 操作。为什么呢,一般的 server 不会回复完 client 后立即关闭连接的,当然不排除有特殊的情况。从上面的描述看,短连接一般只会在 client/server 间传递一次读写操作

短连接的优点是:管理起来比较简单,存在的连接都是有用的连接,不需要额外的控制手段。

3.3 TCP 长连接

接下来我们再模拟一下长连接的情况,client 向 server 发起连接,server 接受 client 连接,双方建立连接。Client 与 server 完成一次读写之后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。

首先说一下 TCP/IP 详解上讲到的 TCP 保活功能,保活功能主要为服务器应用提供,服务器应用希望知道客户主机是否崩溃,从而可以代表客户使用资源。如果客户已经消失,使得服务器上保留一个半开放的连接,而服务器又在等待来自客户端的数据,则服务器将应远等待客户端的数据,保活功能就是试图在服务 器端检测到这种半开放的连接。

如果一个给定的连接在两小时内没有任何的动作,则服务器就向客户发一个探测报文段,客户主机必须处于以下 4 个状态之一:

  • 客户主机依然正常运行,并从服务器可达。客户的TCP响应正常,而服务器也知道对方是正常的,服务器在两小时后将保活定时器复位。

  • 客户主机已经崩溃,并且关闭或者正在重新启动。在任何一种情况下,客户的 TCP 都没有响应。服务端将不能收到对探测的响应,并在75秒后超时。服务器总共发送 10 个这样的探测 ,每个间隔 75 秒。如果服务器没有收到一个响应,它就认为客户主机已经关闭并终止连接。

  • 客户主机崩溃并已经重新启动。服务器将收到一个对其保活探测的响应,这个响应是一个复位,使得服务器终止这个连接。

  • 客户机正常运行,但是服务器不可达,这种情况与 2 类似,TCP 能发现的就是没有收到探查的响应。

3.4 长连接短连接操作过程

短连接的操作步骤是:

建立连接——数据传输——关闭连接…建立连接——数据传输——关闭连接

长连接的操作步骤是:

建立连接——数据传输…(保持连接)…数据传输——关闭连接

4

长连接和短连接的优点和缺点

由上可以看出,长连接可以省去较多的 TCP 建立和关闭的操作,减少浪费,节约时间。对于频繁请求资源的客户来说,较适用长连接。

不过这里存在一个问题,存活功能的探测周期太长,还有就是它只是探测 TCP 连接的存活,属于比较斯文的做法,遇到恶意的连接时,保活功能就不够使了。

在长连接的应用场景下,client 端一般不会主动关闭它们之间的连接,client 与 server 之间的连接如果一直不关闭的话,会存在一个问题,随着客户端连接越来越多,server 早晚有扛不住的时候,这时候 server 端需要采取一些策略,如关闭一些长时间没有读写事件发生的连接,这样可 以避免一些恶意连接导致server 端服务受损;如果条件再允许就可以以客户端机器为颗粒度,限制每个客户端的最大长连接数,这样可以完全避免某个蛋疼的客户端连累后端服务。

短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。但如果客户请求频繁,将在 TCP 的建立和关闭操作上浪费时间和带宽。

长连接和短连接的产生在于 client 和 server 采取的关闭策略,具体的应用场景采用具体的策略,没有十全十美的选择,只有合适的选择。

5

什么时候用长连接,短连接?

长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。每个 TCP 连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,次处理时直接发送数据包就 OK 了,不用建立 TCP 连接。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成 socket 错误,而且频繁的 socket 创建也是对资源的浪费。

而像 WEB 网站的 http 服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像 WEB 网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。

6

长连接和短连接的生命周期有多久?

短连接在建立连接后,完成一次读写就会自动关闭了。

正常情况下,一条TCP长连接建立后,只要双不提出关闭请求并且不出现异常情况,这条连接是一直存在的,操作系统不会自动去关闭它,甚至经过物理网络拓扑的改变之后仍然可以使用。所以一条连接保持几天、几个月、几年或者更长时间都有可能,只要不出现异常情况或由用户(应用层)主动关闭。

在编程中,往往需要建立一条TCP连接,并且长时间处于连接状态。所谓的TCP长连接并没有确切的时间限制,而是说这条连接需要的时间比较长。

7

怎样检测长连接是否中断?

1、在应用层使用heartbeat来主动检测

对于实时性要求较高的网络通信程序,往往需要更加及时的获取已经中断的连接,从而进行及时的处理。但如果对方的连接异常中断,往往是不能及时的得到对方连接已经中断的信息,操作系统检测连接是否中断的时间间隔默认是比较长的,即便它能够检测到,但却不符合我们的实时性需求,所以需要我们进行手工去不断探测。

2、改变socket的keepalive选项,以使socket检测连接是否中断的时间间隔更小,以满足我们的及时性需求。有关的几个选项使用和解析如下:


A、我们在检测对端以一种非优雅的方式断开连接的时候,可以设置SO_KEEPALIVE属性使得我们在2小时以后发现对方的TCP连接是否依然存在。用法如下:

keepAlive = 1;setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));

B、如果我们不想使用这么长的等待时间,可以修改内核关于网络方面的配置参数,也可设置SOCKET的TCP层(SOL_TCP)选项TCP_KEEPIDLE、TCP_KEEPINTVL和TCP_KEEPCNT

  • TCP_KEEPIDLE:开始首次KeepAlive探测前的TCP空闭时间(默认2h)

  • TCP_KEEPINTVL:两次KeepAlive探测间的时间间隔(默认75s)

  • TCP_KEEPCNT:断开前的KeepAlive探测次数

如果心跳函数要维护客户端的存活,即服务器必须每隔一段时间必须向客户段发送一定的数据,那么使用SO_KEEPALIVE是有很大的不足的。因为SO_KEEPALIVE选项指"此套接口的任一方向都没有数据交换"。在Linux 2.6系列上,上面话的理解是只要打开SO_KEEPALIVE选项的套接口端检测到数据发送或者数据接受就认为是数据交换。因此在这种情况下使用 SO_KEEPALIVE选项 检测对方是否非正常连接是完全没有作用的,在每隔一段时间发包的情况, keep-alive的包是不可能被发送的。上层程序在非正常断开的情况下是可以正常发送包到缓冲区的。非正常端开的情况是指服务器没有收到"FIN" 或者 "RST"包。

IGMP

ICMP:

https://zhuanlan.zhihu.com/p/64577451

tcp 图解

https://www.cnblogs.com/xiaolincoding/p/12638546.html

转载参考:

https://www.cnblogs.com/weiliuyby/p/8030175.html

https://zhuanlan.zhihu.com/p/76023663

https://www.cnblogs.com/xiexun/p/10684436.html

https://www.cnblogs.com/zhangyinhua/p/7617027.html

https://blog.csdn.net/lqglqglqg/article/details/48714611

https://www.cnblogs.com/ppzhang/p/10506237.html

https://luoguochun.cn/post/2016-09-23-tcp-fuck/

https://blog.51cto.com/10706198/1775555

https://blog.csdn.net/u010178308/article/details/73100132

tcp ip

https://www.cnblogs.com/zhangyinhua/tag/TCP%2FIP%E8%AF%A6%E8%A7%A3/

https://www.cnblogs.com/zhangyinhua/p/7609242.html

https://www.cnblogs.com/MyOnlyBook/p/9576474.html

http tcp 长连接 短连接

https://www.sysapi.com/article/389584.html

猜你喜欢

转载自blog.csdn.net/shanandqiu/article/details/114014903
今日推荐