赞
踩
TCP报文段分为首部和数据两部分。TCP报文段首部的前20个字节是固定的,后面有4n字节是根据需要而增加的选项(n是整数)。因此TCP首部的最小长度是20字节(最大长度是60字节)。
==========================================================================
源端口 ===> 2byte;发送方端口号
==========================================================================
目的端口 ===> 2byte;接收方端口号
==========================================================================
序号 ===> 4byte;序号范围是[0,2^32 - 1]
序号增加到 2^32 - 1 后,下一个序号就又回到0。也就是说,序号使用mod 2^32 运算;由于序号字段有32位长,可对4GB的数据进行编号。在一般情况下可保证当序号重复使用时,旧序号的数据早已通过网络到达终点了
在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。字节流的起始序号必须在连接建立时设置。TCP数据报首部中的序号字段值则指的是本报文段所发送的数据的第一个字节的序号。
例如,一报文段的序号字段值是301,而携带的数据共有100字节。这就表明:本报文段的数据的第一个字节的序号是301,最后一个字节的序号是400。显然,下一个报文段(如果还有的话)的数据序号应当从401开始,即下一个报文段的序号字段值应为401。这个字段的名称也叫做“报文段序号”。
==========================================================================
确认号 ===> 4byte;即期望收到对方下一个报文段的第一个数据字节的序号。若确认号为N,则表明:到序号N-1为止的所有数据都已正确收到。
例如,B收到了A发送的一个报文段,其序号字段值是501,而数据长度是200字节(序号501~700),此时,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701。
==========================================================================
数据偏移 ===> 4bit;单位为4byte即32bit;它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。(即TCP报文段的首部长度)。因为该字段长4bit,单位为4byte。所以数据偏移的最大值是60字节,这也是TCP首部的最大长度(即选项长度不能超过40字节)。
==========================================================================
保留 ===> 6bit,保留为今后使用,但目前应置为0
==========================================================================
紧急URG(URGent)===> 1bit;
当URG=1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据),而不要按原来的排队顺序来传送。于是发送方TCP就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍是普通数据。这时要与首部中紧急指针(Urgent Pointer)字段配合使用
==========================================================================
确认ACK(ACKnowledgment)===> 1bit;仅当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1
==========================================================================
推送PSH(PuSH) ===> 1bit;当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能够收到对方的响应,而不再等到整个缓存都填满了后再向上交付。这时,发送方TCP把PSH置1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程
==========================================================================
复位RST(ReSeT)===> 1bit;当RST=1时,表明TCP连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。RST置1还用来拒绝一个非法的报文段或拒绝打开一个连接。RST也可称为重建位或重置位。
==========================================================================
同步SYN(SYNchronization)===> 1bit;在连接建立时用来同步序号
当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1。因此,SYN置为1就表示这是一个连接请求或连接接受报文
==========================================================================
终止FIN(FINis)===> 用来释放一个连接
当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接
==========================================================================
窗口 ===> 2byte;值是[0,2^16-1]之间的整数;窗口字段明确指出了现在允许对方发送的数据量。窗口值经常在动态变化着
窗口指的是发送本报文段的一方的接收窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的
例如,发送了一个报文段,其确认号是701,窗口字段是1000。这就是告诉对方:“从701号算起,我(即发送此报文段的一方)的接收缓存空间还可接收1000个字节数据(字节序号是701~1700)
==========================================================================
检验和 ===> 2byte;检验和字段检验的范围包括首部和数据这两部分
在计算检验和时,要在TCP报文段的前面加上12字节的伪首部(同UDP)。伪首部的格式如图。注意伪首部第4个字段值为6(TCP的协议号是6)。接收方收到此报文段后,仍要加上这个伪首部来计算检验和。若使用IPv6,则相应的伪首部也要改变
==========================================================================
紧急指针 ===> 2byte;仅在URG=1时有效
它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据)。注意,即使窗口为零时也可发送紧急数据
==========================================================================
选项 ===> 长度可变,最长可达40字节;当没有使用“选项“时,TCP的首部长度是20字节
主要选项有:
---------------------------------------------------------------------------------------------------------------------------------
最大报文段长度 MSS(Maximum Segment Size)===> 4byte;TCP报文段中的数据字段的最大长度,并不是整个TCP报文段的最大长度(数据字段加上TCP首部才等于整个的TCP报文段)
注意,MSS与接收窗口值没有关系。我们知道,TCP报文段的数据部分,至少要加上40字节的首部(TCP首部20字节和IP首部20字节,这里都还没有考虑首部中的选项部分),才能组装成一个IP数据报。若选择较小的MSS长度,网络的利用率就降低。若TCP报文段非常长,那么在IP层传输时就有可能要分解成多个短数据报片。在终点要把收到的各个短数据报片装配成原来的TCP报文段。当传输出错时还要进行重传。这些也都会使开销增大。因此,MSS应尽可能大些,只要在IP层传输时不需要再分片就行。由于IP数据报所经历的路径是动态变化的,因此在这条路径上确定的不需要分片的MSS,如果改走另一条路径就可能需要进行分片。因此最佳的MSS是很难确定的。在连接建立的过程中,双方都把自己能够支持的MSS写入这一字段,以后就按照这个数值传送数据,两个传送方向可以有不同的MSS值。若主机未填写这一项,则MSS的默认值是536字节长。因此,所有在互联网上的主机都应能接受的报文段长度是536+20(固定首部长度)=556字节
---------------------------------------------------------------------------------------------------------------------------------
窗口扩大选项 ===> 3byte;
因为TCP首部中窗口字段长度是16位,因此最大的窗口大小为64K字节。可是特殊场合需要更大的窗口大小。于是引入了该选项
该选项中有一个字节表示移位值S。新的窗口值等于TCP首部中的窗口位数从16增大到(16+S)。移位值允许使用的最大值是14,相当于窗口最大值增大到2^(16+14) - 1 = 2^30 - 1。窗口扩大选项可以在双方初始建立TCP连接时进行协商。如果连接的某一端实现了窗口扩大,当它不再需要扩大其窗口时,发送S=0的选项,可使窗口大小回到16
---------------------------------------------------------------------------------------------------------------------------------
时间戳选项 ===> 10byte;
主要包括时间戳值字段(4字节)和时间戳回送回答字段(4字节)。时间戳选项有以下两个功能:
第一,用来计算往返时间RTT。发送方在发送报文段时把当前时钟的时间值放入时间戳字段,接收方在确认该报文段时把时间戳字段值复制到时间戳回送回答字段。因此,发送方在收到确认报文后,可以准确地计算出RTT来。
第二,用于处理TCP序号超过2^32的情况,这又称为防止序号绕回PAWS(Protect Against Wrapped Sequence numbers)。TCP报文段的序号有32位,而每增加2^32个序号就会重复使用原来用过的序号。当使用高速网络时,在一次TCP连接的数据传送中序号很可能会被重复使用。
例如,当使用1.5Mbit/s的速率发送报文段时,序号重复要6小时以上。但若用2.5Gbit/s的速率发送报文段,则不到14秒钟序号就会重复。为了使接收方能够把新的报文段和迟到很久的报文段区分开,可以在报文段中加上这种时间戳。
---------------------------------------------------------------------------------------------------------------------------------
选择确认(SACK)选项 ===> 在建立TCP连接时,双方必须都事先商定好开启此选项,以便报告收到的不连续的字节块的边界。同时,原来首部中的“确认号字段”的用法仍然不变。
但是,SACK文档并没有指明发送方应当怎样响应SACK。因此大多数的实现还是重传所有未被确认的数据块
产生原因:
若收到的报文段无差错,只是未按序号,中间还缺少一些序号的数据,那么能否设法只传送缺少的数据而不重传已经正确到达接收方的数据?当然可以,就是用选择确认
TCP的接收方在接收对方发送过来的数据字节流的序号不连续,结果就形成了一些不连续的字节块。
如图。可以看出,序号1~1000收到了,但序号1001~1500没有收到。接下来的字节流又收到了,可是又缺少了3001~3500。再后面从序号4501起又没有收到。
边界标记规则:
注意:我们标记的是和前后字节不连续的每一个字节块,每个这样的字节块需要两个边界——左边界、右边界
左边界 ===> 字节块的第一个字节的序号
右边界 ===> 字节块的最后一个序号+1
例如:
第一个字节块:1501 - 3001
第二个字节块:3501 - 4501
---------------------------------------------------------------------------------------------------------------------------------
==========================================================================
TCP把连接作为最基本的抽象。我们知道,每一条TCP连接有两个端点。我们把TCP连接的端点叫做套接字(socket)或插口。根据RFC793的定义:端口号拼接到(concatenated with)IP地址即构成了套接字。
概述:
点分十进制的IP地址后面写上端口号,中间用冒号或逗号隔开
格式:
[点分十进制的IP地址] [: | ,] [端口号]
例如:
192.168.1.1:80
或
192.168.1.1,80
因为每一条TCP连接唯一地被通信两端的两个端点(即两个套接字)所确定。所以一个TCP连接可以采取如下表示方法:
TCP连接::= {socket1,socket2} = {(IP1:port1),(IP2:port2)}
这里IP1和IP2分别是两个端点主机的IP地址,而port1和port2分别是两个端点主机中的端口号。TCP连接的两个套接字就是socket;和socket2
注意:同一个IP地址可以有多个不同的TCP连接,而同一个端口号也可以出现在多个不同的TCP连接中。
随着互联网的不断发展以及网络技术的进步,socket可表示多种不同的意思。例如:
(1)允许应用程序访问连网协议的应用编程接口API(Application Programming Interface),即运输层和应用层之间的一种接口,称为socketAPI,并简称为socket。
(2)在socketAPI中使用的一个函数名也叫做 socket。
(3)调用socket函数的端点称为socket,如“创建一个数据报socket”。
(4)调用socket函数时,其返回值称为socket描述符,可简称为socket。
(5)在操作系统内核中连网协议的Berkeley实现,称为socket实现。
要理解TCP是如何实现通信的可靠性,就必须理解两个协议——停止等待协议、自动重传请求协议(ARQ),下面让我们来学习以下这两个协议
概述:
全双工通信的双方既是发送方也是接收方。为了方便,我们仅考虑A(发送方)发送数据,B(接收方)接收数据并发送确认。“停止等待”就是每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。
注意事项:
(1)A在发送完一个分组后,必须暂时保留已发送的分组的副本(在发生超时重传时使用)。只有在收到相应的确认后才能清除暂时保留的分组副本。
(2)分组和确认分组都必须进行编号。这样才能明确是哪一个发送出去的分组收到了确认,而哪一个分组还没有收到确认。
具体过程:
我们知道在研究停止等待协议的时候,数据传输有可能会因为种种的情况出现差错,因此我们可分为两种情况讨论
(1)无差错情况
如图A发送分组M1,发完就暂停发送,等待B的确认。B收到了M1就向A发送确认。A在收到了对M1的确认后,就再发送下一个分组M2。同样,在收到B对M2的确认后,再发送M3。
(2)有差错情况
传输出差错有两种情况,一种是B接收M1时检测出了差错(这时候B会丢弃M1),另一种是数据M1在传输过程中丢失了(B不会知道)。在这两种情况下,B都不会发送任何信息。
注意:
1.A只要超过了一段时间仍然没有收到确认,就认为刚才发送的分组丢失了,因而重传前面发送过的分组。这就叫做超时重传。
超时重传的注意事项:
(1)要在每发送完一个分组时设置一个超时计时器。如果在超时计时器到期之前收到了对方的确认,就撤销已设置的超时计时器。其实在图中,A为每一个已发送的分组都设置了一个超时计时器。但A只要在超时计时器到期之前收到了相应的B发出的确认,A就撤销该超时计时器。
(2)超时计时器设置的重传时间应当比数据在分组传输的平均往返时间更长一些。但是如果重传时间设定得很长,那么通信的效率就会很低。如果重传时间设定得太短,以致产生不必要的重传,就浪费了网络资源。再加上已发送出的分组到底会经过哪些网络,以及这些网络将会产生多大的时延(这取决于这些网络当时的拥塞情况),这些都是不确定因素。因此在运输层重传时间的准确设定是非常复杂的,
2.在运输层并不使用停止等待协议,这里只是为了引出可靠传输的问题才从最简单的概念讲起。在运输层使用的可靠传输协议要复杂得多。
利用确认和重传机制,我们就可以在不可靠的传输网络上实现可靠的通信。这种可靠传输协议常称为自动重传请求ARQ(Automatic Repeat reQuest)。那么什么是确认重传机制呢?它主要依赖于以下两个机制:
注意:重传的请求是自动进行的。接收方不需要请求发送方重传某个出错的分组
(1)确认丢失
如图,若数据传输过程中B所发送的对M1的确认丢失了。A在设定的超时重传时间内没有收到确认(这时A并不知道是自己发送的分组出错、丢失,还是B发送的确认丢失了)。所以A在超时计时器到期后就要重M1。这时候假如B又收到了重传的分组M1,它会做以下两件事情:
①丢弃这个重复的分组M1,不向上层交付。
②向A发送确认。不能认为已经发送过确认就不再发送,因为A之所以重传M1
(2)确认迟到
如果,传输过程中若B对分组M1的确认迟到了(并没有出现差错)。A会收到重复的确认。对重复的确认的处理很简单:收下后就丢弃。B仍然会收到重复的M1,并且同样要丢弃重复的M1,并重传确认分组。
注意:通常A最终总是可以收到对所有发出的分组的确认。如果A不断重传分组但总是收不到确认,就说明通信线路太差,不能进行通信
背景:
如上图,假定A发送分组需要的时间是TD(分组长度除以数据率),再假定分组正确到达B后,B处理分组的时间可以忽略不计,同时立即发回确认。假定B发送确认分组需要时间TA。如果A处理确认分组的时间也可以忽略不计。那么A在经过时间(TD+RTT+TA)后就可以再发送下一个分组,这里的RTT是往返时间。
因为仅仅是在时间TD内才用来传送有用的数据(包括分组的首部),因此信道的利用率U可用下式计算:
U = TD / (TD+RTT+TA)
下面我们做个例题,计算一下信道的利用率:
例如,假定1200km的信道的往返时间RTT=20ms。分组长度是1200bit,发送速率是1Mbit/s。若忽略处理时间和TA(T一般都远小于TD)
则,
U = ([数据长度] / [传输速率]) / ([数据长度] / [传输速率] + RTT +TA) = (1200 / 100 0000) / (1200 / 100 0000 + 20ms + 0)
= 1.2 / 21.2
= 5.66%
可以发现,信道利用率是相当低的,为了提高信道利用率,出现了连续ARQ协议和滑动窗口协议的概念
滑动窗口协议是TCP的精髓,如图,它表示发送方维持的发送窗口,位于该发送窗口内的5个分组都可连续发送出去,而不需要等待对方的确认。这样,信道利用率就提高了。
发送窗口的位置由窗口前沿和后沿的位置共同确定。
发送窗口后沿的变化情况有两种可能,即不动(没有收到新的确认)和前移(收到了新的确认)。发送窗口后沿不可能向后移动,因为不能撤销掉已收到的确认。
发送窗口前沿的变化情况有三种可能,即不动(这对应于两种情况:一是没有收到新的确认,对方通知的窗口大小也不变;二是收到了新的确认但对方通知的窗口缩小了,使得发送窗口前沿正好不动)和前移以及向后收缩(对方通知的窗口缩小了)。
注意:TCP的标准强烈不赞成发送窗口向后收缩。因为很可能发送方在收到这个通知以前已经发送了窗口中的许多数据,现在又要收缩窗口,不让发送这些数据,这样就会产生一些错误
(收到一个确认后,发送窗口向前滑动)
注意:如果发送方发送了前5个分组,而中间的第3个分组丢失了。这时接收方只能对前两个分组发出确认。发送方无法知道后面三个分组的下落,而只好把后面的三个分组都再重传一次。这就叫做Go-back-N(回退N),表示需要再退回来重传已发送过的N个分组。可见当通信线路质量不好时,连续ARQ协议会带来负面的影响。
连续ARQ协议:发送方每收到一个确认,就把发送窗口向前滑动一个分组的。表示发送方收到了对第1个分组的确认,于是把发送窗口向前移动一个分组的位置。如果原来已经发送了前5个分组,那么现在就可以发送窗口内的第6个分组了。接收方一般都是采用累积确认的方式。
累积确认:接收方不必对收到的分组逐个发送确认,而是在收到几个分组后,对按序到达的最后一个分组发送确认,这就表示:到这个分组为止的所有分组都已正确收到了
①假定A收到了B发来的确认报文段,其中窗口是20字节,而确认号是31(这表明B期望收到的下一个序号是31,而序号30为止的数据已经收到了)。根据这两个数据,A就构造出自己的发送窗口,如图。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
注意:
1.在没有收到B的确认的情况下,A可以连续把窗口内的数据都发送出去。凡是已经发送过的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用。
2.接收方(B)会把自己的接收窗口数值放在窗口字段中发送给对方(A)。因此,A的发送窗口一定不能超过B的接收窗口数值。此外,发送方的发送窗口大小还要受到当时网络拥塞程度的制约。
3.发送窗口后沿的后面部分表示已发送且已收到了确认。这些数据显然不需要再保留了。而发送窗口前沿的前面部分表示不允许发送的,因为接收方都没有为这部分数据保留临时存放的缓存空间。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
②假定A发送了序号为31~41的数据。发送窗口内靠后面有11个字节(黑心方块)表示已发送但未收到确认。而发送窗口内靠前面的9个字节(白心方块)是允许发送但尚未发送的。
P1之前 ===> 已发送并已收到确认的部分
P3之后 ===> 不允许发送的部分
P1-P3 ===> A的发送窗口
P1-P2 ===> 已发送但尚未收到确认的字节数
P2-P3 ===> 允许发送但当前尚未发送的字节数(又称为可用窗口或有效窗口)
③B的接收窗口大小是20。在接收窗口外面,到30号为止的数据是已经发送过确认,并且已经交付主机了。因此在B可以不再保留这些数据。接收窗口内的序号(31~50)是允许接收的。如图B收到了序号为32和33的数据。这些数据没有按序到达,因为序号为31的数据没有收到(也许丢失了,也许滞留在网络中的某处)。注意,B只能对按序收到的数据中的最高序号给出确认,因此B发送的确认报文段中的确认号仍然是31(即期望收到的序号)
④假定B收到了序号为31的数据,并把序号为31~33的数据交付主机,然后B删除这些数据。接着把接收窗口向前移动3个序号,同时给A发送确认,其中窗口值仍为20,但确认号是34。这表明B已经收到了到序号33为止的数据。但此时B还收到了序号为37,38和40的数据,但这些都没有按序到达,只能先暂存在接收窗口中。A收到B的确认后,就可以把发送窗口向前滑动3个序号,但指针P2不动。可以看出,现在A的可用窗口增大了,可发送的序号范围是42~53。
⑤A在继续发送完序号42~53的数据后,指针P2向前移动和P3重合。发送窗口内的序号都已用完,但还没有再收到确认(图5-18)。由于A的发送窗口已满,可用窗口已减小到零,因此必须停止发送。此时有可能发送窗口内所有的数据都已正确到达B,B也早已发出了确认。但不幸的是,所有这些确认都滞留在网络中。在没有收到B的确认时,A在经过一段时间后(由超时计时器控制)就重传这部分数据,重新设置超时计时器,直到收到B的确认为止。如果A收到确认号落在发送窗口内,那么A就可以使发送窗口继续向前滑动,并发送新的数据。
发送方的应用进程把字节流写入TCP的发送缓存,接收方的应用进程从TCP的接收缓存中读取字节流
发送缓存用来暂时存放:
①发送应用程序传送给发送方TCP准备发送的数据
②TCP已发送出但尚未收到确认的数据
发送窗口通常只是发送缓存的一部分。已被确认的数据应当从发送缓存中删除,因此发送缓存和发送窗口的后沿是重合的。发送应用程序最后写入发送缓存的字节减去最后被确认的字节,就是还保留在发送缓存中的被写入的字节数。发送应用程序必须控制写入缓存的速率,不能太快,否则发送缓存就会没有存放数据的空间。
接收缓存用来暂时存放:
①按序到达的、但尚未被接收应用程序读取的数据
②未按序到达的数据
如果收到的分组被检测出有差错,则要丢弃。如果接收应用程序来不及读取收到的数据,接收缓存最终就会被填满,使接收窗口减小到零。反之,如果接收应用程序能够及时从接收缓存中读取收到的数据,接收窗口就可以增大,但最大不能超过接收缓存的大小。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
注意:
1.虽然A的发送窗口是根据B的接收窗口设置的,但在同一时刻,A的发送窗口并不总是和B的接收窗口一样大。这是因为通过网络传送窗口值需要经历一定的时间滞后(这个时间还是不确定的)。另外,发送方A还可能根据网络当时的拥塞情况适当减小自己的发送窗口数值。
2.对于不按序到达的数据应如何处理,TCP标准并无明确规定。如果接收方把不按序到达的数据一律丢弃,那么接收窗口的管理将会比较简单,但这样做对网络资源的利用不利(因为发送方会重复传送较多的数据)。因此TCP通常对不按序到达的数据是先临时存放在接收窗口中,等到字节流中所缺少的字节收到后,再按序交付上层的应用进程。
3.TCP要求接收方必须有累积确认的功能,这样可以减小传输开销。接收方可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息顺便捎带上。但请注意两点。
(1)接收方不应过分推迟发送确认,否则会导致发送方不必要的重传,这反而浪费了网络的资源。TCP标准规定,确认推迟的时间不应超过0.5秒。若收到一连串具有最大长度的报文段,则必须每隔一个报文段就发送一个确认。
(2)捎带确认实际上并不经常发生,因为大多数应用程序很少同时在两个方向上发送数据。
最后再强调一下,TCP的通信是全双工通信。通信中的每一方都在发送和接收报文段。因此,每一方都有自己的发送窗口和接收窗口。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
TCP为超时重传时间提供了一种自适应算法,它记录一个报文段发出的时间,以及收到相应的确认的时间。这两个时间之差就是报文段的往返时间RTT。TCP保留了RTT的一个加权平均往返时间RTTs(平滑的往返时间)。每当第一次测量到RTT样本时,RTTs值就取为所测量到的RTT样本值。但以后每测量到一个新的RTT样本,就按下式重新计算一次RTTs(往返时间):
新的RTTs = (1-α)×(旧的RTTs)+ α *(新的RTT样本) (0 ≤ α < 1)
若α 接近于0,表示新的RTTs值和旧的RTTs值相比变化不大
若α 接近于1,表示新的RTTs值和旧的RTTs值相比变化较大
RFC标准推荐 α 的值为1/8,即0.125
超时计时器设置的超时重传时间RTO(RetransmissionTime-Out)应略大于上面得出的加权平均往返时间RTTs。RFC建议这么计算RTO:
RTO = RTTs + 4 × RTTD
RFC建议这样计算RTTD:
当第一次测量时,RTTD值取为测量到的RTT样本值的一半。在以后的测量中,则使用下式计算加权平均的RTTD
新的RTTD =(1-β)x(旧的RTTD)+ β x |RTTs - 新的RTT样本|
β是个小于1的系数,RFC标准推荐其值是1/4,即0.25
=======================================================================================================
思考一个问题:
假定发送出一个报文段,设定的重传时间到了,还没有收到确认。于是重传报文段。经过了一段时间后,收到了确认报文段。
现在的问题是:如何判定此确认报文段是对先发送的报文段的确认,还是对后来重传的报文段的确认?
分析:
若收到的确认是对重传报文段的确认,但却被源主机当成是对原来的报文段的确认,则这样计算出的RTTs和超时重传时间RTO就会偏大。若后面再发送的报文段又是经过重传后才收到确认报文段,则按此方法得出的超时重传时间RTO就越来越长。
若收到的确认是对原来的报文段的确认,但被当成是对重传报文段的确认,则由此计算出的RTTs和RTO都会偏小。这就必然导致报文段过多地重传。这样就有可能使RTO 越来越短。
为此,我们规定:
报文段每重传一次,就把超时重传时间RTO增大一些(典型的做法是取新的重传时间为旧的重传时间的2倍)当不再发生报文段的重传时,才根据上面给出的计算超时重传时间
=======================================================================================================
TCP利用滑动窗口实现流量的控制,下面我们举个例子:
ACK ===> 首部中的ACK;ACK=1时,确认号有效、确认号有效时,可以指定下一个期望数据序号的值(即seq)
ack ===> 确认字段的值
seq ===> 设置 下一个期望数据号 的值
rwnd ===> 设置接收方的窗口大小
DATA ===> 发送方发送的数据
注意:
(1)发送方的发送窗口不能超过接收方给出的接收窗口的数值
(2)TCP的窗口单位是字节,不是报文段
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
引出问题:
B向A发送了零窗口的报文段后不久,B的接收缓存又有了一些存储空间。于是B向A发送了rwnd=400的报文段。然而这个报文段在传送过程中丢失了。A一直等待收到B发送的非零窗口的通知,而B也一直等待A发送的数据。如果没有其他措施,这种互相等待的死锁局面将一直延续下去。
解决办法:
TCP为每一个连接设有一个持续计时器。只要TCP连接的一方收到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口探测报文段(仅携带1字节的数据),而对方就在确认这个探测报文段时给出了现在的窗口值。如果窗口仍然是零,那么收到这个报文段的一方就重新设置持续计时器
注意:TCP规定,即使设置为零窗口,也必须接收以下几种报文段:零窗口探测报文段、确认报文段和携带紧急数据的报文段
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
方法一:TCP维持一个变量,它等于最大报文段长度MSS。只要缓存中存放的数据达到MSS字节时,就组装成一个TCP报文段发送出去
方法二:由发送方的应用进程指明要求发送报文段,即TCP支持的推送(push)操作
方法三:发送方的一个计时器期限到了,这时就把当前已有的缓存数据装入报文段(但长度不能超过MSS)发送出去
尽管如此,如何控制TCP发送报文段的时机仍然是一个较为复杂的问题;为此出现了以下算法。
若发送应用进程把要发送的数据逐个字节地送到TCP的发送缓存,则发送方就把第一个数据字节先发送出去,把后面到达的数据字节都缓存起来。当发送方收到对第一个数据字符的确认后,再把发送缓存中的所有数据组装成一个报文段发送出去,同时继续对随后到达的数据进行缓存。只有在收到对前一个报文段的确认后才继续发送下一个报文段。Nagle算法还规定,当到达的数据已达到发送窗口大小的一半或已达到报文段的最大长度时,就立即发送一个报文段。这样做,就可以有效地提高网络的吞吐量。
方法一:让接收方等待一段时间,使得或者接收缓存已有足够空间容纳一个最长的报文段
方法二:等到接收缓存已有一半空闲的空间
在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫做拥塞(congestion)
∑对资源的需求 > 可用资源
①拥塞控制
拥塞控制:防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载
拥塞控制的一个前提:就是网络能够承受现有的网络负荷
拥塞控制是一个全局性的过程,涉及到所有的主机、所有的路由器,以及与降低网络传输性能有关的所有因素
②流量控制
流量控制:一般指点对点通信量的控制,是个端到端的问题(接收端控制发送端)
流量控制的目的:抑制发送端发送数据的速率,以便使接收端来得及接收
步骤分析:
①在吞吐量饱和之前,网络吞吐量应等于提供的负载,故吞吐量曲线是45°的斜线(理想状况)
②当提供的负载超过某一限度时,由于网络资源受限,吞吐量不再增长而保持为水平线,即吞吐量达到饱和。这就表明提供的负载中有一部分损失掉了,在理想的拥塞控制作用下,网络的吞吐量仍然维持在其所能达到的最大值。(理想状态,实际情况见第③条)
③随着提供的负载的增大,网络吞吐量的增长速率逐渐减小。也就是说,在网络吞吐量还未达到饱和时,就已经有一部分的输入分组被丢弃了
④当网络的吞吐量明显地小于理想的吞吐量时,网络就进入了轻度拥塞的状态
⑤当提供的负载达到某一数值时,网络的吞吐量反而随提供的负载的增大而下降,这时网络就进入了拥塞状态
⑥当提供的负载继续增大到某一数值时,网络的吞吐量就下降到零,网络已无法工作,这就是所谓的死锁(deadlock)
从大的方面看,可以分为开环控制和闭环控制两种方法
开环控制:在设计网络时事先将有关发生拥塞的因素考虑周到,力求网络在工作时不产生拥塞。但一旦整个系统运行起来,就不再中途进行改正了
闭环控制:基于反馈环路的概念,主要有以下几种措施:
①监测网络系统以便检测到拥塞在何时、何处发生
②把拥塞发生的信息传送到可采取行动的地方
③调整网络系统的运行以解决出现的问题
TCP进行拥塞控制的算法有四种,即
慢开始(slow-start)
拥塞避免(congestion avoidance)
快重传(fast retransmit)
快恢复(fast recovery)
(1)概述
发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口
(2)原则
发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就可以再增大一些,以便把更多的分组发送出去,这样就可以提高网络的利用率。但只要网络出现拥塞或有可能出现拥塞,就必须把拥塞窗口减小一些,以减少注入到网络中的分组数,以便缓解网络出现的拥塞。判断网络拥塞的依据就是出现了超时。
(3)思路
慢开始算法的思路:由小到大逐渐增大拥塞窗口数值。在刚发送报文段时,先把初始拥塞窗口cwnd设置为不超过2至4个SMSS的数值(旧版为1-2个)
(4)具体规定
若SMSS>2190字节,则设置初始拥塞窗口cwnd=2×SMSS字节,且不得超过2个报文段
若SMSS>1095字节且,SMSS≤2190字节,则设置初始拥塞窗口cwnd=3×SMSS字节,且不得超过3个报文段
若SMSS≤1095字节,则设置初始拥塞窗口cwnd=4xSMSS字节,且不得超过4个报文段
每收到一个对新的报文段的确认,可以把拥塞窗口增加最多一个SMSS的数值,具体公式表示如下:
- 拥塞窗口cwnd每次的增加量 = min(N,SMSS)
-
- #N表示收到确认的个数
- #SMSS代表单位
(5)举例
开始发送方先设置cwnd=1,发送第一个报文段M1,接收方收到后确认M1
发送方收到对M1的确认后,把cwnd从1增大到2,于是发送方接着发送M2和M3两个报文段,接收方收到后发回对M2和M3的确认
发送方收到对M2和M3的确认后,cwnd就从2增大到4,并可发送M4~M共4个报文段
注意:
①.使用慢开始算法后,每经过一个传输轮次(transmission round),拥塞窗口cwnd就加倍
②.在TCP的实际运行中,发送方只要收到一个对新报文段的确认,其拥塞窗口cwnd就立即加1,并可以立即发送新的报文段,而不需要等这个轮次中所有的确认都收到后再发送新的报文段
传输轮次:一个传输轮次所经历的时间其实就是往返时间RTT(RTT并非是恒定的数值)。这里的往返时间是指把拥塞窗口cwnd所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认所经历的总共时间
例如,拥塞窗口cwnd的大小是4个报文段,那么这时的往返时间RTT就是发送方连续发送4个报文段,并收到这4个报文段的确认,总共经历的时间
(6)优化
为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量。慢开始门限 ssthresh的用法如下:
当cwnd<ssthresh时,使用上述的慢开始算法
当cwnd>ssthresh时,停止使用慢开始算法而改用拥塞避免算法
当cwnd=ssthresh时,既可使用慢开始算法,也可使用拥塞避免算法
(1)算法思路
拥塞避免算法的思路是让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是像慢开始阶段那样加倍增长。因此在拥塞避免阶段就有“加法增大”AI(Additive Increase)的特点。这表明在拥塞避免阶段,拥塞窗口cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多
(2)举例
步骤1:当TCP连接进行初始化时,把拥塞窗口cwnd置为1
步骤2:在执行慢开始算法时,发送方每收到一个对新报文段的确认ACK,就把拥塞窗口值加1,然后开始下一轮的传输。因此拥塞窗口cwnd随着传输轮次按指数规律增长
步骤3:当拥塞窗口cwnd增长到慢开始门限值ssthresh时(图中①点),就改为执行拥塞避免算法,拥塞窗口按线性规律增长
步骤4:当拥塞窗口cwnd=24时,网络出现了超时(图中点②),发送方判断为网络拥塞。于是调整门限值 ssthresh=cwnd/2=12,同时设置拥塞窗口cwnd=1,进入慢开始阶段。按照慢开始算法,发送方每收到一个对新报文段的确认ACK,就把拥塞窗口值加1
步骤5:当拥塞窗口cwnd=ssthresh=12时(图中点③),改为执行拥塞避免算法,拥塞窗口按线性规律增大
步骤6:当拥塞窗口cwnd=16时(图中的点④),发送方一连收到3个对同一个报文段的重复确认(图中记为3-ACK)。
注意:
步骤6出现了个问题。有时,个别报文段会在网络中丢失,但实际上网络并未发生拥塞。如果发送方迟迟收不到确认,就会产生超时,就会误认为网络发生了拥塞。这就导致发送方错误地启动慢开始,把拥塞窗口cwnd又设置为1,因而降低了传输效率。为此,出现了快重传算法
(1)前提条件
发送方只要一连收到3个重复确认,就知道接收方确实没有收到报文段M3,因而应当立即进行重传(即“快重传”),这样就不会出现超时,发送方也不就会误认为出现了网络拥塞。
(2)规则
快重传算法首先要求接收方不要等待自己发送数据时才进行捎带确认,而是要立即发送确认,即使收到了失序的报文段也要立即发出对已收到的报文段的重复确认
(3)影响/优点
使用快重传可以使整个网络的吞吐量提高约20%
(4)举例
如图所示
步骤1:接收方收到了M1和M2后都分别及时发出了确认
步骤2:接收方没有收到M3但却收到了M4。但按照快重传算法,接收方必须立即发送对M2的重复确认,以便让发送方及早知道接收方没有收到报文段M3
步骤3:发送方接着发送M5和M6。接收方收到后也仍要再次分别发出对M2的重复确认。这样,发送方共收到了接收方的4个对M2的确认,其中后3个都是重复确认,因此触发了快重传算法。发送方立即进行重传(即“快重传”),这样就不会出现超时,发送方也不就会误认为出现了网络拥塞。
(1)前提条件
快恢复算法是用来配合快重传算法使用的。当执行快重传算法后,发送方并不执行慢启动算法,而是执行快恢复算法
(2)规则
步骤1:当收到第3个重复的ACK时,将ssthresh设置为当前拥塞窗口cwnd的一半。重传丢失的报文段。设置cwnd为ssthresh加上3倍的报文段大小
- ssthresh = cwnd / 2
- cwnd = ssthresh + 3 x MSS
-
- #发送方收到3个重复的确认,表明有3个分组已经离开了网络。这3个分组不再消耗网络的资源而是停留在接收方的缓存中.可见现在网络中减少了3个分组。因此可以适当把拥塞窗口扩大些
(3)举例
步骤1:在图中的点④,发送方知道现在只是丢失了个别的报文段。于是不启动慢开始,而是执行快恢复算法
步骤2:发送方调整门限值 ssthresh=cwnd/2=8,同时设置拥塞窗口cwnd=ssthresh=8,并开始执行拥塞避免算法
拥塞避免阶段,拥塞窗口是按照线性规律增大的,这常称为加法增大AI(Additive Increase)。而一旦出现超时或3个重复的确认,就要把门限值设置为当前拥塞窗口值的一半,并大大减小拥塞窗口的数值。这常称为“乘法减小”MD(Multiplicative Decrease)。二者合在一起就是所谓的AIMD算法
接收方根据自己的接收能力设定了接收方窗口rwnd,并把这个窗口值写入TCP首部中的窗口字段,传送给发送方。因此,接收方窗口又称为通知窗口(advertised window)。因此,发送方的发送窗口一定不能超过对方给出的接收方窗口值rwnd。发送方的窗口的上限值应当取为接收方窗口rwnd和拥塞窗口cwnd这两个变量中较小的一个,以此控制了发送方发送数据的速率。即:
发送方窗口的上限值 = Min[rwnd,cwnd]
从公式中,我们得知:
当rwnd<cwnd时,是接收方的接收能力限制发送方窗口的最大值
当cwnd<rwnd时,则是网络的拥塞程度限制发送方窗口的最大值
网络层的策略对TCP拥塞控制影响最大的就是路由器的分组丢弃策略
尾部丢弃策略(tail-drop policy):在最简单的情况下,路由器的队列通常都是按照“先进先出”FIFO(First In First Out)的规则处理到来的分组。由于队列长度总是有限的,因此当队列已满时,以后再到达的所有分组(如果能够继续排队,这些分组都将排在队列的尾部)将都被丢弃
全局同步:路由器的尾部丢弃往往会导致一连串分组的丢失,这就使发送方出现超时重传,使TCP进入拥塞控制的慢开始状态。网络中通常有很多的TCP连接(它们有不同的源点和终点),这些连接中的报文段通常是复用在网络层的IP数据报中传送。这时尾部丢弃会同时影响到很多条TCP连接,结果使这许多TCP连接在同一时间突然都进入到慢开始状态
①产生原因
全局同步使得全网的通信量突然下降了很多,而在网络恢复正常后,其通信量又突然增大很多。为了避免发生网络中的全局同步现象,出现了主动队列管理AQM。
②AQM主要概念
所谓“主动”就是不要等到路由器的队列长度已经达到最大值时才不得不丢弃后面到达的分组。而是在队列长度达到某个值得警惕的数值时(即当网络拥塞有了某些拥塞征兆时),就主动丢弃到达的分组。
③AQM的优点
提醒了发送方放慢发送的速率,因而有可能使网络拥塞的程度减轻,甚至不出现网络拥塞
④实现AQM的算法
RED(Random Early Detection 又称 Random Early Drop 或 Random Early Discard(随机早期丢弃)
实现RED时需要使路由器维持两个参数,即队列长度最小门限和最大门限。当每一个分组到达时,RED就按照规定的算法先计算当前的平均队列长度。
①若平均队列长度小于最小门限,则把新到达的分组放入队列进行排队
②若平均队列长度超过最大门限,则把新到达的分组丢弃
③若平均队列长度在最小门限和最大门限之间,则按照某一丢弃概率p把新到达的分组丢弃
由此可见,RED不是等到已经发生网络拥塞后才把所有在队列尾部的分组全部丢弃,而是在检测到网络拥塞的早期征兆时(即路由器的平均队列长度达到一定数值时),就以概率p丢弃个别的分组,让拥塞控制只在个别的TCP连接上进行,因而避免发生全局性的拥塞控制
但是因为RED的使用效果不太理想,因此RFC已经不推荐使用RED算法了
TCP是面向连接的协议。运输连接是用来传送TCP报文的。TCP运输连接的建立和释放是每一次面向连接的通信中必不可少的过程
连接建立、数据传送、连接释放
TCP连接的建立采用客户服务器方式。主动发起连接建立的应用进程叫做客户(client),而被动等待连接建立的应用进程叫做服务器(server)
TCP建立连接的过程叫做握手,握手需要在客户和服务器之间交换三个TCP报文段,即三次握手。如下图:
步骤1:B的TCP服务器进程先创建传输控制块TCB,然后服务器进程就处于LISTEN(收听)状态,等待客户的连接请求
注意:TCB存储了每一个连接中的一些重要信息,如:TCP连接表,指向发送和接收缓存的指针,指向重传队列的指针,当前的发送和接收序号,等等
步骤2:A的TCP客户进程也是首先创建传输控制模块TCB。然后,在建立TCP连接时,A向B发出连接请求报文段,这时首部中的同步位SYN=1,同时选择一个初始序号seq=x。这时,TCP客户进程进入SYN-SENT(同步已发送)状态。
注意:TCP规定,SYN报文段(即SYN=1的报文段)不能携带数据,但要消耗掉一个序号
步骤3:B收到连接请求报文段后,若同意建立连接,则向A发送确认。在确认报文段中应把SYN位和ACK位都置1,确认号是ack=x+1,同时也为自己选择一个初始序号seq=y
注意:这个报文段也不能携带数据,但同样要消耗掉一个序号。这时TCP服务器进程进入SYN-RCVD(同步收到)状态。B发送给A的报文段也可拆成两个报文段。即先发送一个确认报文段(ACK=1,ack=x+1),然后再发送一个同步报文段(SYN=1,seq=y)。这样的过程就变成了四报文握手,但效果一样
步骤4:TCP客户进程收到B的确认后,还要向B给出确认。确认报文段的ACK置1,确认号ack=y+1,而自己的序号seq=x+1
注意:TCP的标准规定,ACK报文段可以携带数据。但如果不携带数据则不消耗序号,在这种情况下,下一个数据报文段的序号仍是seq=x+1。这时,TCP连接已经建立,A进入ESTABLISHED(已建立连接)状态
步骤5:当B收到A的确认后,也进入ESTABLISHED状态
了解完TCP的连接过程之后,我们心中或许会有一个疑问——为什么A最后还要发送一次确认呢?
其实,这主要是为了防止已失效的连接请求报文段突然又传送到了B,因而产生错误,具体情况如下:
假如A发出的第一个连接请求报文段在某些网络结点长时间滞留了,以致延误到连接释放以后的某个时间才到达B。但是这是一个早已失效的报文段。B收到此失效的连接请求报文段后,误认为是A又发出一次新的连接请求。于是就向A发出确认报文段,同意建立连接。假定不采用报文握手,那么只要B发出确认,新的连接就建立了。由于现在A并没有发出建立连接的请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的运输连接已经建立了,并一直等待A发来数据。B的许多资源就这样白白浪费了
数据传输结束后,通信的双方都可释放连接,通常需要四报文握手,即四次握手
步骤1:连接完成后,A和B都处于ESTABLISHED状态
步骤2:A的应用进程先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。A把连接释放报文段首部的终止控制位FIN置1,其序号seq=u,它等于前面已传送过的数据的最后一个字节的序号加1。这时A进入FIN-WAIT-1(终止等待1)状态,等待B的确认
注意:TCP规定,FIN报文段即使不携带数据,它也消耗掉一个序号
步骤3:B收到连接释放报文段后即发出确认,确认号是ack=u+1,而这个报文段自己的序号是v,等于B前面已传送过的数据的最后一个字节的序号加1。然后B就进入CLOSE-WAIT(关闭等待)状态
注意:TCP服务器进程这时应通知高层应用进程,因而从A到B这个方向的连接就释放了,这时的TCP连接处于半关闭(half-close)状态,即A已经没有数据要发送了,但B若发送数据,A仍要接收。也就是说,从B到A这个方向的连接并未关闭,这个状态可能会持续一段时间
步骤4:A收到来自B的确认后,就进入FIN-WAIT-2(终止等待 2)状态,等待B发出的连接释放报文段
步骤5:
若B准备关闭连接,其应用进程就通知TCP释放连接。这时B发出的连接释放报文段必须使FIN=1
若B准备继续连接,并且向A又发送了一些数据后,B发完数据后假定现在的序号为w,必须重复上次已发送过的确认号ack=u+1。这时B就进入LAST-ACK(最后确认)状态,等待A的确认
步骤6:A在收到B的连接释放报文段后,必须对此发出确认。在确认报文段中把ACK置1,确认号ack=w+1,而自己的序号是seq=u+1
(根据TCP标准,前面发送过的FIN报文段要消耗一个序号),然后进入到TIME-WAIT(时间等待)状态。4分钟后,A进入CLOSED状态,才能开始建立下一个新的连接。当A撤销相应的传输控制块TCB后,TCP连接彻底释放
注意:A进入TIME-WAIT状态后,必须经过时间等待计时器(TIME-WAIT timer)设置的时间2MSL(最长报文段寿命,RFC 建议设为2分钟。但TCP允许不同的实现可根据具体情况使用更小的MSL值。因此,从A进入到TIME-WAIT状态后,要经过4分钟才能进入到CLOSED状态)后,A才进入到CLOSED状态
为什么A在TIME-WAIT状态必须等待2MSL的时间呢?
(1)为了保证A发送的最后一个ACK报文段能够到达B。
ACK报文段有可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认。B会超时重传这个FIN+ACK报文段,而A就能在2MSL时间内收到这个重传的FIN+ACK报文段。接着A重传一次确认,重新启动2MSL计时器。最后,A和B都正常进入到CLOSED状态。
(2)防止TCP连接的建立中提到的“已失效的连接请求报文段”出现在本连接中。
A在发送完最后一个ACK报文段后,再经过时间2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。
B只要收到了A发出的确认,就进入CLOSED状态。同样,B在撤销相应的传输控制块TCB后,就结束了这次的TCP连接。我们注意到,B结束TCP连接的时间要比A早一些。
从上文中我们知道,TCP主动释放中,是利用了等待计时器外,除此之外,TCP还有一种被动释放连接的方式,这种方式利用的是保活计时器(keepalive timer)
假如,客户已主动与服务器建立了TCP连接。但后来客户端的主机突然出故障。显然,服务器以后就不能再收到客户发来的数据。因此,应当有措施使服务器不要再白白等待下去。这就是使用保活计时器。服务器每收到一次客户的数据,就重新设置保活计时器,时间的设置通常是两小时。若两小时没有收到客户的数据,服务器就发送一个探测报文段,以后则每隔75秒钟发送一次。若一连发送10个探测报文段后仍无客户的响应,服务器就认为客户端出了故障,接着就关闭这个连接
注意图中有三种不同的箭头
粗实线箭头表示对客户进程的正常变迁
粗虚线箭头表示对服务器进程的正常变迁
细线箭头表示异常变迁
下面我带领大家实践下TCP数据报的分析:
我决定用python编写一个简单的TCP对话程序
(1).服务端代码如下
- import socket
- socketserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- port = 8866
- # 绑定端口号
- socketserver.bind(('', port))
- socketserver.listen(5)
- clientsocket,addr = socketserver.accept()
- while True:
- recvmsg = clientsocket.recv(1024)
- strData = recvmsg.decode("utf-8")
- if strData=='q':
- break
- print("receive: "+strData)
- #python V3 msg = input("reply: ")
- # 上面是Python3语法
- msg = raw_input("send: ")
- # 上面是python2语法
- msg = str(msg)
- clientsocket.send(msg.encode("utf-8"))
- socketserver.close()
(2).客户端代码如下
- import socket
- client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- host = "xxx.xxx.xxx.xxx"
- # 上面是绑定服务器的IP地址,这里根据具体情况修改IP地址
- port = 8866
- # 上面是绑定端口号,可以自己指定一个不被使用的端口号进行通信
- client.connect((host, port))
- while True:
- msg = input("send: ")
- # 上面是Python3的语法
- # python V2 msg = raw_input("send: ")
- # 上面是python2的语法
- if msg == 'q':
- break
- msg = msg
- client.send(msg.encode("utf-8"))
- msg = client.recv(1024)
- print("receive: " + msg.decode("utf-8"))
- client.close()
(3)运行客户端tcp,客户端IP为192.168.8.222
(4)运行服务端tcp,服务器IP为192.168.8.111
(5).当他们连接成功后,使用wireshark抓取tcp数据报,抓取cp连接的三次握手数据报
(1)第一、二次握手数据报分析——鼠标选中第一个数据报
从第三层——网络层的服务——IP数据报中我们可以得到以下信息:
原地址为:192.168.8.222
目的地址为:192.168.8.111
我们知道tcp客户端的IP地址即原地址,tcp服务器地址即目的地址。即这是一条由客户端向服务器发送的请求连接报文
从第四层——运输层——TCP数据报中我们得知:
源端口为:38304
目的端口为:8866
这个8866是我们设置的,38304端口是客户端自动选取的一个未使用端口
我们知道TCP连接建立时,TCP客户端要向服务器发送请求数据报,并且
TCP首部中的SYN = 1
seq = x(这里的x为0)
同时从抓取的数据中我们可以得知二者协商的窗口值大小为29200
从截取的数据报中,我们还可以知道,该tcp报文首部长40字节,确认号为0(期望收到对方下一个报文段的第一个数据字节的序号)
接下来,我们鼠标选中第二个数据报,看服务器第一次对客户端的回应
服务器同意建立连接,并且在发送给客户端的确认报文段中各值如下(注意,这里是服务器发送给客户端的确认报文,所以应该是截图中的第三个报文段):
SYN置1
ACK置1
seq=y=0
ack=x+1=0+1=1
(2)第二、三次握手数据报分析(源地址为客户端即192.168.8.222,目的地址为服务器即192.168.8.111)
TCP客户进程收到服务器的确认后,还要向服务器给出确认。确认报文段的各值如下:
ACK置1
自己的序号seq=x+1=1
确认号ack=y+1=1
TCP连接建立好之后,我们就可以利用TCP协议发送数据了,这时候我们使用客户端向服务器发送数据“123456789”
这时,我们使用wireshark继续抓取数据,得到以下数据报
从上面的两个截图中,我们看到了该数据报的序号与确认号,接下来,我们抓取通信中发送的数据
我们发送的有用数据为“123456789“,占九个字节。可是tcp报文中的数据部分一共占12个字节。前三个非实体数据为
(1b)H = (27)D === (ESC)ASCII
(4f)H = (79)D === (O)ASCII
(52)H = (82)D === (R)ASCII
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。