做蛋糕的企业网站,app定制网站建设应有尽有,科学小制作小发明,网页游戏排行2013目录
1.认识TCP
2.TCP协议段格式
3.可靠性保证的机制
确认应答机制
超时重传机制
连接管理机制
三次握手
四次挥手 1.认识TCP
在网络通信模型中#xff0c;传输层有两个经典的协议#xff0c;分别是UDP协议和TCP协议。其中TCP协议全称为传输控制协议#xff08;Tra…目录
1.认识TCP
2.TCP协议段格式
3.可靠性保证的机制
确认应答机制
超时重传机制
连接管理机制
三次握手
四次挥手 1.认识TCP
在网络通信模型中传输层有两个经典的协议分别是UDP协议和TCP协议。其中TCP协议全称为传输控制协议Transmission Control Protocol从名称就可以看出TCP协议需要对数据的传输进行严格的控制。
UDP协议具有无连接、不可靠、面向数据报的特点而TCP协议恰恰相反具有 有连接、可靠、面向字节流的特点。而其中可靠性是TCP最著名的特点也正因为TCP协议需要保证通信的可靠性所以TCP协议才会有一系列保证可靠性的机制和策略这也是我们需要重点学习的内容。
2.TCP协议段格式
所谓协议其实就是通信双方都认识的结构化的数据。TCP协议是传输层的协议传输层的协议是在操作系统内部实现的所以操作系统内部一定有TCP协议相关的代码。
Linux内核中TCP协议部分代码 把代码形象化便得到了下面这张图 各个字段的粗略认识 1、16位源端口和16位目的端口表明数据从哪个进程来要发送给哪个进程。 2、32位序号和32位确认序号序号可以用来对接收到的报文进行按序到达和去重确认序号表明该序号之前的报文都收到了。后面会详谈 3、4位首部长度表明TCP报头的长度。TCP报头由固定长度的20字节和不固定的选项构成四位首部长度表明了这两部分共同的长度。其中首部长度是有基本单位的基本单位是4字节。4个比特位的最大取值是15所以四位首部长度的最大范围是60字节。 4、6位标记位 URG: 表明紧急指针是否有效 ACK: 表明确认号是否有效 PSH: 提示接收端应用程序立刻从 TCP 缓冲区把数据读走 RST: 对方要求重新建立连接; 我们把携带 RST 标识的称为复位报文段 SYN: 请求建立连接; 我们把携带 SYN 标识的称为同步报文段 FIN: 通知对方, 本端要关闭了, 我们称携带 FIN 标识的为结束报文段 5、16 位窗口大小: 表明自己的接收能力通信双方可以动态的调整发送报文的大小。 6、16 位校验和: 发送端填充, CRC 校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含 TCP 首部, 也包含 TCP 数据部分. 7、16 位紧急指针: 标识哪部分数据是紧急数据需要优先处理。 8、40 字节头部选项: 暂时忽略; 3.可靠性保证的机制
确认应答机制 铺垫什么是序列号 我们可以这样理解。操作系统会为TCP分配两个进行通信的缓冲区我们把缓冲区当成char类型的大数组那么缓冲区中的数据不就天然的具有编号了吗这个编号我们把它叫做序列号。序列号是对每个字节的编号这也体现出了TCP面向字节流的特点。 TCP需要保证可靠性首先需要保证发送方发送的数据接收方要能收到。那发送方如何得知接收方是否收到了自己发送的数据呢这个时候TCP协议便引入了确认应答机制。
确认应答机制就是接收方对接收到的报文进行应答这样一来发送方就知道对方有没有收到我发送的数据了。并且这个应答是需要在指定的时间内收到的如果主动发送数据的一方没有在规定时间内收到应答那么它就会认为对方没有收到我发送的数据。 但是问题又来了发送方发送一次消息接收方应答一下这样似乎没有什么问题但是这种通信模式的效率是非常低的所以通信双方进行通信的时候数据的发送往往是并行的。 那么问题又来了接收方收到多个报文的时候如何保证能够正确的进行应答呢 其实啊应答的时候是通过序列号来完成的。序列号是能够确认顺序的接收方会对收到的报文根据序列号进行排序一旦排好序之后接收方就会先判断最小序列号之前的报文是否全部收到如果该序列号之前的报文全部收到才会进行应答并且应答的时候是需要填充32位确认序号的确认序号的值是收到的报文的序列号1表明该序列号之前的数据我全部收到了下次你应该从哪里发。这其实这就是按序到达策略。 如果收到了对方的确认序号下一次发送数据的序号就是 收到的确认序号要发送报文的长度。 我们已经知道了确认应答机制是通过序列号来完成的那你有没有这样的疑问为什么报头中有序列号了还需要有一个确认序号呢直接用序列号的字段来表明确认序号不就可以了吗
这是因为在实际通信的过程中接收方往往也需要向发送方响应消息。 也就是说数据的接收方既要响应又要发送消息那么这个过程可不可以一步到位呢这是可以的序列号表明自己发送的报文确认序号可以作为接收消息的响应。那么这样一来中间的两次发送可以合并为一次发送这样不就又提高了通信的效率了吗这其实就是捎带应答。 超时重传机制
前面我们已经知道了TCP可靠性保证的一个机制 —— 确认应答机制接收方需要向发送方进行响应表明自己收到了对方的消息。但是如果发送方一直没有收到对方的响应会怎么办呢这就需要引入超时重传机制了。
发送方发送数据之后会等待一段时间在该时间内如果收到了对方的响应就表明对方收到了我发送的数据如果在该时间段内没有收到对方的应答发送方就会认为对方没有收到自己发送的数据这个时候就需要再发送一遍这就是超时重传机制。
超时重传的时间怎么定呢
数据是需要通过网络进行发送的如果网络状态比较好的话数据发送的速度就会比较快如果网络比较差的话数据发送的速度就会比较慢。也就是说网络的状态是动态变化的那么超时时间的设置也必须是动态变化的如果网络状态比较好的话超时时间就可以设置的短一点如果网络状态比较差的话超时时间就设置的长一点。 • Linux 中, 超时时间以 500ms 为一个单位进行控制, 每次判定超时重发的超时时间都是500ms 的整数倍. • 如果重发一次之后, 仍然得不到应答, 等待 2 * 500ms 后再进行重传. • 如果仍然得不到应答, 等待 4 * 500ms 进行重传. 依次类推, 以指数形式递增. • 累计到一定的重传次数, TCP 认为网络或者对端主机出现异常, 强制关闭连接. 超时重传机制存在的问题
由于超时重传机制的存在在规定时间内没有收到应答就会进行重传。那有没有可能发送方发送的数据在网络中阻塞了一段时间但是在一段时间后被对方收到但是这个时候已经重传了还有没有可能对方已经收到了报文但是响应丢了呢不管是那种情况都会导致发送方重复发送对方已经收到的报文那么接收方就会收到重复的报文这个时候怎么办呢
不要忘了TCP协议报头中有序列号序列号不仅仅可以用来做为应答还可以用来去重。当接收方接收到消息的时候它会根据序列号判断这个报文我曾经是否收到过如果收到过的话就直接将该报文丢弃了。所以我们不用担心重复报文的问题。 接收方如何判断这个报文我曾经是否收到过呢 这个问题更具体的解决策略就是接收方根据自己最新一次的确认序号就能知道多少号报文之前的报文我都收到了如果对方发过来的报文的序号小于最新一次的确认序号那么该报文就能丢弃了也就实现了去重。 连接管理机制
TCP协议是面向连接的协议通信之前通信双方必须进行三次握手建立连接通信之后通信双方必须进行四次挥手断开连接。
三次握手
使用TCP协议进行通信的时候通信双方必须建立连接才能进行正常的通信当通信结束时通信双方也必须断开连接以确保不会造成服务器端的资源浪费。所以在基于TCP通信的过程中会有各种各样的报文有的报文是用来请求建立连接的有的报文是用来进行正常通信的有的报文是用来请求断开连接的。为了区分这些不同的报文于是TCP协议报头中引入了标记位。
TCP协议中与连接管理有关的标记位 SYNSYN标记位也称为同步标记位。如果客户端发送的报文中的SYN标记位被置为1服务器端就知道对方想与我建立连接了。 FINFIN标记位也称为结束标记位。如果客户端发送的报文中的FIN标记位被置为1服务器端就知道对方想与我断开连接了。 ACKACK标记位我们可以称其为应答标记位。用于表明该应答中的确认序号是否有效也就是表明该报文是否是用于应答的报文。 RST重置标记位。要求对方重新建立连接。 三次握手过程中套接字的状态变化 1.双方未建立连接的时候双方的套接字都处于CLOSED状态。 2.服务器端需要先调用listen接口将自己的套接字状态设为LISTEN状态等待客户端连接。 3.此时客户端需要主动调用connect接口向服务器发起连接此时客户端套接字状态变为SYN_SENT状态。 4.当服务器端监听到连接请求(SYN报文), 就将该连接放入内核等待队列中, 如果它也想与对方建立连接就会在发送的报文中将SYN和ACK标记位置1当该报文发送出去的时候客户端的套接字状态进入SYN_RCVD状态。 5.当客户端收到服务器端的应答的时候他就认为连接建立好了客户端的套接字状态进入ESTABLISHED状态并向服务器端发送一个ACK报文表明我也愿意与你通信。 6.当服务器端收到这个ACK报文的时候服务器端也认为连接建立好了服务器端的套接字状态就进入ESTABLISHED状态。 此时通信双方都认为连接建立好了在这个过程中客户端通过connect函数发起连接服务器端的accept函数并不参与三次握手。 一个问题 在这个过程中我们发现客户端认为连接建立好的时间是早于服务器端的。如果客户端发送信息的需求非常迫切一旦认为连接建立好了就要发送消息但是此时服务器端的连接还没有建立好呢 这个时候当服务器端收到客户端的正常的通信报文的时候就会向客户端响应一个RST标记位被置为1的报文要求对方重新建立连接。也就是重新进行三次握手。 那你有没有思考过一个问题为什么建立连接之前要进行三次握手呢 1.双方要进行通信首先要确保通信的信道是健康的。三次握手的工程中客户端发送的数据被服务器端接收到之后服务器端要对客户端进行响应如果客户端也收到了服务器端的应答说明客户端是能够进行收发的同理客户端也要对服务器端进行响应如果服务器端收到了客户端的应答说明服务器端也是能够进行收发的。 2.通信双方都能进行数据的收发还不够还需要检查对方是否愿意和自己通信。三次握手的过程中都有一次给对方的响应说明对方是愿意和自己进行通信的。 此时通信双方都能够进行数据的收发并且对方也愿意与自己进行通信那么此时就可以建立连接进行通信了。 三次握手的本质 在三次握手的过程中服务器是提供服务的一方当有客户来请求建立连接的时候服务器肯定是愿意的并且也要询问对方是否愿意和自己建立连接再者两次报文中并不涉及数据只是涉及报头中标记位的变化所以两次报文可以进行捎带应答合并成一个报文这才有了三次握手。也就是说三次握手的本质也是四次握手只不过中间的两次被捎带应答合二为一了。 谈完三次握手建立连接我们现在谈谈四次挥手断开连接。
四次挥手
四次挥手过程中套接字状态的变化 我们假如客户端主动请求断开连接。 1.客户端主动调用 close 时, 向服务器发送结束报文段, 同时进入 FIN_WAIT_1; 2.当客户端主动关闭连接(调用 close), 服务器会收到结束报文段, 服务器返回确认报文段并进入 CLOSE_WAIT; 3. 客户端收到服务器对结束报文段的确认, 则进入 FIN_WAIT_2, 开始等待服务器的结束报文段; 4.服务器进入 CLOSE_WAIT 后说明服务器准备关闭连接(需要处理完之前的数据); 当服务器真正调用 close 关闭连接时, 会向客户端发送FIN, 此时服务器进入 LAST_ACK 状态, 等待最后一个 ACK 到来(这个 ACK 是客户端确认收到了 FIN) 5.客户端收到服务器发来的结束报文段, 进入TIME_WAIT, 并发出 ACK;客户端要等待一个 2MSL(Max Segment Life, 报文最大生存时间)的时间, 才会进入CLOSED 状态. 6.服务器收到了对 FIN 的 ACK, 彻底关闭连接 理解CLOSE_WAIT状态和TIME_WAIT状态
当客户端主动请求断开连接的时候说明客户端要发送的数据发送完了但是服务器端要发送的数据不一定发送完了所以服务器端不能立即调用close函数断开连接而是进入CLOSE_WAIT状态直到将要发送的数据发送完之后才向客户端发送请求断开连接的报文也就是说进入CLOSE_WAIT状态的一方不会立即关闭文件描述符。所以如果我们发现我们的服务器上有大量的CLOSE_WAIT状态很有可能是服务器端没有关闭文件描述符。
如果客户端是主动断开连接的一方当客户端收到来自服务器端的断开连接的请求报文的时候就会进入TIME_WAIT状态处于TIME_WAIT状态的一方不会立即断开连接而是需要进行一段时间的等待。这是因为网络中可能还有历史报文如果连接关闭之后立马又来了相同的连接那么历史报文就会对新的连接发送的报文造成影响等待一段时间可以让历史报文消散。当然还有一个原因。如果服务器端没有收到客户端发送的最后一个ACK报文服务器端可以要求客户端进行超时重传此时连接还在是可以进行超时重传的也就保证最后一个报文可靠到达。 进入TIME_WAIT状态的一方需要等待的时长是两个MSL(maximum segment lifetime)时间MSL时间并不是指数据从发送到接收所花费的时间而是数据在网络中的最大存活时间。 等待两个MSL时间是因为客户端发送最后一个ACK需要消耗一个MSL时间如果服务器端要求客户端进行重传客户端接收消息也需要消耗一个ACK时间。 和三次握手一样我们来思考一下为什么断开连接要进行四次挥手呢
和三次握手一样断开连接也需要表明通信双方的意愿这个过程需要双方进行至少一次的互问互答来完成当双方都发起断开连接的请求之后并且也都收到了对方肯定的回答那么这个时候就可以断开连接了。 四次挥手的过程和三次握手的过程挺像的那中间的两个报文能否合并成一个报文呢 通信双方断开连接的时候必须保证待发送的数据都已经发送完了。假如客户端发起断开连接的请求客户端是知道自己没有数据再要发送了也就不会再向服务器发送消息了这里的消息主要是数据不包括协议报头所以才会要求断开连接。但是服务器端不一定将待发送的数据都发送完了服务器必须保证待发送的数据发送完之后才能断开连接。也就是说客户端发起断开连接请求的时候如果服务器没有需要发送的数据了那么此时是可以将中间的两个报文合二为一的但是这种情况的概率非常小。所以断开连接的时候通常是四次挥手。