第二章 用电信号传输TCP/IP数据
关键词:TCP/IP,套接字,协议栈,IP地址,端口号,包,头部,网卡,网卡驱动,MAC地址,以太网控制器,ICMP,UDP
一、创建套接字
1、协议栈的内部结构
TCP/IP软件采用分层结构:上层会向下层委派任务

协议栈位于操作系统内部,协议栈内部包括上下两个部分。
-
上面部分包括:
-
TCP模块,负责用TCP协议收发数据,
-
UDP模块,负责用UDP协议收发数据。
适用范围:
-
像浏览器、邮件等一般的应用程序都是使用TCP收发数据的;
-
像DNS查询等,收发较短的控制数据时,则使用UDP模块。
-
-
下面一半是IP模块,是用IP协议控制网络包收发操作的部分。
IP模块:负责将数据切分成一个个网络包,以这种方式在互联网上传输。
IP模块中还包含
ICMP协议模块和ARP协议模块:-
ICMP协议模块:告知网络包传送过程中产生的错误,以及各种控制信息
-
ARP协议模块:根据IP地址查询相应的以太网MAC地址。
-
-
网卡驱动程序:负责控制网卡硬件
-
网卡:负责实际的收发操作,对网线中的信号执行发送、接收操作
2、套接字的实体就是通信控制信息
套接字:在协议栈内部一块用于存放控制信息的内存空间
功能:记录用于控制通信操作的控制信息。
(通信对象的IP地址、端口号、通信操作的进行状态:是否收到响应,发送数据已经经过了多长时间)
netstat命令:Windows的命令行中输入netstat则显示套接字内容

进程标识符(PID,Process ID):操作系统为了标识程序而分配的编号,使用任务管理器可以查询所对应的程序名称
-
第8行:表示进程标识符(PID)为4的程序正在使用IP地址为10.10.1.16的网卡与IP地址为10.10.1.80的对象进行通信
-
第1行:表示进程标识符(PID)为984的程序正在135端口等待另一方的连接,其中本地IP与远程IP都为0.0.0.0,表示通信还没开始
3、调用socket时的操作

-
创建套接字阶段:调用socket申请创建套接字,写入初始状态的控制信息。将套接字的描述符返回给应用程序。
-
描述符是给应用程序区分不同套接字的标识,在收发信息的时候使用。
二、连接服务器
1、连接
连接(Connect):通信双方交换控制信息。
连接操作的目的:
-
客户端向服务器传达开始通信请求,并告知服务器自己的IP地址和端口号(提供给服务器的套接字)
-
把服务器的IP地址和端口号写入套接字。
-
分配缓冲区,临时存放要收发的数据的内存空间
2、负责保存控制信息的头部
第一类控制信息:存放控制信息的头部——TCP头部
存放:客户端与服务器相互联络时,交换的控制信息
依据协议:TCP协议
记录内容:通信双方的端口号,序号(发送数据的顺序编号),ACK号(接受数据的顺序编号)

在连接阶段发送的网络包没有实际的数据,只有控制信息,如下图b所示

第二类控制信息:保存在套接字中,用来控制协议栈操作的信息
3、连接操作的实际过程
1 | connect(<描述符>,<服务器IP地址和端口号>,……) |
- 客户端发送
-
客户端创建一个TCP头部
-
通过双方端口的信息,客户端(发送方)的套接字 准确找到服务器(接收方)的套接字
-
将TCP头部的控制位SYN比特设置为1,ACK控制位未初始化,为0;设置序号字段的值(后面会有解释)
-
设置合适的序号和窗口大小
- 服务器返回
-
TCP模块将信息传递给IP模块并委托IP模块进行发送,服务器上的IP模块传递给服务器的TCP模块,服务器的TCP模块 通过TCP头部记录的端口号 找到对应的套接字。
-
修改找到的套接字状态为“正在连接”
-
返回响应:需要在返回的网络包的TCP头部将SYN设置为1(如果不同意连接则将RST设置为1),同时将ACK控制位设置为1
- 客户端接收
-
返回客户端后,若SYN位为1,则会向套接字中写入服务器的IP地址、端口号等信息
-
将状态改为“连接完毕”
-
客户端将ACK比特设置为1的一个网络包发回服务器,告知服务器刚才的响应已经收到
当服务器收到这个返回包后,连接操作完成
三、收发数据
1、将HTTP请求交给协议栈
数据的收发操作的开始:应用程序调用write

