《网络*》——读书笔记2

2.用电信号传输TCP/IP数据——协议栈、网卡

2.1 创建套接字

本章名词收录
  1. 套接字

  2. 控制信息、TCP头部

协议栈是什么

协议栈是操作系统的网络控制软件,网卡属于网络控制硬件。

所有TCP/IP软件采用分层结构:

  1. 应用程序:包括客户端、Web服务器、邮件客户端、邮件服务器等;Socket库和解析器(向DNS查询)

  2. 操作系统:协议栈包括两部分,上半部分包括TCP协议部分和UDP协议部分,下半部分是用IP协议控制网络包收发操作的部分(IP模块)。

    浏览器、邮件等一般应用程序收发数据时用 TCP;DNS 查询等收发较短的控制数据时用 UDP。

    数据在传送时会被切分成网络包,IP协议负责收发网络包,包括ICMP协议和ARP协议。ICMP负责告知网络包传达过程中产生的错误和各种控制信息;ARP用于根据IP地址查询相应的MAC地址。

  3. 网卡驱动程序和网卡硬件:物理结构完成信号的发送和接收操作。

套接字是什么

服务器在应用程序启动时就会创建好套接字等待连接;客户端则是在触发特定动作的时候才创建。

协议栈内部有一块内存空间专门用于存放控制信息,例如通信对象的IP、端口号、状态等。协议栈在执行操作时需要参考这些控制信息。例如:发送对象的IP地址、端口号;我发的数据是否得到了响应、我发的数据经过了多长时间等。协议栈是根据套接字中记录的控制信息来工作的。

协议栈如何调用socket、connect

浏览器委托协议栈使用TCP协议来收发数据。

首先是创建套接字的阶段,应用程序调用socket申请创建套接字,协议栈分配一个内存空间,然后向其中写入初始状态,到此完成套接字的创建工作。然后需要将套接字的“描述符”(内存的地址)告知应用程序。应用程序收到“描述符”之后可以访问准确的套接字,并从中获取通信双方的信息。

2.2 连接服务器

连接是什么意思

套接字创建好之后,应用程序会调用connect,协议栈会将本地的套接字与服务器的套接字进行连接。本质上是客户端和服务器双方做好传递数据之前的准备工作。其要点包括:

客户端这边需要把服务器的IP信息、端口号和其它控制信息告知协议栈;

服务器这边创建完套接字之后是等待客户端发送必要的信息过来,如IP、端口号等。

在执行数据收发操作时,双方会创建一块“缓冲区”,用来临时存放要收发的数据。

做完这些“准备”,才可以进入后面的动作

控制信息(负责保存控制信息的网络包叫“头部”)

第一类控制信息:客户端与服务器之间交换的控制信息

在TCP协议中进行了定义,规定了字段。这些信息会被添加在网络包的开头,由于只有控制信息,所以被称为“头部”。为了区分以太网、IP协议的控制信息,一般会称为“TCP头部”、“以太网头部(MAC头部)”、“IP头部”。

第二类控制信息:保存在套接字中用来控制自身协议栈操作的信息。

应用程序传递来的信息和从通信对象接收到的信息都会保存在这里。收发数据操作的执行状态信息也会保存在这里。

TCP 传输数据之前,要先三次握手建立连接

调用Socket库中的connect,输入参数“描述符”,“服务器IP地址和端口号”等。“服务器IP地址和端口号”会被传递给协议栈中的TCP模块。连接操作的第一步目的是在 TCP 模块处创建表示连接控制信息的头部。

TCP模块会与IP地址对应的对象的TCP模块交换控制信息(TCP头部)。

一开始,客户端和服务器都处于“CLOSED”状态。

第一次握手

先是服务器进入“LISTEN”监听状态,然后客户端发来一个包含IP地址和双方端口号的TCP头部,其中会把控制位的SYN置1(表示连接),进入“SYN-SENT”状态。

第二次握手

服务器根据TCP头部中记录的端口号,顺利找到端口号对应的套接字,并在套接字中写入相应信息,更新状态为“正在连接”,然后把头部中的控制位的SYN比特设置为1,ACK控制位置1(表示收到包),进入“SYN-RCVD”状态,向客户端发包;客户端收到带有SYN为1的头部之后,向套接字中写入相应信息(服务器的IP地址、端口号等),更新状态为“连接完毕”,ACK比特设置为1,向服务器发包,进入“ESTABLISHED”状态。

