TCP协议与流通信
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢!
TCP(Transportation Control Protocol)协议与IP协议是一同产生的。事实上,两者最初是一个协议,后来才被分拆成网络层的IP和传输层的TCP。我们已经在UDP协议中介绍过,UDP协议是IP协议在传输层的“傀儡”,用来实现数据包形式的通信。而TCP协议则实现了“流”形式的通信。
TCP的内容非常丰富。我不能在一篇文章中将TCP讲完。这一篇主要介绍TCP协议的下面几个方面:
1. “流”通信的意义与实现方式
2. 如何实现可靠传输
3. 使用滑窗提高效率
“流”通信
TCP协议是传输层协议,实现的是端口到端口(port)的通信。更进一步,TCP协议虚拟了文本流(byte stream)的通信。在Linux文本流中我们谈到,计算机数据的本质是有序的0/1序列 (如果以byte为单位,就叫做文本流)。计算机的功能就是储存和处理文本流。CPU + memory + 存储设备实现了文本流在同一台计算机内部的加工处理。通过一些IO,比如屏幕和键盘,文本流实现了人机交互。而进一步,如果网络通信可在不同计算机之间进行文本流的交互,那么我们就和整个计算机系统的数据处理方式实现了对接。
IP协议(参考协议森林03, 05)和UDP协议采用的是数据包的方式传送,后发出的数据包可能早到,我们并不能保证数据到达的次序。TCP协议确保了数据到达的顺序与文本流顺序相符。当计算机从TCP协议的接口读取数据时,这些数据已经是排列好顺序的“流”了。比如我们有一个大文件要从本地主机发送到远程主机,如果是按照“流”接收到的话,我们可以一边接收,一边将文本流存入文件系统。这样,等到“流”接收完了,硬盘写入操作也已经完成。如果采取UDP的传输方式,我们需要等到所有的数据到达后,进行排序,才能组装成大的文件。这种情况下,我们不得不使用大量的计算机资源来存储已经到达的数据,直到所有数据都达到了,才能开始处理。
“流”的要点是次序(order),然而实现这一点并不简单。TCP协议是基于IP协议的,所以最终数据传送还是以IP数据包为单位进行的。如果一个文本流很长的话,我们不可能将整个文本流放入到一个IP数据包中,那样有可能会超过MTU。所以,TCP协议封装到IP包的不是整个文本流,而是TCP协议所规定的片段(segment)。与之前的一个IP或者UDP数据包类似,一个TCP片段同样分为头部(header)和数据(payload)两部分 (“片段”这个名字更多是起提醒作用:嘿,这里并不是完整的文本流)。整个文本流按照次序被分成小段,而每一段被放入TCP片段的数据部分。一个TCP片段封装成的IP包不超过整个IP接力路径上的最小MTU,从而避免令人痛苦的碎片化(fragmentation)。
(给文本流分段是在发送主机完成的,而碎片化是在网络中的路由器完成的。路由器要处理许多路的通信,所以相当繁忙。文本流提前在发送主机分好段,可以避免在路由器上执行碎片化,可大大减小网络负担)

片段与编号
TCP片段的头部(header)会存有该片段的序号(sequence number)。这样,接收的计算机就可以知道接收到的片段在原文本流中的顺序了,也可以知道自己下一步需要接收哪个片段以形成流。比如已经接收到了片段1,片段2,片段3,那么接收主机就开始期待片段4。如果接收到不符合顺序的数据包(比如片段8),接收方的TCP模块可以拒绝接收,从而保证呈现给接收主机的信息是符合次序的“流”。
可靠性
片段编号这个初步的想法并不能解决我们所有的问题。IP协议是不可靠的,所以IP数据包可能在传输过程中发生错误或者丢失。而IP传输是"Best Effort" 式的,如果发生异常情况,我们的IP数据包就会被轻易的丢弃掉。另一方面,如果乱序(out-of-order)片段到达,根据我们上面说的,接收主机不会接收。这样,错误片段、丢失片段和被拒片段的联手破坏之下,接收主机只可能收到一个充满“漏洞”的文本流。

请补上漏洞
TCP的补救方法是,在每收到一个正确的、符合次序的片段之后,就向发送方(也就是连接的另一段)发送一个特殊的TCP片段,用来知会(ACK,acknowledge)发送方:我已经收到那个片段了。这个特殊的TCP片段叫做ACK回复。如果一个片段序号为L,对应ACK回复有回复号L+1,也就是接收方期待接收的下一个发送片段的序号。如果发送方在一定时间等待之后,还是没有收到ACK回复,那么它推断之前发送的片段一定发生了异常。发送方会重复发送(retransmit)那个出现异常的片段,等待ACK回复,如果还没有收到,那么再重复发送原片段... 直到收到该片段对应的ACK回复(回复号为L+1的ACK)。