发送数据的特点:
-
协议栈不关心数据的内容
-
协议栈是先将数据存放在内部的发送缓冲区,并等待下一段数据,数据积累到一定量后再发送
判断是否执行发送的指标:
-
1.每个网络包能容纳的数据长度:MTU(网络包最大长度,包含头部),MSS(网络包最大数据长度,MTU-头部长度)

-
2.时间:协议栈内部有计时器,当经过一个最长时间的阈值后就发送一次
-
注:也可以由应用程序来控制发送时机
-
注2:像浏览器这样的会话型应用程序在向服务器发送数据时,等待缓冲区填满太消耗时间,因此一般选择直接发送
2、对较大的数据进行拆分
适用情况:发送缓冲区中数据长度超过MSS
实现:1. 发送缓冲区的数据会被以MSS进行拆分 2. 每一块前面加上TCP头部

3、使用ACK号确认网络包已收到
序号:TCP在拆分数据时,会事先计算好每一块数据相当于从头开始的第几个字节,写入序号
每次发送时会告知:
1.当前序号
2.接收方可以计算该包的数据长度(等价于告知)(=MTU-MSS)
有了以上两个数值,我们就知道发送的数据是从第几个字节开始,长度是多少

ACK号:确认响应。
接收方会将到目前为止 接收到的数据长度加起来,计算出收到了多少字节,写入ACK号返回给发送方,告诉其已经收到这些数据。
注:实际上,序号并不都是从1开始的
上面内容的解释:生成一个序号初始值,并告知通信对方约定的序号初始值。
通信过程(以下编号对应图中编号)
连接阶段:
-
客户端计算 客户端的数据的序号初始值,发给服务器
-
服务器返回ACK号响应;服务器返回 服务器端的数据的序号初始值,发给客户端
-
客户端收到服务器的序号初始值后,返回ACK号响应
数据收发阶段:
-
给出序号+数据
-
返回ACK号
…… …… ……

4、根据网络包往返时间调整ACK号超时时间
-
超时时间设定的必要性:
当网络繁忙时,发生堵塞,若很快判定为丢包而执行再次发送,则会出现ACK号姗姗来迟,而发出很多重复多余的包导致网络雪上加霜。
-
动态调整时间:根据ACK号返回时间判断超时时间。
- 如果返回时间慢,则响应延长超时时间;相对的,如果ACK号马上就能返回,则缩短超时时间
5、使用窗口有效管理ACK号
滑动窗口:在发送一个包后,不等待ACK返回,而是直接发送后续的一系列包。可以有效利用等待ACK号的时间

但如果不等待返回就发送ACK号就连续发送数据包,就有可能出现发送包的频率超过对方的处理能力的情况。
-
当接收方的TCP模块收到包后,会将数据存放到接收缓冲区。
-
接收方需要计算ACK号返回客户端,并将数据块组装起来还原成原本数据,传递给应用程序
注:如果这些操作还没完成就收到了下一个包,下一个包会被存在接收缓冲区中。
接收不过来的时候,势必导致缓冲区溢出
所以,改进如下:
接收方事先告知发送方自己最多能接收多少数据,然后发送方根据这个值对还剩的缓存区空间进行计算得知(总缓冲区容量-已经发送的信息量)

