二、用电信号传输 TCP/IP 数据(1)

OS 中协议栈是如何处理数据发送请求的。

1、创建套接字

(1)、协议栈的内部结构

本章探索OS中的网络控制软件(协议栈)和网络硬件(网卡)是如何将qq浏览器的消息发送给服务器的。先了解协议栈的内部结构:

最上面是网络应用程序,如qq浏览器、电子邮件客户端、web服务器等程序,它们将收发数据等工作委派给下层部分来完成。

应用程序下是Socket库,其中包括解析器,向DNS server查询IP。

接着是操作系统内部(OS),其中包括协议栈。协议栈分上下部分:

  上半部分是用TCP协议收发数据的部分(浏览器、邮件等)和用UDP收发数据的部分(DNS查询等)。

  下半部分是用IP协议控制网络包收发操作的部分。互联网上传送数据时,数据会被切分成一个个的网络包,IP负责将网络包发给通信对象。IP中包括ICMP协议(告知网络包传送过程中产生的错误及各种控制信息)和ARP协议(根据IP地址查询相应的以太网MAC地址)。

网卡驱动程序负责控制网卡硬件。

网卡负责完成实际的收发操作,即对网线中的信号执行发送和接收的操作。

(2)、套接字的实体就是通信控制信息

在协议栈内部有一块用于存放控制信息的内存空间,这里记录了用于控制通信操作的控制信息,如通信对象的IP、端口、通信操作的进行状态等。套接字的实体可认为就是这些控制信息,或存放控制信息的内存空间。套接字中记录了用于控制通信操作的各种控制信息,而协议栈则需要根据这些信息判断下一步的行动,这就是套接字的作用。

总之,协议栈是根据套接字中记录的控制信息来工作的。

win10中netstat命令显示套接字内容(部分),如下所示,每一行相当于一个套接字

C:UsersAdministrator>netstat -ano  #a:不限于正在通信的套接字,n:显示IP和端口,o:显示使用该套接字的程序pid

活动连接

  协议  本地地址          外部地址        状态           PID
  TCP    0.0.0.0:135            0.0.0.0:0              LISTENING       956  #地址后是端口号
  TCP    1.1.3.164:52340        183.192.192.163:8080   ESTABLISHED     2720
  TCP    1.1.3.164:54217        111.13.105.58:443      CLOSE_WAIT      2068
  TCP    [::1]:8307             [::]:0                 LISTENING       3360
  UDP    0.0.0.0:51439          *:*                                    5028
  UDP    169.254.205.51:138     *:*                                    4

(3)、调用socket时的操作

应用程序调用socket申请创建套接字--->协议栈分配用于存放一个套接字所需的内存空间并写入初始状态的控制信息,创建完成--->协议栈将表示套接字的描述符告知应用程序。

2、连接服务器

(1)、认识 “连接”

创建套接字后,浏览器会调用connect,然后协议栈将本地套接字和服务器套接字进行连接。

而连接是指:通信双方交换控制信息,在套接字中记录这些必要信息并准备数据收发的一连串操作。

连接操作:

  • 应用程序将服务器IP和端口号等信息告知协议栈
  • 客户端向服务器传达开始通信的请求
  • 分配缓冲区,一块临时存放要收发的数据的内存空间

(2)、负责保存控制信息的头部

通信操作中使用的控制信息分为两类:

a、头部中记录的信息

b、套接字(协议栈中的内存空间)中记录的信息

(3)、连接操作的实际过程

浏览器调用connect,connect(< 描述符 >,< 服务器 IP 和 port >,...)---- 协议栈中tcp模块创建头部,如双方port等信息 ---- 头部中控制位的SYN比特设为1,表示连接 ---- tcp头部创建好后,tcp模块将信息传给ip模块,委托它发送

---- ip模块执行网络包发送,到达服务器 ---- 服务器的ip模块将收到的数据传给tcp模块 ---- tcp模块根据头部信息中的端口号找到对应的套接字 ---- 在套接字中写入相应信息,将状态改为正在连接 --- 此时服务器tcp模块将要返回响应,在tcp头部中设置双方端口号及SYN比特(设为1,若由于某些原因不接受连接则不设SYN,而是将RST比特设为1表示强制断开),ACK控制位设为1表示已经接收到相应的网络包(ACK用于相互确认网络包是否已经送达) ---- tcp模块将tcp头部传给ip模块,委托它向客户端返回响应