第三次握手

服务器收到带有ACK为1的头部之后,进入“ESTABLISHED”状态。

  1. 三次握手目的是保证双方都有发送和接收的能力

  2. 在调用close断开之前,连接一直存在

  3. connect到此就结束了

2.3 收发数据

待发送消息到达协议栈

connect之后,数据收发操作从调用write将信息交给协议栈开始。

协议栈会把数据存放在发送缓冲区中,等待下一段数据。至于要积累多少数据才会发送,和不同的种类和版本有关。但都是根据以下几个要求来进行判断:

  1. MTU:每个网络包能容纳的数据长度,在以太网中一般是1500字节。

  2. MSS:从MTU中减去IP和TCP头部就得到一个网络包能容纳的最大数据长度MSS。

  3. 时间。协议栈内部有一个计时器,积累一定时间之后,尽管数据长度没有达到MSS,也会发送数据包

实际情况下这两个因素是矛盾的,不同的版本操作也会不同。例如浏览器一般不等待缓冲区堆满,会选择直接发送。

这里面需要注意的是,MTU并不等于网络包的数据长度,网络包还包括报头、MAC头部、FCS信息

MSS会包括头部的信息,头部一般是40个字节。有时候头部会增加加密信息,因此MSS会更短。

拆分较大的数据

当缓冲区中的数据长度大于MSS时,数据就会被TCP模块以MSS的长度为单位进行拆分。拆分完之后加上TCP头部再交给IP模块。

使用ACK号确认收到网络包

TCP模块在拆分数据时会先算好数据是从第几个字节开始的,并把这个信息写在TCP头部中,用数据包长度减去这个数值就能得到这次发的数据的长度。

在实际通信中,发送方会告知本次发送的是从第几字节开始,总长度有多少字节;接收方会回复接受到了第几字节的数据。而每次最开始的序号都是随机数随着第一次握手SYN置1时告知对方;接收方除了返回ACK比特置1(表示收到)之外,还要把ACK号设置成目前收到的是第几个字节。

因为在客户端和服务器之间的连接阶段中,TCP数据收发是双向的,因此双方都需要在SYN中用随机数得到一个开始的序号并把序号初始值告知对方。而初始值可能丢失,因此需要ACK比特置1来进行确认。当初始序号和ACK号都准备完成就可以进入数据收发模式了。

TCP会把发送过的包保存在发送缓冲区,当对方没有返回某些包对应的ACK号,就重新发送。

【错误检测和补偿机制】网卡、集线器、路由器都没有错误补偿机制,一旦检测到错误就直接丢弃相应的包。

TCP重传几次无效之后会强制结束通信并报错。

ACK号等待时间(超时时间)

当网络拥塞时,ACK号的返回会变慢,此时就必须把等待时间(超时时间)延长,否则会出现多余的重传,使得网络更加堵塞。因此,TCP采用动态调整等待时间(超时时间)的方法。怎么个动态法呢,TCP会持续测量每次ACK号的返回时间,如果返回时间变慢就会适当延长动态调整等待时间(超时时间),反之亦然。

由于计算机的时间测量精度较低,ACK 返回时间过短时无法被正确测量,因此等待时间有一个最小值,这个值在每个操作系统上不一样,基本上是在 0.5 秒到 1 秒之间。

通过滑动窗口管理ACK号

发送方不等待对端发回来ACK号,直接继续发送下一个包,避免浪费等待ACK号的时间。但有可能出现接收方处理不过来的情况。接收方收到数据包会先存在接收缓冲区中,因此接收方需要告诉发送方缓冲区的大小。这是所谓滑动窗口。

具体说来,接收方会在TCP头部中的窗口字段告知发送方自己能接收的数据量(窗口大小)。

ACK号和窗口的合并

ACK号发送的时机与窗口大小的更新时机不同。

更新窗口大小的动作应该发生在接收方从缓冲区中取出数据时进行,当接收方将数据传递给应用程序后,接收缓冲区余量增加,此时接收方就要通过发包告知发送方,更新窗口大小。