当有从缓冲区取出的操作时,会重新告知客户端还剩的缓冲区空间(更新窗口),则客户端更新缓冲区剩余容量的数据并给后续应用
6、ACK号和窗口号的同步发送
注:合并ACK号的发送和窗口更新的发送,从而减少包的数量
注2:连续发送多个ACK号时,只用发送最后一个即可,也可以减少包的数量
7、接收HTTP响应消息
-
应用程序调用read程序来获取响应消息,应用程序此时read操作会被挂起,直到FINISH包到达时应用程序才会真正开始读取数据
-
控制流程转移到协议栈。协议栈从接收缓冲区中取出数据,根据TCP头部判断是否有数据缺失,若没有则传递给应用程序。再返回ACK号
-
将控制流程交给应用程序
四、从服务器断开并删除套接字
1、数据发送完毕后断开连接
数据发送完毕的一方发起断开过程。(以下假设服务器一方发起断开过程)
-
服务器应用程序调用Socket库的close程序
-
服务器协议栈生成包含断开信息(将控制位中的FIN设为1)的TCP头部的包(暂命名为FINISH包)
-
当客户端收到FINISH包之后,会返回一个ACK号
-
应用程序开始通过read读取数据。
-
客户端程序调用close来结束数据收发操作。客户端也会生成一个FIN为1的TCP包,发给服务器,服务器返回ACK号表收到,通信结束。

2、删除套接字
套接字会等待一段时间再被删除,为了防止误操作。
3、数据收发操作小结

五、IP与以太网的包收发操作
1、包及其收发操作
包=头部+数据
头部:目的地址等控制信息

转发过程:
(1)路由器(按照IP协议)根据目标地址判断下一个路由器的位置
(2)集线器(按照以太网规则)在子网中将网络包传给下一个路由

TCP/IP包 包含:
-
MAC头部(用于以太网协议)
-
IP头部(用于IP协议)
IP头部:访问服务器的IP地址(不变),其他目的服务器的控制信息
MAC头部:IP协议查找出的下一个路由器的IP地址(每次到一个路由器,被改写为下一个路由的IP地址,遇到集线器不变),最近路由器的控制信息

以太网平替:无线局域网,ADSL,FTTH
包收发操作的过程:

2、生成IP头部
-
应用程序告诉TCP模块需要通信的服务器IP地址
-
TCP模块 告知IP模块 需要生成的IP头部 的IP地址部分的内容
IP头部格式

IP头部的“接收方IP地址”填写通信对象的IP地址
发送方IP地址需要判断发送所使用的网卡,并填写该网卡的IP地址
显示路由表的内容:指令route print

注:Gateway(网关):在TCP/IP世界里就是路由器的意思
注2:只需看IP地址网络号部分是否一样即确定转发的网卡
注3:第一行中目标地址和子网掩码都是0.0.0.0表示默认网关。如果与其他的条目都无法匹配,则会执行这一条
3. 生成以太网的MAC头部
-
由IP模块生成
-
包含:接收方和发送方的MAC地址

发送方MAC地址:网卡生产时写入ROM中的地址
(注:如果有多块网卡,则在设置发送方IP地址的时候,已经设置好了,只需要把对应IP地址网卡的MAC地址写进去即可)
接收方MAC地址:IP模块已经在路由表中查询到相匹配的条目,得到Gateway的IP地址,填入Gateway中的IP地址对应的MAC地址
3.5 通过ARP查询目标路由器的MAC地址
- ARP(地址解析协议)就是向所有设备,发出广播,给出Gateway的IP地址,请求其MAC地址。

-
注:路由表设置正确的话,该设备应该和Gateway在同一个子网中:
如果操作成功,即可得到MAC地址
操作失败,子网内设备无一ARP响应。认为对方不存在,包发送操作失败
-
ARP缓存
ARP查询结果的暂存位置
在发送包前,优先访问ARP缓存,如果其中已经保存了对方的MAC地址,就不需要查询了

- 注:为了防止IP改变导致ARP中存储信息发生改变的问题,ARP缓存中的值过一段时间会被删除,需要重新获取
4. 网卡:将IP包转换成电or光信号发送出去
背景:IP模块生成的网络包只是放在内存中的一串数字信息
网卡的内部结构

由此可见,网卡的MAC地址是保存在网卡ROM中的(生产时写入),在运行网卡驱动程序的时候,ROM中的MAC地址会被网卡驱动程序读取,分配给MAC模块
-
网卡的初始化操作
-
开机后网卡驱动程序对网卡的初始化操作
-
包含硬件错误检查,初始设置等步骤
-
网卡MAC模块加入的3个控制数据
网卡驱动从IP模块读取包,复制到网卡缓冲区,向MAC模块发送包的命令
MAC模块的工作
-
将包从缓冲区取出
-
在开头加上报头、起始帧分界符
-
在末尾加上帧校验序列:用于检测错误

