Skip to content

先来分个层

{
应用程序(浏览器、邮件客户端等)
包含【Socket库】
}
{
协议栈
TCP/UDP
一般应用程序都是使用TCP收发数据、诸如DNS查询收发短数据是使用UDP
IP协议传送网络包(互联网传输的数据会被切成几十-几千字节的块,每一块数据就成为一个网络包)
}
{
网卡驱动程序
}
{
硬件网卡
}


每一个网络包都有TCP头部信息(控制信息),包含:

  1. 双方端口
  2. 序号(发送方告知这个数据包相当于所有数据的第几个字节)
  3. ACK号(接收方告知发送方,已经收到了第几个字节。acknowledge的缩写)
  4. 数据偏移量(数据搁哪开始的)
  5. 控制位(SYN相互确认序号,表示连接、FIN表示断开。。。。)
  6. 其他。。。。

连接的具体过程

  1. 从Socket库的connect(描述符、服务器ip和端口)开始
  2. 创建TCP头部信息,将控制位SYN设为1,表示连接;ACK为0
  3. 协议栈中的TCP模块委托IP模块发送,与服务器TCP模块交换控制信息
  4. 服务器TCP模块根据信息找到端口对应的套接字
  5. 找到后在套接字中写入信息,将状态改为正在连接,ACK设为1
  6. 同客户端将TCP头部信息委托IP模块发送到客户端
  7. 客户端收到,向套接字中写入信息,将状态修改为连接完毕,将ACK修改为1,发回服务器(表示刚才收到)
  8. 服务器再次收到,连接全部完成

序号和ACK的用法(传送数据过程中)

序号一般不是从1开始的(防止预测通信过程而发动攻击)
(在上面的第7步中,客户端告诉了服务端,随机生成的序号是几)

为了简化理解,以下描述把序号作为1开始:

发送方:我现在发送所有数据的第1(序号1)字节,一共1460字节
接收方:已收到1460字节,确认ACK:1461(字节数(算出来的=网络包-头部信息)+序号)
发送方:我现在发送数据从第1461字节(序号1461),一共1460字节
接收方:已收到1460字节,确认ACK:2921(上次收到1460字节,下次序号不是1461,则丢包了)

注意:数据是双向传输的,所以以上过程也是双向的,为了方便理解,上面只简述了从客户端传输数据至服务器端

传输的包存放在缓冲区,通过ACK这个机制,可以判断哪些包丢了,丢了重发
如果TCP重传几次都无效,则强制结束通信,并向应用程序报错

以上是基本原理,实际情况非常有意思

1.假设网络繁忙拥堵,长时间收不到ACK就会重传,频繁重传则会导致网络更堵
那我多等一会儿
多等就会延迟,延迟也会导致网络变慢
(动态调整)

2.每发一个包,等ACK再发下一个,太浪费时间
所以,发包不等ACK,连续发下一个,这有一个问题
接收方要处理数据(还原数据,算ACK),假如,发的速度超过处理的速度
则会挤爆接收方的缓冲区,导致后面的数据都进不来 (滑动窗口)