终于收到ACK的发送主机
当发送方收到ACK回复时,它看到里面的回复号为L+1,也就是发送方下一个应该发送的TCP片段序号。发送方推断出之前的片段已经被正确的接收,随后发出L+1号片段。ACK回复也有可能丢失。对于发送方来说,这和接收方拒绝发送ACK回复是一样的。发送方会重复发送,而接收方接收到已知会过的片段,推断出ACK回复丢失,会重新发送ACK回复。
通过ACK回复和重新发送机制,TCP协议将片段传输变得可靠。尽管底盘是不可靠的IP协议,但TCP协议以一种“不放弃的精神”,不断尝试,最终成功。(技术也可以很励志)

面对“挫折”,TCP协议的态度: never give up
TCP协议和UDP协议走了两个极端。TCP协议复杂但可靠,UDP协议轻便但不可靠。在处理异常的时候,TCP极端负责,而UDP一副无所谓的样子。我们可以顺便“黑”一下UDP协议:

同样面对“挫折”,UDP的态度: who cares...
滑窗
上面的工作方式中,发送方保持发送->等待ACK->发送->等待ACK...的单线工作方式,这样的工作方式叫做stop-and-wait。stop-and-wait虽然实现了TCP通信的可靠性,但同时牺牲了网络通信的效率。在等待ACK的时间段内,我们的网络都处于闲置(idle)状态。我们希望有一种方式,可以同时发送出多个片段。然而如果同时发出多个片段,那么由于IP包传送是无次序的,有可能会生成乱序片段(out-of-order),也就是后发出的片段先到达。在stop-and-wait的工作方式下,乱序片段完全被拒绝,这也很不效率。毕竟,乱序片段只是提前到达的片段。我们可以在缓存中先存放它,等到它之前的片段补充完毕,再将它缀在后面。然而,如果一个乱序片段实在是太过提前(太“乱”了),该片段将长时间占用缓存。我们需要一种折中的方法来解决该问题:利用缓存保留一些“不那么乱”的片段,期望能在段时间内补充上之前的片段(暂不处理,但发送相应的ACK);对于“乱”的比较厉害的片段,则将它们拒绝(不处理,也不发送对应的ACK)。

总有那么几个“出格”片段
滑窗(sliding window)被同时应用于接收方和发送方,以解决以上问题。发送方和接收方各有一个滑窗。当片段位于滑窗中时,表示TCP正在处理该片段。滑窗中可以有多个片段,也就是可以同时处理多个片段。滑窗越大,越大的滑窗同时处理的片段数目越多(当然,计算机也必须分配出更多的缓存供滑窗使用)。

