Please enable JavaScript.
Coggle requires JavaScript to display documents.
Chapter 2:用电信号传输TCP/IP数据(探索协议栈和网卡) - Coggle Diagram
Chapter 2:用电信号传输TCP/IP数据(探索协议栈和网卡)
Part 1:创建套接字
协议栈的内部结构:
协议栈最上层是
网络应用程序
,应用程序下面是
Socket库
(其中包括解析器等)。
再下面是
操作系统内部
,其中包括
协议栈
。协议栈的上半部分分为两块,分别是负责用
TCP协议
收发数据的部分和负责用
UDP协议
收发数据的部分。(浏览器、邮件等一般应用程序收发数据时用TCP,DNS查询等收发较短的控制数据时用UDP)。
协议栈的下半部分负责用
IP协议
控制
网络包收发
操作。(其中IP还包括
ICMP协议
和
ARP协议
,ICMP用于告知网络包传送过程中产生的错误以及各种控制消息,ARP用于根据IP地址查询相应的以太网MAC地址)
IP下面的
网卡驱动程序
负责控制
网卡硬件
,而最下面的
网卡
负责完成实际的数据收发操作。
套接字的实体就是通信控制信息:
协议栈内部有一块用于
存放控制信息
的内存空间,其中记录了通信对象的IP地址、端口号、通信操作的状态等用于
控制通信操作
的信息,而这些信息本身就是
套接字
协议栈在执行操作时访问参阅这些控制信息,才能开始数据传输。
调用Socket时的操作:
浏览器委托协议栈使用TCP协议来收发数据。
首先
创建套接字
,应用程序调用Socket向协议栈申请创建套接字,分配用于存放一个套接字的内存空间,然后填写控制信息。
接下来将这个套接字的描述符告知应用程序。
Part 2:连接服务器
连接的含义:
连接实际上是
通信双方交换控制信息
。
套接字刚刚创建时,其中并未存放任何数据,也不知道具体的通信对象。在服务器端甚至连应用程序也不知道通信对象是谁,故需要客户端将IP地址和端口号告知服务器。
负责保存控制信息的头部:
控制信息
分为两类,分别是
客户端和服务器相互联络时交换的控制信息
和保存在套接字中的
控制协议栈操作
的信息。
其中前者在TCP协议中的规格进行了定义,位于网络包的开头。而套接字的控制信息和协议栈的程序本身是一体的,通信对方不可见,且会因为协议栈的实现的不同而不同,但一些重要的套接字控制信息无论何种操作系统的协议栈都是互通的。
连接操作的实际过程:
应用程序调用Socket库的connect,将
服务器的IP地址和端口号
传递给协议栈中的
TCP模块
,然后TCP模块创建表示
连接控制信息的头部
,通过头部中发送方和接收方的端口号可以找到要连接诶的套接字。
当TCP头部创建好后,TCP模块将信息传递给
IP模块
并委托其发送到
服务器端的IP模块
,然后服务器端的IP模块再将接收到的数据传递给
服务器端的TCP模块
,然后TCP模块根据TCP头部中的信息找到端口号对应的套接字,向套接字中写入相应的信息,并将状态改为正在连接。
最后,把响应返回给客户端。
Part 3:收发数据
将HTTP请求消息交给协议栈:
应用程序调用write将数据交给
协议栈
,但是协议栈并不关心数据内容,并且不会马上将数据分包发送(防止发送大量小包),而是放入缓冲区,在数据
积累一定量
后再发送出去,至于具体的积累量需要根据每个网络包能容纳的
最大数据长度MSS
和
最大等待时间
共同决定。
此外,当某次
发送数据过长
以至于大于MSS时,需要对其进行拆分分别放入单独的网络包中。
使用ACK号确认网络包已收到:
TCP需要确认对方是否成功收到网络包,以此决定是否要重发包。
确认原理如下:首先,TCP模块在拆分数据时,会先算好每一块数据相当于从头开始的第几个字节,作为
序号
存在TCP头部中,此外
发送数据的长度
也要告知对方。
接收方收到数据后,根据序号确定是否发生数据遗漏,如果未遗漏,则将到目前为止接收到的数据长度加起来作为写入头部的
ACK号
发回给发送方。
(需要注意,序号并不都是从1开始,而是随机生成)
(此外,这个过程是双向的)
根据网络包平均往返时间调整ACK号等待时间:
当发送方长时间未收到返回的ACK时,就会选择重发数据包,而这个最大等待时间一般会根据历史ACK号的返回时间
动态调整
使用窗口有效管理ACK号:
在发送一个包后,
不等待ACK号返回
,而是直接发送后续的一系列包。这被称为
滑动窗口
。
但如果连续发送过多数据包,可能
超过接收方的处理能力
,使接收缓冲区溢出。
故需要在缓冲区更新时通过TCP头部中的
窗口
字段将自己能接受的数据量告知给发送方。
ACK与窗口的合并:
每当接收方将数据从缓冲区传递给应用程序导致缓冲区剩余容量增加时,便
告知发送方新的窗口大小
。
每当接收方收到数据时,如果确认内容没有问题,便向发送方
返回ACK号
。
为了避免多余的包发送,接收方在发送ACK号和窗口更新时,并不会马上把包发送出去,而是会等待一段时间,在出现其他通知操作时
合并在一个包里
发送。
Part 4:从服务器断开连接并删除套接字
数据发送完毕断开连接:
应用程序判断所有数据都已经发送完毕时,数据发送完毕的一方发起断开过程。以服务器一方发起断开过程为例。
首先,服务器一方的应用程序调用Socket库的
close程序
,服务器的
协议栈
于是生成
包含断开信息的TCP头部
,即将控制位的
FIN位
设为1 。之后协议栈委托IP模块向
客户端
发送数据,同时服务器的
套接字
中也会记录下断开操作的相关信息。
之后客户端收到服务器发来的TCP头部,客户端的协议栈会将自己的
套接字
标记为进入
断开操作状态
,之后为了告知服务器已成功收到TCP包,客户端会向服务器返回一个ACK号。
之后,应用程序调用
read
来从协议栈读取数据,客户端应用程序调用close,客户端协议栈也生成一个FIN位为1的TCP包,委托IP模块发送给服务器,一段时间后,服务器返回ACK后确保收到,至此通信全部结束。
删除套接字:
通信结束后,便可删除套接字,但为了避免误操作,往往会等待一段时间后再删除。
Part 5:IP与以太网的包收发操作
包的基本知识:
包由
头部
和
数据
两部分构成,头部包含地址等控制信息。
在包的传输过程中,包被发往最近的网络转发设备,转发设备根据头部的目的地址对设备内的表单进行查询,判断接下来的传输方向,传到下一个设备后又以同样的方式继续传输,最终到达接收方的网络设备。
而在TCP/IP网络中,有路由器和集线器两种不同的转发设备,其中
路由器
按照
IP协议
根据目标地址判断下一个路由器的位置,而
集线器
按照
以太网协议
在子网中将网络包传输到下一个路由器。
故TCP/IP包有用于以太网协议的
MAC头部
和用于IP协议的
IP头部
。其中IP头部用于存放目的服务器的IP地址,MAC头部用于存放查询到的下一个路由器的以太网地址
包收发操作概览:
Part 6:用UDP协议收发数据的操作