- TCP三次握手建立连接
- MTU
- TCP状态转换图
- 半关闭
shutdown - 2MSL
- 端口复用
TCP通信
- 网络层传输不稳定,则上层传输层有两种应对:完全不弥补——UDP(无连接不可靠报文传输)、完全弥补——TCP(面向连接的可靠数据包传递)
- TCP:先建立固定连接通路,再发送数据。建立连接:三次握手;关闭连接:四次握手。
- 注意:TCP和UDP一样都会在网络传输过程中出现丢包,但TCP会重传,而UDP不会重传。因此,考虑到丢包重传,TCP数据包的数据大小设置了一个上限,以免重传时消耗过多系统资源。这反映在以太网帧上,表现为数据部分限制在1500字节以内。而IP协议中规定,IP数据包最多封装65535(即16位)字节,因此如果下层采用以太网帧协议(ARP),则需将IP数据包拆包。
- 三次握手:确立客户端和服务器端的连接通路。这个过程正是客户端的
connect
函数和服务器端的accept
函数相互连接的过程。 - 半关闭:Linux中允许建立连接的双方有一端关闭,此时信息只能单向流动。这也是TCP中为什么建立连接要三次握手而关闭连接需要四次握手的原因。
- 四次握手:当一方发起关闭请求且另一方发回确认消息后,即为半关闭。接下来另一方也发起关闭请求,收到关闭方的确认消息后,连接完全关闭。
- 在TCP数据报中,除了包含16位源端口号和16位目的端口号之外,还有32位序号,它指的是所发送数据包的包号。还有32位确认序号,它指的是跟在ACK标志位后的那个数字。此外还有6个标志位,常用的有:
SYN
:建立连接请求
ACK
:确认消息
FIN
:关闭连接请求。 - TCP通讯时序如下:
上图中,establish connection部分为三次握手建立连接,接下来是双方数据传递过程,最后是close connection四次握手关闭连接。
连接双方的数据包号各自是不同的,但对方发回来的ACK标志位后跟的确认序号是跟自己的数据报号以及括号里的字节数相关的(当括号里标0时,表示当前这个数据包没有消息数据,仅是一个标志位)。一个标志位尽管只有一位,但占用一个字节。
MTU
- 最大传输单元(Maximum Transmission Unit,MTU),是指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位)。最大传
输单元这个参数通常与通信接口有关(网络接口卡、串口等)。例如,以太网(Ethernet)协议:1500字节;网络层IP协议:65535(2的16次方)字节。 - mss:最大报文段长度,指的是一次传递的数据包中所能携带的数据大小,受MTU影响,同时还跟协议的首部有关。
- 滑动窗口:实现TCP流量控制,防止一端发送数据过快而另一端来不及处理导致数据丢失。
1)发送端发起连接,声明最大段尺寸是1460,初始序号是0,窗口大小是4K,表示“我
的接收缓冲区还有4K字节空闲,你发的数据不要超过4K”。接收端应答连接请求,声明最大
段尺寸是1024,初始序号是8000,窗口大小是6K。发送端应答,三方握手结束。
2)发送端发出段4-9,每个段带1K的数据,发送端根据窗口大小知道接收端的缓冲区满
了,因此停止发送数据。
3)接收端的应用程序提走2K数据,接收缓冲区又有了2K空闲,接收端发出段10,在应答
已收到6K数据的同时声明窗口大小为2K。
4)接收端的应用程序又提走2K数据,接收缓冲区有4K空闲,接收端发出段11,重新声明
窗口大小为4K。
5)发送端发出段12-13,每个段带2K数据,段13同时还包含FIN位。
6)接收端应答接收到的2K数据(6145-8192),再加上FIN位占一个序号8193,因此应答
序号是8194,连接处于半关闭状态,接收端同时声明窗口大小为2K。
7)接收端的应用程序提走2K数据,接收端重新声明窗口大小为4K。
8)接收端的应用程序提走剩下的2K数据,接收缓冲区全空,接收端重新声明窗口大小为6K。
9)接收端的应用程序在提走全部数据后,决定关闭连接,发出段17包含FIN位,发送端应答,连接完全关闭。
上图在接收端用小方块表示1K数据,实心的小方块表示已接收到的数据,虚线框表示接收缓冲区,因此套在虚线框中的空心小方块表示窗口大小,从图中可以看出,随着应用程序提走数据,虚线框是向右滑动的,因此称为滑动窗口。
注意:
1)从这个例子还可以看出,发送端是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),在底层通讯中这些数据可能被拆成很多数据包来发送,但是一个数据包有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议。
2)而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。
3)UDP中没有滑动窗口,这也反映在UDP数据报格式中,其中没有像TCP数据报中的16位窗口大小成员。
TCP状态转换
-
TCP状态转换图
注:图中,加粗实线表示主动操作,虚线表示被动操作。另外,实线表示双方同时操作(极端情况下)。主动方和被动方的称呼不区分客户端和服务器端。在TCP通信中,相对应的状态转换时序如下图所示。
-
TCP状态转换时序
注:TCP状态转换图应用于程序的调试过程中。
-
半链接(半关闭)
1)一方发起了关闭连接请求(调用close
或shutdown
)并收到了对方的ACK
后,当前连接即处于半关闭状态,此时主动方处于FIN_WAIT_2
状态,被动方处于CLOSE_WAIT
状态。
2)shutdown
函数:
使用close
中止一个连接,但它只是减少描述符的引用计数,并不直接关闭连接,只有当描述符的引用计数为0时才关闭连接。
shutdown
可直接关闭描述符,不考虑描述符的引用计数,可选择中止一个方向的连接。
注意:
1)如果有多个进程共享一个套接字,close
每被调用一次,计数减1,直到计数为0时,也就是所用进程都调用了close
,套接字将被释放。
2)在多进程中如果一个进程中shutdown(sfd, SHUT_RDWR)
后其它的进程将无法进行通信。如果一个进程close(sfd)
将不会影响到其它进程。 -
2MSL
在半关闭状态下,当处于CLOSE_WAIT
状态的被动方也发起关闭请求(调用close
或shutdown
)后,由上图可知,它的状态变为LAST_ACK
,等待来自对方的ACK
。在收到处于FIN_WAIT_2
状态的主动方的ACK
后,该连接完全关闭,此时该被动方的状态直接变为CLOSED
。而主动方的状态仍暂时维持不变,它要等待一个2MSL
的时间段,留给被动方接收来自自己的最后一个ACK
。然后主动方的状态才会最终变为CLOSED
。 -
端口复用
把下列两句置于主程序中的socket函数和bind函数之间,可复用处于TIME_WAIT状态、等在2MSL时间段里的服务器所占的端口。这样就可以不必等到2MSL时间段过去后才能再使用该端口。例如,在某些需要服务器端一直与客户端实时连接、而又需要短暂修改维护服务器端程序的场景中。
int opt = 1;
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
- 心跳检测:实时检测服务器端和客户端之间所保持的连接状态。方法有:
应用层协议层面:1)心跳包、2)乒乓包(除探测c\s连接状态外还能携带简单的数据)。
设置TCP属性:探测分节(已几乎不用,因为探测时间间隔太久,2小时)。