同时处理多个片段
我们假设一个可以容纳三个片段的滑窗,并假设片段从左向右排列。对于发送方来说,滑窗的左侧为已发送并已ACK过的片段序列,滑窗右侧是尚未发送的片段序列。滑窗中的片段(比如片段5,6,7)被发送出去,并等待相应的ACK。如果收到片段5的ACK,滑窗将向右移动。这样,新的片段从右侧进入滑窗内,被发送出去,并进入等待状态。在接收到片段5的ACK之前,滑窗不会移动,即使已经收到了片段6和7的ACK。这样,就保证了滑窗左侧的序列是已经发送的、接收到ACK的、符合顺序的片段序列。
对于接收方来说,滑窗的左侧是已经正确收到并ACK回复过的片段(比如片段1,2,3,4),也就是正确接收到的文本流。滑窗中是期望接收的片段(比如片段5, 6, 7)。同样,如果片段6,7先到达,那么滑窗不会移动。如果片段5先到达,那么滑窗会向右移动,以等待接收新的片段。如果出现滑窗之外的片段,比如片段9,那么滑窗将拒绝接收。
下面一个视频中,我尝试模拟可容纳三个片段的滑窗(固定大小)的工作过程。
如果视频加载有问题,可点下面链接: http://v.youku.com/v_show/id_XNDg1NDUyMDUy.html
上面的视频是我用Python和matplotlib包制作的。蓝色点表示片段,红色点表示ACK。为了说明乱序片段,我故意让片段和ACK的速度从两个值中随机选择。
可以看到,随着滑窗的滑动,越来越多的片段被正确的传送。利用滑窗,我们一定程度上实现了对乱序数据的缓存。但是,过于乱序的数据依然会被拒绝。我们之前说的stop-and-wait的工作方式,相当于发送方和接收方的滑窗都只能容纳一个片段。
我们将在以后看到,TCP协议有实时调整滑窗大小的算法,以实现最优效率。
总结
TCP协议和UDP协议走了两个极端。TCP协议复杂但可靠,UDP协议轻便但不可靠。在处理异常的时候,TCP极端负责,而UDP一副无所谓的样子。在TCP中,分段和编号实现了次序;ACK和重新发送实现了可靠性;sliding window则让上面的机制更加有效率的运行。Never give up,这就是TCP协议的态度。
这篇文章也包含了我的一个视频小实验,看看效果如何,欢迎大家的反馈。
TCP协议与流通信的更多相关文章
- 协议森林08 不放弃 (TCP协议与流通信)
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! TCP(Transportation Control Protocol)协议与IP ...
- (转)协议森林08 不放弃 (TCP协议与流通信)
协议森林08 不放弃 (TCP协议与流通信) 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! TCP(Transportation ...
- 浅析C#基于TCP协议的SCOKET通信
TCP协议是一个基本的网络协议,基本上所有的网络服务都是基于TCP协议的,如HTTP,FTP等等,所以要了解网络编程就必须了解基于TCP协议的编程.然而TCP协议是一个庞杂的体系,要彻底的弄清楚它的实 ...
- 基于TCP协议的socket通信
一.服务器端 1.创建serverSocket,即服务器端的socket,绑定指定的端口,并侦听此端口 ServerSocket server = new ServerSocket(8888); 2. ...
- Android TCP协议的Socket通信
1.介绍 2.使用方法 3.java后台代码 服务器server package com.lucky.servertest; import java.io.BufferedReader; import ...
- python 30 基于TCP协议的socket通信
目录 1. 单对单循环通信 2. 循环连接通信:可连接多个客户端 3. 执行远程命令 4. 粘包现象 4.1 socket缓冲区 4.2 出现粘包的情况: 4.3 解决粘包现象 bytes 1. 单对 ...
- python中基于tcp协议的通信(数据传输)
tcp协议:流式协议(以数据流的形式通信传输).安全协议(收发信息都需收到确认信息才能完成收发,是一种双向通道的通信) tcp协议在OSI七层协议中属于传输层,它上承用户层的数据收发,下启网络层.数据 ...
- 基于TCP与UDP协议的socket通信
基于TCP与UDP协议的socket通信 C/S架构与初识socket 在开始socket介绍之前,得先知道一个Client端/服务端架构,也就是 C/S 架构,互联网中处处充满了 C/S 架构(Cl ...
- 网络编程——TCP协议、UDP协议、socket套接字、粘包问题以及解决方法
网络编程--TCP协议.UDP协议.socket套接字.粘包问题以及解决方法 TCP协议(流式协议) 当应用程序想通过TCP协议实现远程通信时,彼此之间必须先建立双向通信通道,基于该双向通道实现数 ...
随机推荐
- python中executemany的使用
conn = MySQLdb.connect(host = “localhost”, user = “root”, passwd = “password”, db = “myDB”, charset= ...
- Android网络编程Socket【实例解析】
Socket 事实上和JavaWeb 里面的Socket一模一样 建立客服端,server端,server开一个port供客服端訪问 第一步创建server端:(这里把为了便于解说.把server端, ...
- ReactNative Navigator
https://facebook.github.io/react-native/docs/navigator.html Navigator实现了页面之间的跳转. Demo描述:打开即进入“课程”页面, ...
- 理解cas
前言 CAS(Compare and Swap),即比较并替换,实现并发算法时常用到的一种技术,Doug lea大神在java同步器中大量使用了CAS技术,鬼斧神工的实现了多线程执行的安全性. CAS ...
- Android-解决Fail to post notification on channel "null"的方法
原文:https://blog.csdn.net/weixin_40604111/article/details/78674563 在sdk版本为25或25之前想在notification中添加一个点 ...
- webstorm vscode 常用设置
webstorm常用的设置及操作图解 VS Code 新建vue文件初始化模板 VSCode新建vue文件自定义模板
- Matlab时频图
[b,f,t]=specgram(data,nfft,Fs,window,numoverlap); imagesc(t,f,20*log10(abs(b))), axis xy, colormap(j ...
- wepy项目中使用async await
https://github.com/Tencent/wepy/wiki/wepy项目中使用async-await
- Android 普通okhttp、okhttp utils执行 post get请求,文件上传下载、请求图片
public class OKHttpActivity extends Activity implements View.OnClickListener { public static final M ...
- js正則函數 match、exec、test、search、replace、split 使用介紹集合
match 方法 使用正則表達式模式對字元串執行查找,並將包含查找的結果作為數組返回. stringObj.match(rgExp) 參數 stringObj 必選項.對其進行查找的 String 對 ...