TCP数据段
- 源端口(Source port)和目的端口(Destination port):
字段标明了一个连接的两个端点用来跟踪同一时间内通过网络的不同会话。一般每个端口对应一个应用程序
- 序列号(Sequence number)
字节号 (32 位),表示一个字节的编号。
- 初始序列号ISNs(initial sequence numbers )
随机产生的。
- SYN
携带了ISNs 和SYN 控制位的数据段。
- 确认号(Acknowledgement number)
期望接收的字节号 (32位) 。
- TCP段头长度(TCP header length)
TCP段头长度, 单位32位(4字节)。
- 保留域/字段
逐步启用,如做拥塞控制等。
- URG
(1)当紧急指针使用的时候,URG 被置为1。紧急指针是一个对于当前序列号的字节偏移量,标明紧急数据从哪里开始。
(2)当URG=1时,表明有紧急数据,必须首先处理收方收到这样的数据后,马上处理,处理完后恢复正常操作即使win=0,也可以发送这样的数据。
- ACK
(1)为1 表示确认号有效,
(2)为0 标明确认号无效。
- PSH(push)
表示这是带有PUSH标志的数据,接收方收到这样的数据,应该立刻送到上层,而不需要缓存它。
- RST(reset)
被用来重置一个已经混乱的连接。
- SYN
用在连接建立过程中:
(1)当SYN=1,ACK=0 连接请求,
(2)当SYN=1,ACK=1 连接接受。
- FIN
被用来释放连接,它表示发送方已经没有数据要传输了,但是可以继续接收数据。
- Window size
告诉对方可以发送的数据字节数,从确认字节号开始(决定于接收方)。
- Checksum:
提供额外的可靠性,校验的范围包括头部、数据和概念性的伪头部。
- 选项域
选项域提供了一种增加基本头没有包含内容的方法。
- 简图(建立连接和断开连接)
TCP的三次握手(建立连接)
① 第一次握手:
建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
② 第二次握手:
服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
③ 第三次握手:
客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
注意:③的必要性:
在服务端对客户端的请求进行回应(第二次握手)后,就会理所当然的认为连接已建立,而如果客户端并没有收到服务端的回应呢?此时,客户端仍认为连接未建立,服务端会对已建立的连接保存必要的资源,如果大量的这种情况,服务端会崩溃。
TCP 的四次挥手(断连过程),以及单向连接关闭后还能否通信
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
- 四次挥手过程:
- 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。
- 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
- 服务器B关闭与客户端A的连接,发送一个FIN给客户端A。
- 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。
- 四次挥手的原因:
- 因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。
- 但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
TCP 和UDP 的区别
- TCP 面向连接;UDP 是无连接的,即发送数据之前不需要建立连接。
- TCP 提供可靠的服务,也就是说,通过TCP 连接传送的数据,无差错,无丢失,无重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付。
- TCP 面向字节流,实际上是TCP 把数据看成一连串无结构的字节流。UDP是面向报文的,应用层交给UDP多长的报文,UDP 就按照发送,即一次发送一个报文。UDP 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对事实应用很有用,如IP电话,事实视频会议等)
- 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信。
- TCP首部开销20字节;UDP的首部开销小,只有8个字节。
- TCP的逻辑通信信道是全双工的可靠信道;UDP则是不可靠信道。
TCP怎样保证可靠
1. 确认和重传机制
建立连接三次握手时同步确认双方的“序列号+确认号+窗口信息”,是确认重传、流控的基础。
传输过程中,如果Checksum校验失败、丢包或延时,发送端重传。
2. 数据排序
TCP有专门的序列号SN字段,可提供数据re-order。
3. 流量控制
滑动窗口和计时器的使用。TCP窗口中会指明双方能够发送接收的最大数据量,发送方通过维持一个发送滑动窗口来确保不会发生由于发送方报文发送太快接收方无法及时处理的问题。
4. 拥塞控制
TCP的拥塞控制由4个核心算法组成:
“慢启动”(Slow Start)
“拥塞避免”(Congestion avoidance)
“快速重传 ”(Fast Retransmit)
“快速恢复”(Fast Recovery)
TCP的流量控制
采用滑动窗口机制。
-
滑动窗口协议的基本原理就是在任意时刻,发送方都维持了一个连续的允许发送的帧的序号,称为发送窗口;同时,接收方也维持了一个连续的允许接收的帧的序号,称为接收窗口。
-
发送窗口和接收窗口的序号的上下界不一定要一样,甚至大小也可以不同。不同的滑动窗口协议窗口大小一般不同。发送方窗口内的序列号代表了那些已经被发送,但是还没有被确认的帧,或者是那些可以被发送的帧。
-
发送和接受方都会维护一个数据帧的序列,这个序列被称作窗口。发送方的窗口大小由接受方确定,目的在于控制发送速度,以免接受方的缓存不够大,而导致溢出,同时控制流量也可以避免网络拥塞。
-
发送端收到接收方的ACK,则窗口的左边缘向右收缩,窗口的右边缘则向右扩展,此时窗口就向前“滑动了”。
TCP进行拆包和组装包
- 拆包:
对于拆包目前常用的是以下两种方式:
- 动态缓冲区暂存方式。
之所以说缓冲区是动态的是因为当需要缓冲的数据长度超出缓冲区的长度时会增大缓冲区长度。大概过程描述如下:
(1)为每一个连接动态分配一个缓冲区,同时把此缓冲区和 SOCKET 关联,常用的是通过结构体关联。
(2)当接收到数据时首先把此段数据存放在缓冲区中。
(3)判断缓存区中的数据长度是否够一个包头的长度,如不够,则不进行拆包操作。
(4)根据包头数据解析出里面代表包体长度的变量。
(5)判断缓存区中除包头外的数据长度是否够一个包体的长度,如不够,则不进行拆包操作。
(6)取出整个数据包,这里的"取"的意思是不光从缓冲区中拷贝出数据包,而且要把此数据包从缓存区中删除掉。删除的办法就是把此包后面的数据移动到缓冲区的起始地址。
这种方法有两个缺点:
(1)为每个连接动态分配一个缓冲区增大了内存的使用;
(2)有三个地方需要拷贝数据,一、是把数据存放在缓冲区,二、是把完整的数据包从缓冲区取出来,三、是把数据包从缓冲区中删除。这种拆包的改进方法会解决和完善部分缺点。
- 利用底层的缓冲区来进行拆包
(1)由于TCP也维护了一个缓冲区,所以我们完全可以利用TCP的缓冲区来缓存我们的数据,这样一来就不需要为每一个连接分配一个缓冲区了。
(2)另一方面我们知道 recv 或者 wsarecv 都有一个参数,用来表示我们要接收多长长度的数据。利用这两个条件我们就可以对第一种方法进行优化了。
对于阻塞 SOCKET 来说,我们可以利用一个循环来接收包头长度的数据,然后解析出代表包体长度的那个变量,再用一个循环来接收包体长度的数据。
- 组装包
TCP和UDP用一个端口发送信息是否冲突
不冲突。
- TCP、UDP可以绑定同一端口来进行通信,许多协议已经这样做了。例如,DNS适用于udp / 53和tcp / 53。
- 因为数据接收时是根据五元组 { 传输协议,源IP,目的IP,源端口,目的端口}判断接受者的。