---- 网络包返回客户端,通过ip模块到达tcp模块,并通过tcp头部信息确认连接服务器操作是否成功 ---- 如果SYN为1表示连接成功了,这时向套接字中写入服务器的ip和port等信息,同时将状态改为连接完毕。 ---- 最后,将ACK比特设为1并发回服务器,告知服务器刚才的响应包已经收到 ---- 当服务器收到该返回包后,连接操作全部完成。

现在,套接字处于可随时收发数据的状态。建立连接(或会话)后,协议栈连接操作结束,控制流程被交回到浏览器。

3、收发数据

(1)将HTTP请求消息交给协议栈

浏览器调用write将要发生的数据交给操作系统的协议栈,协议栈收到数据后执行发送操作。

浏览器会指定发送数的长度(注:指数据总长度)----协议栈将数据存在内部的发送缓冲区,并等待下一段数据(注:python网络编程中:如果要求两次发送数据隔开,但实际上第一次没有填满,导致两次数据混合,就发生粘包)----那么,协议栈积累到什么程度才发送呢,要综合考虑两个因素:1、每个网络包能容纳的数据长度,MTU表示一个网络包的最大长度,MTU - 头部长度 = MSS 表示一个网络包中能容纳的最大数据长度;2、时间,当浏览器发送数据频率不高时,若每次都等到长度接近MSS再发送,可能因等待时间长而延迟,这种情况下,即使缓冲区的数据长度没达到MSS,也要发送。而协议栈内部有一个计时器(毫秒级),当经过一定时间后,就会发送网络包。

另外,开发者在一定程度上也可通过浏览器指定数据发送时机。

当在浏览器上提交表单数据如博客等,长度超过一个网络包长度即MSS,这时发送缓冲区的数据会以MSS进行拆分,放进不同网络包中,并在数据前加TCP头部(双方的port),然后交给ip模块添加ip头部和以太网的MAC头部后执行发送数据操作。

(2)使用ACK号确认网络包已收到

TCP能够确认对方是否成功收到网络包,若对方没收到则重发。

以下仅是单向数据传输:

但是,实际通信中,为防止被攻击,序号并不是从1开始,而是选择一个随机数为初始值。在连接阶段,客户端将SYN控制位设为1时还要设置序号字段初始值发送给服务器。

(3)根据网络包平均往返时间调整ACK号等待时间(又叫超时时间)

TCP在发送数据过程中持续测量ACK号的返回时间,慢则延长等待时间,快则缩短。达到动态调整等待时间的效果。

(4)使用窗口有效管理ACK号

之前模式是发送方发送数据后等到ACK号后再发送,这样等待的时间就白白浪费了,因此TCP采用滑动窗口方式来管理数据发送和ACK号的操作,也就是在发送一个包后,不等ACK号返回直接发送后续一些列包。这样会出现发送包的频率 > 接收方处理能力的情况。

接收方:TCP收到包后将数据存到接收缓冲区中,然后计算ACK号,组装还原数据并传给浏览器,如果这些操作还没完成下个包就到了怎么办?此时下个包也会被暂存在接收缓冲区中。但是这种情况持续发生,就会导致接收缓冲区数据溢出,接收方就收不到后面的包了。

解决办法:接收方将数据暂存在接收缓冲区并执行接收操作,接收操作完成后,接收缓冲区中的空间被释放,接收方通过TCP头部中的窗口字段将自己能接收的数据量告知发送方。

能够接收的最大数据量为窗口大小(与接收缓冲区大小一致)是TCP调优参数一种。

(5)ACK与窗口的合并

更新窗口大小的时机:接收方将数据传给浏览器导致接收缓冲区剩余容量增加时,告知发送方。

返回ACK号时机:接收方收到数据,确认内容没问题,就应返回ACK号。

两者是一起方式,但要等待合适时机,如剩余缓冲区大小持续增加,ACK号更新。

(6)接收HTTP响应消息

至此,浏览器委托协议栈发送HTTP请求消息的过程结束。

浏览器委托协议栈发送请求消息后,调用read获取响应消息,控制流程通过read转到协议栈,此时接收缓冲区无数据,协议栈将浏览器委托(从接收缓冲区取数据传给浏览器)挂起等响应消息到达再继续执行接收操作,响应到达后,协议栈检查收到的数据块和TCP头判断有无数据缺失,无则返回ACK号,将数据块暂存接收缓冲区,还原数据并复制到浏览器指定内存地址中,将控制流程交回浏览器。

渐变 --> 突变
原文地址:https://www.cnblogs.com/lybpy/p/8146531.html