报头(56bit):101010循环的序列

-
作用:确定时机,判断每个比特信号的中间位置

当信号连续为1,或者连续为0时,比特之间的界限就会消失,此时如果以时钟信号为参照,就可以分别0,1。但是这样并不方便传输(传输导致时钟偏移)
为了方便传输,需要将时钟信号叠加进去,可以判断出比特之间的界限:
-
数据信号为1,时钟为上升沿 叠加为下降沿
-
数据信号为0,时钟为下降沿 叠加为上升沿
接收方需要对信号进行观察,才能找到周期规律,此时报头的作用就是一段用来测量时钟信号的特殊信号
起始帧分界符(SFD)(8bit):0101011
- 作用:生成后,波形不同,对方网卡将此处作为起始位置
帧校验序列(FCS):检查包传输过程中因噪声导致的波形紊乱,数据错误
(当原始数据有一个发生变化的时候,计算结果就会变化,接受方就可以判断有无错误)
5. MAC模块和PHY(MAU)模块发送网络包
-
MAC 模块从报头开始将数字信息转换成电信号
- 数字信号转换为电信号的速率,就是网络的传输速率
-
PHY(MAU)模块负责发送
PHY(MAU)模块将MAC模块传来的数据转换成可在网线上传输的格式
- PHY(MAU)模块的功能:格式转换,发送数据,监控有无信号进入(在半双工模式下,不检测可能会发生信号碰撞)
PHY (物理层装置)和MAU(介质连接单元)仅仅是叫法不一样,其他一样
6. 接收返回包
半双工模式下,设备接收所有信号,然后执行相反的过程
-
PHY(MAU)模块将信号转换为通用格式,发给MAC模块
-
MAC 模块将电信号转换为数字信号,存放到缓冲区
-
MAC模块检查帧校验序列(FCS),发现有错误则丢弃
-
检查接收方MAC地址,是否和自己的一样,不一样则丢弃
-
通知计算机收到一个包:
网卡向扩展总线里的中断信号线发送信号
CPU暂时挂起处理任务,切换到操作系统中的中断处理程序
中断处理程序调用网卡驱动,接收网卡发来的数据
-
网卡驱动被调用:
网卡驱动程序会从网卡的缓冲区取出收到的包
通过MAC头部中的以太类型判断协议的类型:
-
0080h表示IP协议,驱动会把包交给TCP/IP协议栈
-
809Bh表示AppleTalk协议,驱动会把包交给AppleTalk协议栈
讨论最普遍的情况,识别到0080h,网卡将包按照以太类型交给协议栈
-
-
协议栈的IP模块:
功能1:IP检查
检查头部格式和IP地址。如果发现该包不是自己的,那么IP模块会通过ICMP消息将错误告知发送方(由下表可知,通过设置过Destination unreachable的值的包,发送回发送方即可)

功能2:分片重组
IP将分片之后(如果有分片)的包还原成原始的包:(分片重组)
- IP模块会将收到的包暂存在其内存空间中,然后等待IP头部具有相同ID的包全部到达
分片重组的依据:同一个包所有分片都具有相同的ID,且都具有分片偏移量字段,表示当前分片在整个包中的位置
功能3:把包交给TCP/IP模块
-
协议栈的TCP/IP模块:
功能:根据IP头部中接收方和发送方的IP地址,以及TCP头部中的接收方和发送方端口号来查找对应的套接字。决定需要的操作是什么
-
协议栈会判断这个包交给哪个应用程序
五、UDP协议的收发操作
回忆:向DNS 服务器查询IP地址的时候,用得到;音频数据等实时更新的数据(重发无意义),用得到;
UDP协议:数据短小,一般只用一个包,不用考虑哪个包未送达。只负责单纯发送包,不监控是否到达。出错就收不到对方回复,过一段时间重发即可
做法:用UDP代替TCP,去掉TCP头部,代以UDP头部,其他一样
