TCP/IP协议,以及socket中TCP的三次握手和四次挥手(转)
原文:https://blog.csdn.net/hyz529900/article/details/123635629
无论参考OSI还是TCP/IP的网络模型,我们从传输层向更底层看,各层的协议都是在直接或间接的服务于主机与主机之间的通信,而传输层则是在进程与进程通信层面上的。传输层有两个重要的协议——TCP(Transmission ControlProtocol,传输控制协议)和UDP(User Data Protocol,用户数据报协议)。
UDP协议:
UDP(User Datagram Protocol)即用户数据报协议,其传输机制决定了它的最大优点——快,同时也决定了它最大的缺点
——不可靠、不稳定。
UDP是无连接的,发送数据之前不需要建立连接(TCP需要)。减少了开销和延时。
UDP是面向报文的,对IP数据报只做简单封装(8字节UDP报头)。减少报头开销。
UDP没有阻塞机制,宁愿阻塞时丢弃数据不传,也不阻塞造成延时。
UDP支持一对一、一对多、多对一、多对多通信。
TCP协议:
TCP(Transmission Control Protocol)传输控制协议,相对于UDP,TCP是面向连接的、提供可靠的数据传输服务。同时也是较UDP开销较大的、传输速度较慢的。
TCP提供可靠的、面向连接的数据传输服务。使用TCP通信之前,需要进行“三次握手”建立连接,通信结束后还要使用“四次挥手”断开连接。
TCP是点对点的连接。一条TCP连接只能连接两个端点。
TCP 提供可靠传输,无差错、不丢失、不重复、按顺序。
TCP 提供全双工通信,允许通信双方任何时候都能发送数据,发送方设有发送缓存,接收方设有接收缓存。
TCP 面向字节流 。TCP 并不知道所传输的数据的含义,仅把数据看作一连串的字节序列,它也不保证接收方收到的数据块和发送方发出的数据块具有大小对应关系。
在实现了socket服务器端和客户端编程后,我通过查找相关书籍以及网上查找资料后,进一步深入了解了套接字的概念以及socket中TCP的三次握手和四次挥手机制。
一. 套接字( socket )概念
网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“端口”可以唯一标识主机中的应用程序(进程)。这样利用二元组(ip地址,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX BSD的套接字(socket)来实现网络进程之间的通信。就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在,这就是我为什么说“一切皆socket”。 TCP/IP协议族包括运输层、网络层、链路层,而socket所在位置如图,Socket是TCP协议的封装(TCP协议的实现细节简单来讲就是3次握手4次挥手),用于应用层与TCP/IP协议族通信的中间软件抽象层。
Socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”。在许多操作系统中,套接字API最初是作为UNIX操作系统的一部分而开发的,所以套接字API与系统的其他I/O设备集成在一起。应用程序要为因特网通信而创建一个套接字(socket)时,操作系统就返回一个小整数作为描述符(descriptor)来标识这个套接字。然后应用程序以该描述符作为传递参数,通过调用相应函数(如read、write、close等)来完成某种操作(如从套接字中读取或写入数据)。
二.TCP/IP协议族
要想理解socket首先得熟悉一下TCP/IP协议族, TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何再它们之间传输的标准。
虽然OSI参考模型是国际化标准组织提出了的一个标准,但由于他定义的太过复杂而错过了时间节点,让TCP/IP协议成为了计算机网络通信的事实标准。
从字面意思来看TCP/IP是TCP和IP协议的合称,但实际上TCP/IP协议是指因特网整个TCP/IP协议族。不同于OSI模型的七个分层,TCP/IP协议参考模型把所有的TCP/IP系列协议归类到四个抽象层中。
TCP/IP协议族包括:
应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet ,SSH等等
传输层:TCP,UDP
网络层:IP,ICMP,OSPF,EIGRP,IGMP
网络接口层:SLIP,CSLIP,PPP,MTU
(客户端和服务器之间的每一层协议就像与对方的同层协议直接通信一样,可以想象成他们并知不道还有其它层的协议存在,
就像我们收发邮件一样,我们把邮件塞进我们的邮箱,然后不需要知道中间进行了怎样的传递,可以视为塞进我们的邮箱后,对方就可以直接在自己的邮箱收到我们发的邮件,
后面要讲的socket也可以这样理解,socket是传输层提供给应用层收发数据的接口(例如connect函数和accept函数),可以想象成两个进程的socket直接收发对方数据,而实际上两个socket之间还要经过其它层的传输。
上面讲的应用层的HTTP,FTP,SSH这些协议是基于TCP的,也就是说它们都是基于TCP socket的高层封装,通过Socket传输包协议,至于使用这些应用层协议的接口来通信还是直接使用socket来通信,那要看自己的业务需求)
(TCP(传输控制协议)和IP(网际协议) 是最先定义的两个核心协议,所以才统称为TCP/IP协议族)
直接使用socket还是其他应用层协议(例如HTTP)
socket优点:
1.传输数据为字节级,传输数据可自定义,数据量小。相应的移动端开发,手机费用低
2.传输数据时间短,性能高
3.适合C/S之间信息实时交互
4.可以加密,数据安全性高
缺点:
1.需要对传输的数据进行解析,转化为应用级的数据
2.对开发人员的开发水平要求高
3.相对于Http协议传输,增加了开发量
Http请求主要有http协议,基于http协议的soap协议,常见的http数据请求方式有get和post,web服务。
http优点:
1.基于应用级的接口使用方便
2.要求的开发水平不高,容错性强
缺点:
1.传输速度慢,数据包大。
2.如实现实时交互,服务器性能压力大
3.数据传输安全性差
Socket适用场景:网络游戏,银行交互,支付。
http适用场景:公司OA服务,互联网服务。
因为TCP/IP太过简单,我们也会使用模型化的TCP/IP五层模型来描述计算机网络。
在TCP/IP协议中两个因特网主机通过两个路由器和对应的层连接。各主机上的应用通过一些数据通道相互执行读取操作
TCP可以看成是一种字节流,它会处理IP层或以下的层的丢包、重复以及错误问题。在连接的建立过程中(每发送一个报文的数据都会带有一个TCP头),双方需要交换一些连接的参数。这些参数可以放在TCP头部。
一个TCP连接由一个4元组构成,分别是两个IP地址和两个端口号。一个TCP连接通常分为三个阶段:连接、数据传输、退出(关闭)。通过三次握手建立一个链接,通过四次挥手来关闭一个连接。
当一个连接被建立或被终止时,交换的报文段只包含TCP头部,而没有数据。
三.socket中TCP的三次握手建立连接
在TCP/IP协议中,TCP协议通过三次握手建立一个可靠的连接
第一次握手:客户端(我)尝试连接服务器(对方),向服务器发送syn包(同步序列编号Synchronize Sequence Numbers),syn=j,客户端进入SYN_SEND状态等待服务器确认。(对方若收到了,那么他就知道了我的发件能力和他的收件能力是OK的)
第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。(我若收到了,我便知我的发件能力和他的收件能力是OK的,并且他的发件能力和我的收件能力是OK的)
第三次握手:第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。(他若收到了,他便清楚了他的发件能力和我的收件能力是OK的)
TCP连接至此建立起来了。为什么要做三次握手呢?握手的过程实际上是在通知对方自己的初始化序号(Initial SequenceNumber),简称ISN,也就是上图中的x和y。x和y会被当作之后传输数据的一个依据,以保证TCP报文在传输过程中不会混乱。
我们回到TCP Header结构来看,Sequence Number和Acknowledgment Number都是占32位,所以seq和ack的取值范围是0 ~ 2^32 -1。
seq和ack每增加到2^32-1,则重新从0开始。值得一提的是,seq的初始值(ISN)并不是每次都从0开始的。我们设想一下,如果是从0开始,那么当TCP三次握手建立连接完成后,Client发送了30个报文,然后Client断线了。于是Client重连,再次用0作为初始的seq,这样就会出现两个报文具有相同的seq,就出现了混乱。
事实上TCP的做法是每隔4微秒就对ISN做一次加1操作,当ISN到达2^32-1后再次从0开始的时候,已经过去了几个小时,之前的seq=0的报文已经不存在于这次连接中了,这样就避免了上面的问题。
(每个报文都有一个TCP Header,这个结构体里有Sequence Number(同步序号),同步序号初始值为ISN,每发送一个报文,同步序号就加1,所以在一次发送中每个报文的同步序号都不一样,起到了区分和排序的作用)
几个字段需要重点介绍下:
(1)序号:seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
(2)确认序号:ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,ack=seq+1。
(3)标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:
- ACK:确认序号有效。
- FIN:释放一个连接。(是客户端发送断开连接请求的时候置为1?还是在整个FIN-WAIT状态时它为1?)
- PSH:接收方应该尽快将这个报文交给应用层。
- RST:重置连接。
- SYN:发起一个新连接。
- URG:紧急指针(urgent pointer)有效。
需要注意的是:
- 不要将确认序号ack与标志位中的ACK搞混了。
- 确认方ack=发起方seq+1,两端配对。
定睛一看,服务器socket与客户端socket建立连接的部分其实就是大名鼎鼎的三次握手
但是这个三次握手发生在socket的那几个函数中呢?请看下图:
(由此可见,在一次连接中,无论客户端还是服务器谁接收消息谁发送消息,所发送的消息,发送方发送一个同步序列号seq,接收方回复另一个seq和ack,其中ack=发送方的seq+1,因为seq取初值为isn,而isn如上所述会不断更新,所以每次发送方发送的消息中seq号都不一样)
从图中可以看出:当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函 数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。
总结:客户端的connect在三次握手的第二个次返回,而服务器端的accept在三次握手的第三次返回。
四.socket中TCP的四次挥手断开连接
图示过程如下:
(1)Client向Server发送断开连接请求的报文段,seq=m(m为Client最后一次向Server发送报文段的最后一个字节序号加1),Client进入FIN-WAIT-1状态。(需要注意的是客户端发出FIN报文段后只是不能发数据了,但是还可以正常收数据;另外FIN报文段即使不携带数据也要占据一个序列号)
(2)Server收到断开报文段后,向Client发送确认报文段(可以理解为:确认要断开连接?),seq=n(n为Server最后一次向Client发送报文段的最后一个字节序号加1),ack=m+1,Server进入CLOSE-WAIT状态。此时这个TCP连接处于半开半闭状态,Server发送数据的话,Client仍然可以接收到。(此时服务端处于关闭等待状态,而不是立马给客户端发FIN报文,这个状态还要持续一段时间,因为服务端可能还有数据没发完)
(3)Server向Client发送断开确认报文段,seq=u(u为半开半闭状态下Server最后一次向Client发送报文段的最后一个字节序号加1),ack=m+1,Server进入LAST-ACK状态。(也就是服务端将最后数据(比如50个字节)发送完毕后就向客户端发出连接释放报文)
(4)Client收到Server的断开确认报文段后,向Server发送确认断开报文,seq=m+1,ack=u+1,Client进入TIME-WAIT状态。
(5)Server收到Client的确认断开报文,进入CLOSED状态,断开了TCP连接。
(6)Client在TIME-WAIT状态等待一段时间(时间为2*MSL((Maximum Segment Life)),确认Client向Server发送的最后一次断开确认到达(如果没有到达,Server会重发步骤(3)中的断开确认报文段给Client,告诉Client你的最后一次确认断开没有收到)。如果Client在TIME-WAIT过程中没有再次收到Server的报文段,就进入CLOSES状态。TCP连接至此断开。
(注意客户端发出确认报文后不是立马释放TCP连接,而是要经过2MSL(最长报文段寿命的2倍时长)后才释放TCP连接(也就是TIME-WAIT状态持续时长)。而服务端一旦收到客户端发出的确认报文就会立马释放TCP连接,所以服务端结束TCP连接的时间要比客户端早一些。)
TCP连接可靠性的体现:
(1)TCP报文段的长度可变,根据收发双方的缓存状态、网络状态而调整。
(2)当TCP收到发自TCP连接另一端的数据,它将发送一个确认。
(3)当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段,如果不能及时收到一个确认,将重发这个报文段。
(4)TCP将保持它首部和数据的检验和。如果通过检验和发现报文段有差错,这个报文段将被丢弃,等待超时重传。
(5)TCP将数据按字节排序,报文段中有序号,以确保顺序的正确性。
(6)TCP还能提供流量控制。TCP连接的每一方都有收发缓存。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。
需要注意的是,TCP报文传输采用接受后返回确认的方式来保证报文传输的可靠性,并不是意味着发送方在发送一个报文段后就进入等待确认状态,让后面的报文段等着。也不是接收方在接收到一个报文后,对每一个报文都进行回复确认。
真实的情况是,对于发送方,在发送一个报文段(注意是报文段,而不是单个报文)后,复制一份该报文段的副本,然后继续进行下一个报文段的发送,如果没有得到发送方的回复确认,就对该报文段进行超时重发。对于接收方来说,则采用“积累确认”的方式进行回复。接收者收到多个连续的报文段后,只回复确认最后一个报文段,表示在这之前的数据都已收到(而不是只确认最后一个报文段已收到)。以此达到提升传输效率的目的。
TCP流量控制:
由于接收方缓存的限制,发送窗口不能大于接收方接收窗口。在报文段首部有一个字段就叫做窗口(rwnd),这便是用于告诉对方自己的接收窗口,可见窗口的大小是可以变化的,这个叫做滑动窗口协议。
总结起来如上图,TCP的流量和阻塞控制采用“慢启动”、“加性增”、“乘性减”的策略。
慢启动:初始的窗口值很小,但是按指数规律渐渐增长,直到达到慢开始门限(ssthresh)。
加性增:窗口值达到慢开始门限后,每发送一个报文段,窗口值增加一个单位量。
乘性减:无论什么阶段,只要出现超时,则把窗口值减小一半。