ACK号的发送应该发生在接收方收到数据之后马上进行。(发包

提升网络效率:

  1. 接收方可以把ACK号和窗口大小放在一个包里面发送;

  2. 连续发送多个ACK号时,只需要发送最后一个ACK号即可。因为ACK号表示的是已收到的数据量,也就是目前收到的数据最后位置在哪里;

  3. 连续发送多个窗口更新时,说明应用程序连续请求了数据,这时候接收缓冲区的余量连续增加,只要发送最后一个结果即可。

接收HTTP响应消息

浏览器在委托协议栈发出请求消息之后,会调用read来获取响应消息,控制流程会因为read转移到协议栈,应用程序进入暂停状态。协议栈尝试从接收缓冲区取出数据传递给应用程序,如果响应消息还未收到,协议栈会将该“委托”暂时挂起,等服务器返回的响应消息到达后再继续执行。

协议栈先检查收到的数据块和TCP头部,没有问题返回ACK号;协议栈在接收缓冲区把数据块按顺序连接起来还原原始数据,再将数据交给应用程序。具体来说,协议栈会将数据复制到应用程序指定的内存地址中,再将控制流程交回应用程序。找到合适的时机向发送方发送窗口更新

2.4 从服务器断开并删除套接字

数据发送完毕后断开连接

数据发送完毕的一方发起断开过程,不同的应用程序会选择不同断开时机,例如Web会由服务器发起断开过程,但是在HTTP 1.1中是由客户端发起断开过程。

以服务器发起断开过程为例:应用程序调用Socket库中的close程序,协议栈生成包含断开信息(控制位的FIN比特置1)的TCP头部,委托IP模块发送给客户端,同时套接字记录下断开操作的相关信息。

随后客户端收到FIN为1的TCP头部时,客户端的协议栈会将自己的套接字标记为进入断开操作状态,并向服务器返回一个ACK包。随后客户端的协议栈等待应用程序来读取数据(如果在收到FIN为1的包之前应用程序来读取则会被暂时挂起),告知应用程序数据已经全部收到。然后应用程序会调用close来结束数据收发操作,协议栈生成包含断开信息(控制位的FIN比特置1)的TCP头部,委托IP模块发送给服务器。

服务器收到之后返回一个ACK号,至此通信结束。

删除套接字

客户端的套接字会等待一段时间再被删除,防止误操作。列举两个误操作的情形:

  1. 等待重传包传输完成;

  2. 若客户端返回ACK号丢失,服务器没有收到则会重新发送一个FIN。此时如果原来套接字被删除,对应的端口号被释放后被别的应用程序所占用,就会把FIN误发送给新的应用程序的套接字。

数据收发小结

用TCP协议收发数据的操作分几步:

第一步:套接字创建阶段。服务器的应用程序在应用启动时创建好套接字等待连接,客户端则需要特定动作触发。

第二步:套接字连接阶段。三次握手:①客户端向服务器发起连接操作,发送SYN置1的TCP包,其中包含IP地址和双方端口号、客户端发送数据的初始序号、服务器发送数据的窗口大小;②服务器收到①发来的包,返回一个SYN置1、ACK置1的TCP包,其中包含序号和窗口大小,以及代表收到包的ACK号;③客户端收到②发来的包,返回一个ACK置1,包含确认ACK号的TCP包。完成连接操作。

第三步:数据收发阶段。双向进行,过程相反。以客户端为例,客户端把(请求)消息切分成一定大小的块,并在每一块前面加上TCP头部,TCP头部包含序号;服务器收到数据时会向客户端发送相应的ACK号;在刚开始进行数据收发时,服务器只是不停收数据,随后数据被传递给应用程序,缓冲区的余量逐步被释放,服务器向客户端发送新的窗口大小【可以把ACK号和窗口大小放在一个包里面发送】;服务器接收完成上述请求消息后,会向客户端发送响应消息。

第四步:断开连接阶段。数据先发送完毕的一方发起断开过程,以服务器为例,发送FIN置1的TCP包;客户端收到会在套接字中标记断开连接状态,然后先发送一个FIN置1的包,然后返回一个ACK包;服务器返回一个ACK包。至此通信结束。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值