TCP / IP 协议包含了一系列的协议,也叫TCP / IP协议族(TCP / IP Protocol Suite , 或 TCP / IP Protocols ),简称 TCP / IP 。TCP / IP 协议族提供了点对点的连接机制,并且将传输数据帧的封装、寻址、传输、路由以及接受方式,都予以标准化。
在展开介绍TCP/IP协议之前,⾸先介绍⼀下七层ISO模型。国际标准化组织ISO为了使⽹络应⽤更为普及,推出了OSI参考模型,即开放式系统互联(Open System Interconnect)模型, ⼀般都叫OSI参考模型。 OSI参考模型是ISO组织在1985年发布的⽹络互连模型,其含义就是为所有公司使⽤⼀个统
⼀的规范来控制⽹络,这样所有公司遵循相同的通信规范,⽹络就能互联互通了。
OSI模型定义了⽹络互连的七层框架(物理层、数据链路层、⽹络层、传输层、会话层、表示层、应⽤层),每⼀层实现各⾃的功能和协议,并完成与相邻层的接⼝通信。
OSI 模型各层的通信协议,⼤致举例如下表所示:
应用层 | HTTP、 SMTP、 SNMP、 FTP、 Telnet、 SSH |
---|---|
表示层 | XDR、 ASN.1、 SMB、 AFP、 NCP |
会话层 | ASAP、 SSH、 RPC、 NetBIOS、 ASP、 Winsock、 BSD Sockets |
传输层 | TCP、 UDP、 TLS、 RTP、 SCTP、 SPX、 ATP、 IL |
网络层 | IP、 ICMP、 IGMP、 IPX、 BGP、 OSPF、 RIP、 IGRP、 EIGRP、 ARP、RARP |
数据链路层 | 以太⽹、令牌环、 HDLC、帧中继、 ISDN、 ATM、 IEEE 802.11、 FDDI、 PPP |
物理层 | 例如铜缆、⽹线、光缆、⽆线电 |
TCP/IP协议是Internet互联⽹最基本的协议,其在⼀定程度上参考了七层ISO模型。 OSI模型共有七层,从下到上分别是物理层、数据链路层、⽹络层、运输层、会话层、表示层和应⽤层。但是这显然是有些复杂的,所以在TCP/IP协议中,七层被简化为了四个层次。TCP/IP模型中的各种协议,依其功能不同,被分别归属到这四层之中,常被视为是简化过后的七层OSI模型。 TCP/IP协议与七层ISO模型的对应关系,⼤致如下图所示:
应⽤层包括所有和应⽤程序协同⼯作,并利⽤基础⽹络交换应⽤程序的业务数据的协议。⼀些特定的程序被认为运⾏在这个层上,该层协议所提供的服务能直接⽀持⽤户应⽤。应⽤层协议包括HTTP(万维⽹服务)、 FTP(⽂件传输)、 SMTP(电⼦邮件)、SSH(安全远程登陆)、 DNS(域名解
析)以及许多其他协议。
传输层的协议,解决了诸如端到端可靠性问题,能确保数据可靠的到达⽬的地,甚⾄能保证数据按照正确的顺序到达⽬的地。传输层的主要功能⼤致如下:
传输层主要有两个性质不同的协议: TCP传输控制协议和UDP⽤户数据报协议。
总体来说, TCP协议传输效率低,但可靠性强; UDP协议传输效率⾼,但可靠性略低,适⽤于传输可靠性要求不⾼、体量⼩的数据(⽐如QQ聊天数据)
TCP/IP协议⽹络层的作⽤是在复杂的⽹络环境中为要发送的数据报找到⼀个合适的路径进⾏传输。简单来说,⽹络层负责将数据传输到⽬标地址,⽬标地址可以是多个⽹络通过路由器连接⽽成的某⼀个地址。另外,⽹络层负责寻找合适的路径到达对⽅计算机,并把数据帧传送给对⽅,⽹络层还可以实现
拥塞控制、⽹际互连等功能。⽹络层协议的代表包括: ICMP、 IP、 IGMP等。
链路层有时也称作数据链路层或⽹络接⼝层,⽤来处理连接⽹络的硬件部分。该层既包括操作系统硬件的设备驱动、 NIC(⽹卡)、光纤等物理可⻅部分,还包括连接器等⼀切传输媒介。在这⼀层,数据的传输单位为⽐特。其主要协议有ARP、 RARP等。
利⽤TCP/IP进⾏⽹络通信时,数据包会按照分层顺序与对⽅进⾏通信。发送端从应⽤层往下⾛,接收端从链路层往上⾛。从客户端到服务器的数据,每⼀帧数据的传输的顺序都为:应⽤层->传输层->⽹络层->链路层->链路层->⽹络层->传输层->应⽤层。
以⼀个HTTP请求的传输为例,请求从HTTP客户端(如浏览器)和HTTP服务端应⽤的传输过程,⼤致如下图所示:
数据通过互联⽹传输的时候不可能是光秃秃的不加标识,如果这样数据就会乱。
所以数据在发送的时候,需要加上特定标识,加上特定标识的过程叫做数据的封装,在数据使⽤的时候再去掉特定标识,去掉特定标识的过程就叫做分⽤。
在数据封装时,数据经过每个层都会打上该层特定标识,添加上头部。在传输层封装时,添加的报⽂⾸部时要存⼊⼀个应⽤程序的标识符,⽆论TCP和UDP都⽤⼀个16位的端⼝号来表示不同的应⽤程序,并且都会将源端⼝和⽬的端⼝存⼊报⽂⾸部中。
在⽹络层封装时, IP⾸部会标识处理数据的协议类型,或者说标识出⽹络层数据帧所携带的上层数据类型,如TCP、 UDP、 ICMP、 IGMP等等。 具体来说,会在IP⾸部中存⼊⼀个⻓度为8位的数值,称作协议域: 1表示为ICMP协议、 2表示为IGMP协议、 6表示为TCP协议、 17表示为UDP协议、
等等。 IP⾸部还会标识发送⽅地址(源IP)和接收⽅地址(⽬标IP)。
在链路层封装时,⽹络接⼝分别要发送和接收IP、 ARP和RARP等多种不同协议的报⽂,因此也必须在以太⽹的帧⾸部中加⼊某种形式的标识,以指明所处理的协议类型,为此,以太⽹的报⽂帧的⾸部也有⼀个16位的类型域,标识出以太⽹数据帧所携带的上层数据类型,如IPv4、 ARP、 IPV6、
PPPoE等等。
数据封装和分⽤的过程⼤致为:发送端每通过⼀层会增加该层的⾸部,接收端每通过⼀层则删除该层的⾸部。
总体来说, TCP/IP分层管理、数据封装和分⽤的好处:分层之后若需改变相关设计,只需替换变动的层。各层之间的接⼝部分规划好之后,每个层次内部的设计就可以⾃由改动。层次化之后,设计也变得相对简单:各个层只需考虑分派给⾃⼰的传输任务。
TCP/IP与OSI的区别主要有哪些呢?
实际上,在传输过程中,数据报⽂会在不同的物理⽹络之间传递,还是以⼀个HTTP请求的传输为例,请求在不同物理⽹络之间的传输过程,⼤致如下图所示:
数据包在不同物理⽹络之间的传输过程中,⽹络层会通过路由器去对不同的⽹络之间的数据包进⾏存储、分组转发处理。构造互连⽹最简单的⽅法是把两个或多个⽹络通过路由器进⾏连接。路由器可以简单理解为⼀种特殊的⽤于⽹络互连的硬件盒,其作⽤是为不同类型的物理⽹络提供连接:以太⽹、令牌环⽹、点对点的链接和FDDI(光纤分布式数据接⼝)等等。
物理⽹络之间通过路由器进⾏互连,随着增加不同类型的物理⽹络,可能会有很多个路由器,但是对于应⽤层来说仍然是⼀样的, TCP协议栈为⼤家屏蔽了物理层的复杂性。总之,物理细节和差异性的隐藏,使得互联⽹TCP/IP传输的功能变得⾮常强⼤。
传输层TCP协议提供了⼀种⾯向连接的、可靠的字节流服务,其数据帧格式,⼤致如下图所示:
源端口号表示报文的发送端,占16位。源端口号和源IP地址组合起来,可以标识报文的发送地址。
目的端括号表示报文的接口端,占16位。目的端口和目的IP地址组合,可以标识报文的接收地址。
TCP协议是基于IP协议的基础传输的,TCP报文中的源端口号 + 源IP,与TCP报文中的目的端口号 + 目的IP一起,组合起来 唯一性的确定一条TCP连接。
TCP传输过程中,在发送端发出的字节流中,传输报文中的数据部分的每个字节都有它的编号。序号(Sequence Number)占32位,发起方发送数据是,都需要标记序号。
序号(Sequence Number)的语义与 SYN 控制标识(Control Bits)的值有关。根据控制标识(Control Bits)中的 SYN 是否为 1,序号(Sequence Number)表达不同的含义:
在数据传输过程中,TCP 协议通过序号(Sequence Number)对上层提供有序的数据流。发送端可以用序号来跟踪送的数据量;接收端可以用序号识别出重复接收的就TCP包,从而丢弃重复包;对于乱序的数据包,接收端也可以依靠序号对其进行排序。
确认序号(Acknowledgment Number)标识了报⽂接收端期望接收的字节序列。如果设置了ACK控制位,确认序号的值表示⼀个准备接收的包的序列码,注意,它所指向的是准备接收的包,也就是下⼀个期望接收的包的序列码。
举个例⼦,假设发送端(如Client)发送3个净荷为1000byte、起始SN序号为1的数据包给Server服务端, Server每收到⼀个包之后,需要回复⼀个ACK响应确认数据包给Client。ACK响应数据包的ACK Number值,为每个Client包的为SN+包净荷,既表示Server已经确认收到的字节数,还表示期望
接收到的下⼀个Client发送包的SN序号,具体的ACK值如下图左边的正常传输部分所示。
在上图的左边部分, Server第1个ACK包的ACK Number值为1001,是通过Client第1个包的SN+包净荷=1+1000计算得到,表示期望第2个Client包的SN序号为1001; Server第2个ACK包的ACK Number值为2001,为Client第2个包的SN+包净荷=2001,表示期望第3个Server包的SN为2001,以此
类推。
如果发⽣错误,假设Server在处理Client的第⼆个发送包异常, Server仍然回复⼀个ACK Number值为1001的确认包,则Client的第⼆个数据包需要重复发送,具体的ACK值如上图右边的正常传输部分所示。
只有控制标志的ACK标志为1时,数据帧中的确认序号ACK Number才有效。 TCP协议规定,连接建⽴后,所有发送的报⽂的ACK必须为1,也就是建⽴连接后,所有报⽂的确认序号有效。如果是SYN类型的报⽂,其ACK标志为0,故没有确认序号。
4 bit 表示的最大数为:1111 = 15
该字段占⽤4位,⽤来表示TCP报⽂⾸部的⻓度,单位是4bit位。其值所表示的并不是字节数,⽽是头部的所含有的32bit的数⽬(或者倍数),或者4个字节的倍数,所以TCP头部最多可以有60字节(4*15=60)。没有任何选项字段的TCP头部⻓度为20字节,所以其头部⻓度为5,可以通过20/4=5计算得到。
头部⻓度后⾯预留的字段⻓度为6位,作为保留字段,暂时没有什么⽤处。
共6个bit位,具体的标志位为: URG、 ACK、 PSH、 RST、SYN、 FIN。 6个标志位的说明,如下表所示:
标志位 | 说明 |
---|---|
URG | 占 1 位,表示紧急指针字段有效。URG 位只是报文段里的上层实体(数据)标记为 “紧急” 数据。当 URG=1 时,其后的紧急指针指示紧急数据在当前数据段中的位置(相当于当前序号的字节偏移量),TCP 接收方必须通知上层实体。 |
ACK | 占 1 位,置位 ACK=1 表示确认字段有效;TCP 协议规定,连接建立后所有发送的报文的 ACK 必须为 1;当 ACK=0 时,表示该数据段不包含确认信息。当 ACK=1 时,表示该报文包括一个对于已被成功接收报文段的确认序号 Acknowledgment Number,该序号同时也是下一个报文的预期序号。 |
PSH | 占 1 位,表示当前报文需要请求推(push)操作;当 PSH=1 时,接收方在收到数据后立即将数据交给上层,而不是直到整个缓冲区满。 |
RST | 占 1 位,置位 RST = 1 表示复位 TCP 连接;用于重置一个已经混乱的连接,也可用于拒绝一个无效的数据段或者拒绝一个连接请求。如果数据段被设置了 RST 位,说明报文发送方有问题发生。 |
SYN | 占 1 位,在连接建立时用来同步序号。当 SYN=1 而 ACK=0 时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使 SYN=1 和 ACK=1。综合一下,SYN 置 1 就表示这是一个连接请求或连接接收报文。 |
FIN | 占 1 位,用于在释放 TCP 连接时,标识发送方比特流结束,用来释放一个连接。当 FIN=1 时,表明此报文的发送方的数据已经发送完毕,并要求释放连接。 |
在连接建立的三次握手过程中,若只是单个 SYN 置位,表示的只是建立连接请求。如果SYN和ACK同时置位为1,表示的建⽴连接之后的响应。
长度 16 位,共 2 个字节。此字段用来进行流量控制。流量控制的单位为字节数,这个值是本端期望一次接收的字节数。
长度为 16 位,共 2 个字节。对整个 TCP 报文段,即 TCP 头部和 TCP 数据进行校验和计算,接收端用于对收到的数据包进行验证。
长度为 16 位,共 2 个字节。它是一个偏移量,和 SN 序号值相加表示紧急数据最后一个字节的序号。
以上十项内容是 TCP 报文首部必须的字段,也称固有字段,长度为 20 个字节。接下来是 TCP 报文的可选项和填充部分。
可选项和填充部分的长度为 4n 字节(n 是整数),该部分是根据需要而增加的选项。如果不足 4n 字节,要加填充位,使得选项长度为 32 位(4字节)的整数倍,具体的做法是在这个字段中加入额外的零,以确保 TCP 头是 32 位(4字节)的整数倍。
最常见的选项字段是 MSS (Maximum Segment Size 最长报文大小),每个连接方通常都在通信的第一个报文段(SYN标志为 1 的那个段)中指明这个选项字段,表示当前连接方所能接收的最大报文段的长度
由于可选项和填充部分不是必须的,所以 TCP 报文首部最小长度为 20 个字节。
至此,TCP 报文首部的字段,就全部介绍完了。TCP 报文首部的后面,接着的是数据部分,不过数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有TCP首部。如果⼀⽅没有数据要发送,也使⽤没有任何数据的⾸部来确认收到的数据,⽐如在处理超时的过程中,也会发送不带任何数据的报⽂段。
TCP连接的建⽴时,双⽅需要经过三次握⼿,⽽断开连接时,双⽅需要经过四次分⼿,那么,其三次握⼿和四次分⼿分别做了什么呢?⼜是如何进⾏的呢?
通常情况下,建⽴连接的双⽅,由⼀端打开⼀个监听套接字(ServerSocket)来监听来⾃请求⽅的TCP(Socket)连接,当服务器端监听开始时,必须做好准备接受外来的连接,在Java中该操作通过创建⼀个ServerSocket服务监听套接字实例来完成,此操作会调⽤底层操作系统(如Linux)的C代码中三个函数socket()、 bind()、 listen() 来完成。开始监听之后,服务器端就做好接受外来连接的准备,如果监听到建⽴新连接的请求,会开启⼀个传输套接字,称之为被动打开(Passive Open)。⼀段简单的服务端监听新连接请求,并且被动打开(Passive Open)传输套接字的Java示例代码,具体如下:
public class SocketServer {
public static void main(String[] args) {
try {
// 创建服务端 socket
ServerSocket serverSocket = new ServerSocket(8080);
//循环监听等待客户端的连接
while (true) {
//监听到客户端连接,传输套接字被动开启
Socket socket = serverSocket.accept();
//开启线程进⾏连接的 IO 处理
ServerThread thread = new ServerThread(socket);
thread.start();
}
} catch (Exception e) {
// 处理异常
e.printStackTrace();
}
}
}
客户端在发起连接建⽴时, Java代码通过创建Socket实例,调⽤底层的connect(…)⽅法,主动打开(Active Open)Socket连接。套接字监听⽅在收到请求之后,监听⽅和发起⽅(客户端)之间就会建⽴⼀条的连接通道,该通道由双⽅IP和双⽅端⼝所唯⼀确定。
⼀段简单的客户端连接主动打开(Active Open)的Java示例代码,具体如下:
public class SocketClient {
public static void main(String[] args) throws InterruptedException {
try {
// 和服务器创建连接
Socket socket = new Socket("localhost",8080);
// 写⼊给监听⽅的输出流
OutputStream os = socket.getOutputStream();
// 读取监听⽅的输⼊流
InputStream is = socket.getInputStream();
} catch (Exception e)
e.printStackTrace();
}
}
}
TCP连接的建⽴时,双⽅需要经过三次握⼿,具体过程如下:
第⼀次握⼿:Client进⼊SYN_SENT状态,发送⼀个SYN帧来主动打开传输通道,该帧的SYN标志位被设置为1,同时会带上Client分配好的SN序列号,该SN是根据时间产⽣的⼀个随机值,通常情况下每间隔4ms会加1。除此之外,SYN帧还会带⼀个MSS(最⼤报⽂段⻓度)可选项的值,表示客户端发送出去的最⼤数据块的⻓度。
第⼆次握⼿:Server端在收到SYN帧之后,会进⼊SYN_RCVD状态,同时返回SYN+ACK帧给Client,主要⽬的在于通知Client,Server端已经收到SYN消息,现在需要进⾏确认。Server端发出的SYN+ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment Number)值被设置为Client的SN+1;SYN+ACK帧的SYN标志位被设置为1,SN值为Server端⽣成的SN序号;
SYN+ACK帧的MSS(最⼤报⽂段⻓度)表示的是Server端的最⼤数据块⻓度。
第三次握⼿:Client在收到Server的第⼆次握⼿SYN+ACK确认帧之后,⾸先将⾃⼰的状态会从SYN_SENT变成ESTABLISHED,表示⾃⼰⽅向的连接通道已经建⽴成功,Client可以发送数据给Server端了。然后,Client发ACK帧给Server端,该ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment Number)值被设置为Server端的SN序列号+1。还有⼀种情况,Client可能会将ACK帧和第⼀帧要发送的数据,合并到⼀起发送给Server端。
Server端在收到Client的ACK帧之后,会从SYN_RCVD状态会进⼊ESTABLISHED状态,⾄此,Server⽅向的通道连接建⽴成功,Server可以发送数据给Client,TCP的全双⼯连接建⽴完成。
三次握⼿的交互过程,具体如下图所示:
Client和Server完成了三次握⼿后,双⽅就进⼊了数据传输的阶段。数据传输完成后, 连接将断开,连接断开的过程需要经历四次挥⼿。
注:该阶段,服务器端的接收缓存区还没有初始化,画出来是为了方便理解;
注1:该阶段,客户端的接收缓存区还没有初始化,画出来是为了方便理解;
注2:该阶段,服务器端的接收缓存区处于关闭状态,之后收到客户端的 SYN-ACK 报文后,才会接收报文数据。例子:如果客户端发送的 第三次握手的 SYN-ACK 报文在网络传输中丢失,即使客户端发送过来带有数据(表示客户端本来就要发送的数据)的报文,服务器端也不会接收。
总结:在三次握手的过程中,客户端和服务器端内核都会进行缓存区的分配。客户端发送第一次握手时分配发送缓存区,服务器端接收第一次握手并发送第二次握手时分配接收缓存区和发送缓存区,客户端接收第二次握手并发送第三次握手时分配接收缓存区和发送缓存区,最后服务器端接收第三次握手时也分配接收缓存区和发送缓存区,以准备进行数据的传输。
业务数据通信完成之后,TCP连接开始断开(或者拆接)的过程,在这个过程中连接的每个端的都能独⽴地、主动的发起,断开的过程TCP协议使⽤了四路挥⼿操作。
四次挥⼿具体过程,具体如下:
被动断开⽅在收到主动断开⽅的最后的ACK报⽂以后,最终关闭了连接,⾃⼰啥也不管了。