期末计算机网络知识复习总结
1. 计算机网络基本概念
Q:什么是计算机网络?
计算机网络是由多个计算机系统及其他硬件设备通过通信链路互联而成的系统,旨在共享资源、数据和信息。网络中的计算机和设备通过协议(如TCP/IP)进行通信,确保数据在不同设备之间可靠传输。计算机网络可以按规模、连接方式和功能进行分类,如局域网(LAN)、广域网(WAN)和互联网
基本组成部分
终端设备:计算机、服务器、智能手机、平板电脑等,用于发送和接收数据。
网络设备:路由器、交换机、集线器、网关等,用于数据传输和管理。
通信介质:有线(如以太网电缆、光纤)或无线(如Wi-Fi、蓝牙)介质,用于连接设备。
协议:一组规则和标准,如TCP/IP、HTTP、FTP,用于确保设备间通信的正确性和有效性。
网络的类型
局域网(LAN):覆盖小范围(如办公室、家庭)的网络,传输速率高,延迟低。常用的技术有以太网、Wi-Fi。
城域网(MAN):覆盖一个城市或多个建筑的网络,规模和传输距离介于LAN和WAN之间。
广域网(WAN):覆盖大范围(如国家、洲际)的网络,传输距离远,速率相对较低。互联网就是最大的WAN。
个人局域网(PAN):覆盖个人使用范围的网络,如蓝牙、红外连接
计算机网络的应用
- 互联网:全球最大的计算机网络,提供信息检索、电子邮件、电子商务、社交网络等服务。
- 企业网络:支持企业内部通信和资源共享,提高办公效率和业务协同。
- 校园网:提供学校内部教学、科研和管理的网络环境。
- 家庭网络:连接家庭设备,实现互联网接入、家庭娱乐和智能家居控制
Q:网络核心、网络边缘的概念?
- 网络核心(Network Core)是网络的中心部分,负责高速传输和交换数据包,确保数据能够在不同的网络边缘之间高效传递。核心网络通常由高性能路由器和交换机组成,提供主干传输和数据中继功能。
- 网络边缘(Network Edge)指的是网络中用户和设备直接接入的部分,包括终端设备、接入网络和边缘设备。它是网络中的第一跳,用户通过这里访问网络服务。
Q:网络边缘是如何接入网络核心的?=> 下面详细介绍了。
Q:现在使用的internet协议栈有几层?=> 5层
相关术语:
主机(host)(端系统,end system):连接到网络的设备
通信链路(communication link)
分组交换机(packet switch) 分类:路由器(router)、链路层交换机(link-layer-switch)
因特网服务提供商(Internet sevice provider, ISP)
TCP(transmission control protocol)
IP(internet protocol)
ISPs (Internet Service Providers)
因特网标准(internet standard):由因特网工程任务组(internet engineering task force, IETF)研发。其标准文档称为请求文档(request for comment, RFC)
协议(Protocol):定义了两个或多个通信实体之间交换的报文格式和次序,以及报文发送和/或接受一条报文或其 他事件所采用的动作。
网络接入(Access Networks):网络边缘接入网络核心
DSL(Digital Subscriber Line, 数字用户线)
HFC( hybrid fiber coax)
FTTH(Fiber To The Home,光纤到户)
拨号调制解调器:传输速率低(56 kbps),专用带宽。
HFC:传输速率高(10 Mbps 到 1 Gbps 下行),共享带宽。
DSL:传输速率中等(1.5 Mbps 到 100 Mbps 下行),专用带宽,受距离影响。
FTTH:传输速率非常高(100 Mbps 到 10 Gbps),大多数情况下为专用带宽或部分共享,但实际体验非常高。
以太网 最流行
WIFI
广域无线网 : 5G、LTE等等。
物理媒体:
- 双绞铜线(Twisted-Pair Copper Wire)
- 同轴电缆(Coaxial Cable)
- 光纤(Fiber Optics)
- 陆地无线通信道(Terrestrial Radio Channels)
- 卫星无线电信道(Satellite Radio Channels)
网络协议栈:
目前因特网的协议栈为5层,但是国际标准化化组织(ISO)提出的使七层,称之为开放系统互连模型 (Open Systems Interconnection model, OSI model)
从上到下分别是:
- 应用层 (Application) 服务:网络应用以及它们的应用协议存留的地方 协议例子:HTTP, SMTP, FTP, DNS, P2P, Telnet 分组: 报文(message)
- 运输层 (Transport) (向上层)提供的服务:应用程序之间传送应用层报文 协议(实现机制):TCP、UDP 分组:报文段 (segment)
- 网络层 (Network) (向上层)提供的服务:将分组从一台主机移动到另一台主机 协议(实现机制):IP 分组:数据报 (datagram)
- 链路层 (Link) (向上层)提供的服务:将分组从一个节点(主机或分组交换机)移动到下一个节点 协议(实现机制): 取决于该链路特定链路层协议 分组:帧(frame)
- 物理层 (physical) (向上层)提供的服务:将帧的一个一个比特从一个节点移动到下一个节点 协议(实现机制):与实际的传输媒体有关
2. 应用层
3. 传输层
3.1 传输层的职责概述及其为上层提供的服务
传输层负责为运行在不同主机上的应用进程提供逻辑通信,传输层的协议运行在端系统。大致的工作流程:
- 发送方:从应用层(主机上的应用进程)接收message,将其封装成segment ,利用网络层将数据发出。
- 接收方:从网络层接收数据,将segment重组成messaage,交给应用进程。
传输层是为不同主机的进程提供逻辑通信,是要依赖于下面的网络层(提供主机间的通信),由网络层找到目标主机,传输层找到目标端口,再找到对应的应用进程。传输层可以在网络层提供的服务之上进行增强,比如可靠性,顺序性,安全性等。
Internet传输层的协议包括TCP(可靠、保序)和UDP(不可靠、不保序)。
3.2 传输层的多路复用与解复用
Q:什么是传输层的多路复用与解复用?为什么需要?
一个传输层实体会从上层接收到来自多个应用进程的message , 这些message共享这一个传输层实体,在接收方也一样。所以需要在发送方和接收方将这些message区分开,使不同的message能正确交给对应的进程。
Q:传输层是怎么实现的?
通过在接收方将应用层传下来的message进行封装,加一些头部信息来标识,做到多路复用。在接收方根据不同的标识区分不同的报文,实现解复用。
Q:UDP和TCP在接收端解复用是不一样的,具体的区别是什么?为什么会有这样的区别?
具体区别在于UDP解复用的时候是根据报文段的目标主机号和目标端口号定位sockte,而TCP则需要源主机,目标主机,源端口和目标端口四个字段来定位socket。产生这样区别的原因在于UDP和TCP创建socket时的所需的字段不同,而究其根本是因为TCP是面向连接的,而UDP不是。UDP不需要维护源主机到目标主机的连接信息,相当于使用UDP是只传过去就完事了,所以UDP socket不需要保留源主机号这一字段。而TCP面向连接需要握手,重传等等,是需要多次用到源主机号的信息的。
概念
发送方会从多个套接字(socket)接收来自多个进程的报文,根据套接字对应的IP地址和端口号等信息对报文段用头部加以封装(该头部信息用于以后的解复用)。
接收方根据报文段的头部信息中的IP地址和端口号将接收到的报文段发给正确的套接字(和对应的应用进程)。
UDP的多路解复用
假设主机A和主机B进行通信,A是发送方,B是接收方:
- 双方创建在各自的Socket。
- A将message交给传输层,传输层将meaasge封装成segment,利用网络层发送给B主机。
- B收到UDP segment之后,检查报文段的目标端口号,用该端口号将报文段定位给套接字。
在接收端,UDP套接字用二元组标识(目标IP地址、目标端口号)。如果两个不同源IP地址/源端 口号的数据报,但是有相同的目标IP地址和端口号,则被定位到相同的套接字。
TCP的多路解复用
假设客户端C和服务器S:
- 服务器S建立起监听socket守候在80端口,等待客户端连接。C创建socket连接到服务器。
- C将message交给传输层,传输层将meaasge封装成segment,利用网络层发送给服务器S。
- 服务器收到之后,会根据TCP segment的源主机,目标主机,源端口和目标端口来定位socket。
服务器能够在一个TCP 端口上同时支持多个 TCP套接字:每个套接字由其四元组标识(有不同的源IP和源 PORT)
在这种场景下,虽然这些socket共享一个端口,但是内核中有各自的标识。
在多线程的服务器中,会将不同的socket交给同一个进程中的不同线程进行处理。而对于单线程的服务器,书里写的是一个socket对应一个进程,也就是守候在80号端口的进程创建多个进程,将不同的socket交给他们处理。但是这样效率很低啊,有些连接不活跃,也浪费了资源,后面查阅相关资料,并不会这么做,而是会有I/O调度模型,而是由一个进程来轮询这些连接,监听他们的状态并做出不同的处理。对于多线程的服务器也会使用线程池的机制来处理多个客户端连接。
3.3 无连接传输UDP
UDP提供的是不可靠,不保序的传输,并没有在网络层提供的best effort的服务之上做太多的增强。如果要在UDP之上做可靠性传输,则需要应用层自己增加可靠性。
那为什么要存在UDP呢?
- 不建立连接,传输速度快,实时性好(连接会增加延时 )
- 简单:在发送端和接收端没 有连接状态
- 报文段的头部很小(开销小)
UDP segment:
UDP报文段由20字节的固定头部 + 可变长度的数据报文组成。
校验和(check sum):检测在被传输报文段中的差错(如比特反转)
发送方: 将报文段的内容视为16 比特的整数,然后把这些16bits的整数相加(做1的补运算,进位回滚)得到校验和。再将校验和放在 UDP的校验和字段
接收方:收到数据之后进行一样的操作,进行16bits整数加法,再将计算值与检验和字段比较,不一致说明出错了,丢掉这个数据包,但是不会让发送方重传(TCP会有重传机制,UDP是没有的,丢了就丢了,它不保障)。
- 计算值与校验值不同:一定出错
- 计算值与校验值相同:也可能出错(残存错误)
了解:UDP的check sum如何生成 ?
UDP(User Datagram Protocol)的校验和字段用于确保数据在传输过程中没有发生错误。它覆盖UDP伪首部、UDP首部和数据部分。
UDP校验和计算步骤
- 构建伪首部: 伪首部不是真正的UDP报文的一部分,但在计算校验和时需要包含进来。伪首部包含以下字段:
- 源IP地址(32位)
- 目标IP地址(32位)
- 保留字段(8位,设为0)
- 协议号(8位,UDP为17)
- UDP长度字段(16位)
- 将伪首部、UDP首部和数据部分拼接: 把伪首部、UDP首部和数据部分拼接成一个连续的字节流。
- 将字节流划分为16位字(两个字节): 如果总长度不是偶数,填充一个字节的0。
- 逐个相加所有16位字: 使用二进制补码求和。如果有进位,则将进位加到结果的最低有效位。
- 对和取反: 将最终的和按位取反,得到校验和。
- 填入UDP校验和字段: 将计算出的校验和填入UDP报文的校验和字段。
示例
假设我们有以下数据:
- 源IP地址:192.168.1.1
- 目标IP地址:192.168.1.2
- UDP数据部分:”hello”
构建伪首部
- 源IP地址:192.168.1.1 -> 0xC0A80101
- 目标IP地址:192.168.1.2 -> 0xC0A80102
- 保留字段:0x00
- 协议号:17(UDP) -> 0x11
- UDP长度:8字节(UDP首部) + 5字节(数据) = 13字节 -> 0x000D
伪首部为:
1
0xC0A80101 0xC0A80102 0x0011 0x000D
UDP首部
假设:
- 源端口:12345 -> 0x3039
- 目标端口:80 -> 0x0050
- 长度:13字节(UDP首部 + 数据) -> 0x000D
- 校验和字段:暂时设为0 -> 0x0000
UDP首部为:
1
0x3039 0x0050 0x000D 0x0000
数据部分
“hello”的ASCII码:
1
0x6865 0x6C6C 0x6F00
(补0以凑成偶数字节)
拼接伪首部、UDP首部和数据部分
拼接后的字节流为:
1
2
30xC0A8 0x0101 0xC0A8 0x0102 0x0011 0x000D
0x3039 0x0050 0x000D 0x0000
0x6865 0x6C6C 0x6F00求和
逐个相加(16位字):
1
2
30xC0A8 + 0x0101 + 0xC0A8 + 0x0102 + 0x0011 + 0x000D +
0x3039 + 0x0050 + 0x000D + 0x0000 +
0x6865 + 0x6C6C + 0x6F00求和结果:
1
2
3
4Sum = 0xC0A8 + 0x0101 + 0xC0A8 + 0x0102 + 0x0011 + 0x000D +
0x3039 + 0x0050 + 0x000D + 0x0000 +
0x6865 + 0x6C6C + 0x6F00
= 0x25C02 (包含进位)处理进位:
1
0x25C02 -> 0x5C02 + 0x2 = 0x5C04
取反:
1
~0x5C04 = 0xA3FB
填入校验和字段
将0xA3FB填入UDP报文的校验和字段,这样UDP报文的校验和计算完成。
3.4 面向连接的传输 TCP
3.4.1. 前置知识:可靠数据传输(Reliable Data Transfer,简称RDT )原理
rdt 在应用层、传输层和数据链路层都很重要。是网络 Top 10 问题之一。
信道的不可靠特点决定了可靠数据传输协议( rdt )的复杂性。信道的不可靠特的不可靠性表现在:
- 信道存在比特差错
- 分组在信道中传输可能会丢失
针对比特差错,可以增加检错重传机制:(此时假设信道只存在比特差错)
可以用校验和来检测比特差错。但是当对方检测出报文出错时,会将错误报文丢弃,如何让发送方重传呢?
==> 通过反馈机制响应确认码:ACK
确认(ACK):接收方显式地告诉发送方分组已被正确接收
否定确认( NAK): 接收方显式地告诉发送方分组发生了差错。发送方收到NAK后,发送方重传分组
因此发送方还需要存在一个缓存,因为出错后要重传,将报文传出去后不能立马删除,而是要缓存起来。
机制大致如下:
- 发送方差错控制编码、缓存
- 接收方使用编码检错
- 接收方的反馈:控制报文(ACK,NAK):接收方 –>发送方
- 发送方收到反馈相应的动作
但是这样的机制还存在一个致命的问题,如果ACK / NCK反馈报文出错了怎么办?
==> 给发送的报文添加序号,响应报文ACK或者NCK要跟着上一次成功接收的报文序号。
比如:
发送方有pk0和pk1要发送,缓存后发送pk0 , 等待响应。
接收方成功接收pk0,检错通过,发送ACK0给对方。
ACK0如果出错了,接收方无法知道这是ACK0,变成了“阿巴阿巴”,接收方看不懂,就继续重传没有收到确认的分组pk0
接收方还是正常收到pk0,因为有加序号,它知道这是重复的分组(状态会指示希望接收到的分组的序号),于是将其丢弃,响应ACK0
这次发送方正常收到ACK0,于是开始发送ACK1…
通过引入这种机制解决响应码出错的问题。运行时如图所示:
对上诉机制的优化:使用对前一个数据单位的ACK,代替本数据单位的NAK
- 接收方对最后正确接收的分组发ACK,以替代NAK
- 接收方必须显式地包含被正确接收分组的序号
- 发送方收到重复的ACK(如:再次收到ack0)时, 与收到NAK采取相同的动作:重传当前分组
- 为后面的一次发送多个数据单位做一个准备:一次能够发送多个分组,每一个的应答都有:ACK,NACK;比较麻烦
- 这样可以使确认信息减少一半,协议处理简单
出错了。接收方是不需要理解ACK0或者ACK1的含义,只负责接收ACKn , 下次我就传ACK n+1 :
解决了比特差错的问题之后,再来看看如果信道还存在着丢失如何处理(此时信道具有比特差错和分组丢失)
下层信道可 能会丢失分组(数据 或ACK):发送方发出分组后在等ACK , 接收方在等分组,但这个分组已经丢了,这样就造成了死锁。
==> 解决方案:超时重传,发送方等待ACK一段合理的时间,如果到时没有收到ACK就认为分组丢失了,重传。
问题:如果分组(或ACK)只是被延迟了,发送方却提前重传了?
==> 重传将会导致数据重复,但利用序列号已经可以处理这个问题。因此接收方必须指明被正确接收的序列号(ACKn)。
过早超时也能正常工作,但是效率较低,一半 的分组和确认是重复的; 因此设置一个合理的超时时间也是比较重要的。
以上都是假设一次只能传送一个分组,这样传输模式信道的利用率很低。如何才能同时传多个分组呢?
==> 流水线协议:允许发送方在未得到对方确认的情况下一次发送多个分组
- 必须增加序号的范围:用多个bit表示分组的序号
- 在发送方/接收方要有缓冲区
- 发送方缓冲:未得到确认,可能需要重传;
- 接收方缓存:上层用户取用数据的速率 ≠ 接收到的数据速率;接收到的数据可能乱序,要进行排序交付(可靠)
- 两种通用的流水线协议:回退N步(GBN)和选择重传(SR)
发送方的缓冲区:
形式:内存中的一个区域,落入缓冲区的分组可以发送
功能:用于存放已发送,但是没有得到确认的分组
必要性:需要重发时可用
发送缓冲区的大小:一次最多可以发送多少个未经确认的分组(下图就是8个单位,编号 0 - 7 , 一次最多可以发送8个未确认的分组)
- 停止等待协议 =1 (stop and wait , 前面提到的)
- 流水线协议 >1,合理的值,不能很大,链路利用率不能够超100% (pipeline)
发送缓冲区中有两类分组:
- 未发送的分组
- 已发送但是未确认的分组,这些分组只有收到ACK之后才能从缓冲区中删除(体现为滑动窗口前沿的移动)
工作流程分析:分组落入缓冲区中,初始时发送窗口(发送缓冲区内容的一个范围,是那些已发送但是未经确认分组的序号构成的空间)为0,没发送一个分组,发送窗口的后沿就向后移动1,上图发送窗口移动到了4,说明此时发生出去5个未确认的分组。当收到分组的确认后,发送窗口前沿向前移动,上图中收到了0和1号分组的确认,发送窗口前沿向前移动到了2。
接收窗口
接收窗口(receiving window)=接收缓冲区
接收窗口用于控制哪些分组可以接收
- 只有收到的分组序号落入接收窗口内才允许接收
- 若序号在接收窗口之外,则丢弃;
接收窗口尺寸Wr=1(GBN),则只能顺序接收。
接收窗口尺寸Wr>1 (SR),则可以乱序接收,但提交给上层的分组,要按序。
例子:Wr=1,在0的位置;只有0号分组可以接收; 向前滑动一个,罩在1的位置,如果来了第2号分组,则丢弃;
接收窗口的滑动和发送确认:
滑动:
低序号的分组到来,接收窗口移动;
高序号分组乱序到,缓存但不交付(因为要实现rdt,不允许失序),不滑动
发送确认:
接收窗口尺寸=1:发送连续收到的最大的分组确认(累计确认)
接收窗口尺寸>1:收到分组,发送那个分组的确认(非累计确认)
想一下,这里wr > 1的时候,能不能只发送窗口的最低(最左边)序号分组的那个确认,发送完再让窗口前沿往前移动?
正常情况下发送窗口和接收窗口的交互:
SR为例:
1 | 时间轴 -> |
异常情况下GBN协议的发送窗口和接收窗口的交互:
异常情况下SR协议的发送窗口和接收窗口的交互:
GBN和SR的对比:
相同之处:
- 发送窗口>1
- 一次能够可发送多个 未经确认的分组
不同之处:
GBN:接收窗口尺寸=1
- 接收端:只能顺序接收
- 发送端:从表现来看,一旦一个分组没有发成功,如:0,1,2,3,4 ; 假如1未成功,234都发送出去 了,要返回1再发送;GB1
发送端拥有对最老的未确认分组的定时器。只需设置一个定时器,当定时器到时时,重传所有未确认分组
SR: 接收窗口尺寸>1
- 接收端:可以乱序接收
- 发送端:发送0,1,2,3,4,一旦1 未成功,2,3,4,已发送,无需重发,选择性发送1
发送方为每个未确认的分组保持一个定时器,当超时定时器到时,只是重发到时的未确认分组
3.4.2 TCP的具体实现
特点概述:
- 点对点: 一个发送方,一个接收方
- 可靠的、按顺序的字节流:没有报文边界
- 管道化(流水线): TCP根据调整窗口大小进行拥塞控制和流量控制
- 发送和接收缓存
- 全双工数据: 在同一连接中数据流双向流动;MSS:最大报文段大小
- 面向连接: 在数据交换之前,通过握手(交换控制报文)初始化发送方、接收方的状态变量
报文结构:
序号:报文段首字节的在字节流的编号
在TCP传输过程中,序号(Sequence Number)字段用于跟踪每个字节的数据顺序。当传输端(发送方)从上层接收到一个message并封装成TCP报文段时,它会使用一个随机生成的初始序号(Initial Sequence Number, ISN)。后续的TCP报文段的序号将基于这个初始序号递增。
建立连接时:在三次握手过程中,双方交换初始序号。例如,A发送的SYN报文段中包含初始序号(ISN_A),B响应的SYN-ACK报文段中包含B的初始序号(ISN_B)。
数据传输时:每个报文段的序号字段标记了该段中第一个数据字节的序号。例如,如果A的初始序号是1000,且A发送了100字节的数据,那么A发送的第一个报文段的序号字段将是1000,第二个报文段的序号字段将是1100(因为第一个报文段包含100个字节的数据)。
确认号:期望从另一方收到的下一个字节的序号;累积确认(发送方收到ACKn , 就会发出pktn)
乱序接收:没有规定接收方如何处理乱序的 报文段(但是一般都是先缓存而不会直接丢弃)
TCP 协议虽然没有强制规定接收方必须采取的具体处理方式,但在实际实现中,接收方一般会采用接收缓冲区来处理乱序的报文段。这种方式确保了可靠性和有效性。如果接收方简单地丢弃乱序的报文段,确实会导致传输效率低下甚至错误,因此大多数 TCP 实现不会这样做
设置合理的超时时间:
超时时间设置为一个RTT(加权过后)+ 4被方差。可以理解为比一个往返时长再多一点点
TCP的rdt实现:
TCP在IP不可靠服务的基础上 建立了rdt:
- 管道化的报文段( GBN or SR)
- 累积确认,响应期望收到的下一个分组的序号,也可以说是顺序收到的最后一个字节的数据编号 + 1(像GBN)
- 单个重传定时器(像GBN)
- 没有规范是否可以接受乱序
通过以下事件触发重传:
超时(只重发那个最早的未确认段:SR)
重复的确认(例子:收到了ACK50,之后又收到3 个ACK50)
快速重传超时周期往往太长: 在重传丢失报文段之前的延时太长,通过重复的ACK来检测 报文段丢失。发送方通常连续发送大量 报文段。如果报文段丢失,通常会引起多个重复的ACK
发送方事件:
从应用层接收数据 –> 将应用层数据分段,并为每个报文段分配序列号 –> 将报文段放入发送缓冲区 –> 通过网络发送
- 定时器与最早未确认的报文 段关联
- 收到对尚未确认的报文段确认,则更新已被确认的报文序号。如果当前还有未被确认的报文段,重新启动定时器
流量控制:
接收方控制发送方,不让发送方发送的太多、太快以至于让接收方的缓冲区溢出。
解决方法:
- 接收方在其向发送方的TCP段 头部的rwnd字段“通告”其空 闲buffer大小
- 大小通过socket选项 设置(典型默认大小为4096 字 节)
- 很多操作系统自动调整
- 发送方限制未确认(“in flight”)字节的个数 ≤ 接收方发送过来的值,保证接收方不会被淹没
连接管理:
在正式交换数据之前,发送方和接收方握手建立通 信关系。目的:
- 同意建立连接(每一方都知道对方愿意建立连接)
- 交换连接参数
使用两次握手可以吗?
会存在半连接以及接收旧数据的问题,
对于场景1:如果客户端第一次的连接请求超时了,它会重发一个连接请求与服务器建立起了连接。这时前面的那个连接到了服务器这里,服务器就与它建立起了连接,给它准备连接资源。但这只是个半连接,客户端不认,只有服务器在维持,白白浪费了资源。
对于场景2:客户端与服务器建立起连接之后,连接请求和传送的数据都超时了,而且都在连接断开之后到达了服务器,服务器还是会建立连接并且接收数据。
改进:3次握手来建立起连接
这样也能解决前面的失败场景:
当旧连接到达服务器的时候,服务器会再向客户端发送一个确认连接,客户端可以拒绝或者不理,这样连接过程就不完整(只有2次握手),无法建立起连接。对于旧数据也是如此,半连接建立不起来,更别说传数据了。
在连接结束之后需要关闭连接:4次挥手
客户端,服务器分别关闭它自己这一侧的连接。客户点等待一段时间之后发现没有数据过来了,就关闭。
可以联想,两个人分别的时候,A先说要回家了:
A -> B : 阿B,我要回家啦
B -> A :好
B也要说一下自己走了
B -> A :那我也回家啦
A -> B:ok~
然后A目送B离开,自己也转身迈步离开。