当前位置: 首页 > news >正文

POP3协议详解

基本介绍

POP3是一种用于从邮件服务器获取电子邮件的协议。它允许邮件客户端连接到邮件服务器,检索服务器上存储的邮件,并将邮件下载到客户端设备上。POP3的工作原理如下:

  1. 连接和身份验证: 邮件客户端通过TCP/IP连接到邮件服务器的POP3端口(通常是110)。客户端首先发送USER​命令提供用户名,然后发送PASS​命令提供密码以进行身份验证。
  2. 查看邮件状态: 客户端可以使用STAT​命令来查看邮箱中的邮件状态,包括邮件数量和总字节数。
  3. 查看邮件列表: 使用LIST​命令,客户端可以查看每封邮件的索引号和大小。如果提供邮件索引号,服务器将返回特定邮件的大小。
  4. 检索邮件: 使用RETR​命令,客户端可以检索特定邮件的内容,包括邮件头和正文。邮件被下载到客户端设备上。
  5. 删除邮件: 使用DELE​命令,客户端可以标记要删除的邮件。邮件在标记为删除后不会立即从服务器上删除,直到客户端执行QUIT​命令。
  6. 退出: 使用QUIT​命令,客户端结束POP3会话并断开连接。在断开连接之前,标记为删除的邮件将从服务器上删除。

相关概念

  • 邮件收件箱(Mailbox): 邮件服务器上存储用户接收到的电子邮件的地方。每个用户都有一个邮箱,其中包含他们的电子邮件。
  • 命令和响应: POP3协议使用一系列命令和响应进行通信。客户端发送命令(例如,USER​​,PASS​​,LIST​​等),服务器返回响应(例如,+OK​​表示成功,-ERR​​表示错误)。
  • 邮件索引号(Message Number): 每封邮件在邮箱中都有一个唯一的索引号。客户端可以使用索引号来指定要执行的操作,如检索或删除邮件。
  • 邮件大小(Message Size): 邮件的大小以字节为单位。LIST​​命令返回每封邮件的大小,以便客户端可以了解邮件的大小信息。
  • 标记为删除: 客户端可以使用DELE​​命令将邮件标记为删除。标记为删除的邮件在QUIT​​命令之前不会从服务器上真正删除。
  • 邮件头和正文: 邮件通常由邮件头和正文组成。邮件头包含元数据,如发件人、主题和日期。邮件正文包含邮件的实际内容。
  • MIME(Multipurpose Internet Mail Extensions): MIME是一种电子邮件标准,用于描述邮件内容的类型和格式,包括文本、HTML、图像和附件等。
  • 连接池(Connection Pool): 一些POP3客户端使用连接池来管理与邮件服务器的连接,以提高性能和效率。

POP3命令与响应

Transaction State

一旦客户机成功地向 POP3服务器标识了自己并且 POP3服务器已经锁定并打开了相应的邮件包, POP3会话现在处于 TRANSACTION 状态. 重复发出以下任何 POP3命令命令,POP3服务器发出响应 发出 QUIT 命令,POP3会话进入 UPDATE 状态。

STAT命令

  • STAT​:查询邮箱中邮件的统计信息,包括邮件数量和总字节数。

命令格式:

STAT

服务器应该响应一个包含两个值的响应:邮件数量和邮箱中所有邮件的总字节数。响应的格式如下:

+OK <邮件数量> <总字节数>

LIST命令

获取邮件服务器返回每封邮件的详细信息,包括邮件的索引号和大小。

命令格式:

LIST [<message_number>]
  • <message_number>​ 是可选的参数,用于指定要列出的特定邮件的索引号。如果省略了此参数,则会列出所有邮件的信息。

服务器会以以下格式响应LIST​命令:

如果未指定<message_number>​(列出所有邮件):

+OK <邮件总数> messages (<邮件总大小> octets)
<邮件1的索引号> <邮件1的大小>
<邮件2的索引号> <邮件2的大小>
...
<邮件n的索引号> <邮件n的大小>
.
  • +OK​ 表示命令执行成功。
  • <邮件总数>​ 是邮箱中邮件的数量。
  • <邮件总大小>​ 是所有邮件的总大小(以字节为单位)。
  • 每行中的 <邮件i的索引号>​ 表示第i封邮件的索引号。
  • 每行中的 <邮件i的大小>​ 表示第i封邮件的大小(以字节为单位)。

如果指定了<message_number>​(列出特定邮件):

+OK <邮件索引号> <邮件大小>
  • +OK​ 表示命令执行成功。
  • <邮件索引号>​ 是指定邮件的索引号。
  • <邮件大小>​ 是指定邮件的大小(以字节为单位)。

RETR

检索特定邮件的内容,其中邮件的索引号由客户端指定。RETR命令可用于下载邮件的正文和标头。

命令格式:

RETR <message_number>

服务器会以以下格式响应RETR​命令:

如果命令执行成功:

+OK
<邮件内容>
.
  • +OK​ 表示命令执行成功。
  • <邮件内容>​ 包含了邮件的完整内容,包括邮件头和正文。通常,邮件内容以文本格式返回。
  • 最后一行的.​(句号)表示邮件内容的结束。

如果命令执行失败(邮件不存在或索引号无效):

-ERR <错误消息>
  • -ERR​ 表示命令执行失败。
  • <错误消息>​ 包含有关失败原因的描述。

DELE

标记特定邮件以删除它们,但不会立即从服务器上删除它们。删除操作只有在客户端执行QUIT​命令时才会生效。

命令格式:

DELE <message_number>

**服务器响应: **

如果命令执行成功(邮件被成功标记为删除):

+OK Message <message_number> deleted
  • +OK​ 表示命令执行成功。
  • <message_number>​ 是已标记为删除的邮件的索引号。

如果命令执行失败(邮件不存在或索引号无效):

-ERR <错误消息>
  • -ERR​ 表示命令执行失败。

    • <错误消息>​ 包含有关失败原因的描述。

NOOP

它不执行任何实际的操作。它的主要目的是告诉邮件服务器,客户端仍然活跃,正在使用连接,以防止连接因长时间不活动而被服务器关闭。

REST

重置或取消标记为删除的邮件,以便这些邮件不会被删除。
RSET命令通常用于撤销之前使用DELE命令标记为删除的邮件。

命令格式:

RSET

命令响应:

如果命令执行成功:

+OK
  • +OK​ 表示命令执行成功。

如果命令执行失败(通常是因为RSET​命令不支持或出现其他问题):

-ERR <错误消息>
  • -ERR​ 表示命令执行失败。
  • <错误消息>​ 包含有关失败原因的描述。

UPDATE State

QUIT

告知邮件服务器客户端已经完成了邮件收取或管理任务,并请求结束与服务器的连接。

Optional命令

TOP

检索邮件的头部信息和指定行数的邮件正文。
TOP​命令允许客户端请求邮件服务器返回特定邮件的头部信息和部分邮件正文,以减少数据传输量和加快邮件检索速度。

命令格式:

TOP <message_number> <number_of_lines>
  • <message_number>​ 是要检索的邮件的索引号。客户端必须提供一个有效的索引号,以指示要检索的邮件。
  • <number_of_lines>​ 是要返回的邮件正文的行数。客户端可以指定要检索多少行邮件正文,通常用于查看邮件的摘要信息。

响应:

+OK
<邮件头部信息>
<邮件正文的前n行>
.
  • +OK​ 表示命令执行成功。
  • <邮件头部信息>​ 包含了邮件的头部信息,如发件人、主题、日期等。
  • <邮件正文的前n行>​ 包含了指定行数的邮件正文。
  • 最后一行的.​(句号)表示邮件内容的结束。

UIDL

检索每封邮件的唯一标识符(UID)。每封邮件都分配了一个唯一的UID,通常用于客户端在与服务器交互时标识邮件,而不仅仅依赖于邮件的序号。

命令格式:

UIDL [message_number]

响应

+OK
<message_number> <UID>
  • +OK​ 表示命令执行成功。
  • <message_number>​ 是邮件的索引号。
  • <UID>​ 是邮件的唯一标识符(UID)。

USER

USER​命令是POP3(Post Office Protocol Version 3)协议中的一条命令,用于向邮件服务器提供登录用户名。USER​命令通常是POP3会话的第一步,用于标识客户端要使用的邮箱账户。

PASS

PASS​命令是POP3(Post Office Protocol Version 3)协议中的一条命令,用于向邮件服务器提供登录密码以完成身份验证。一般情况下,在使用USER​命令提供用户名后,客户端必须使用PASS​命令提供相应的密码,以便访问邮箱账户。

APOP

APOP​(Authenticated Post Office Protocol)是POP3(Post Office Protocol Version 3)协议的一种认证机制。它是一种用于安全地认证邮件客户端与邮件服务器之间的身份的方法。与普通的POP3用户名和密码认证不同,APOP​使用一种基于哈希函数的挑战-响应机制来认证客户端的身份。

以下是APOP​认证的工作原理:

  1. 邮件服务器生成一个随机的挑战字符串,通常是一个长字符串,称为挑战值(challenge)。
  2. 邮件服务器将挑战值与密码的哈希值进行拼接,并计算拼接后的字符串的MD5哈希值。这个MD5哈希值称为响应值(response)。
  3. 邮件服务器将挑战值发送给客户端。
  4. 客户端收到挑战值后,将自己的密码和挑战值拼接,并计算拼接后的字符串的MD5哈希值,得到自己的响应值。
  5. 客户端将自己的用户名和响应值发送给邮件服务器。
  6. 邮件服务器收到客户端的响应后,会检查客户端的用户名是否存在,并将客户端的响应值与邮件服务器计算的响应值进行比较。
  7. 如果客户端的响应值与邮件服务器的响应值匹配,服务器将认为客户端通过了认证,允许客户端访问邮箱。

APOP​认证的优点是它通过使用挑战-响应机制,使得在网络上传输的密码不会以明文形式传输,从而提高了安全性。这种认证机制确保只有知道正确密码的客户端才能通过认证。

请注意,APOP​认证是一种POP3协议的扩展认证机制,因此并不是所有邮件服务器和邮件客户端都支持它。在使用APOP​认证之前,确保您的邮件服务器和客户端都支持此认证方式。

Go语言解析POP3

解析头部

  1. 通过mail.ReadMessage来解析邮件内容

    message, err := mail.ReadMessage(strings.NewReader(source))
    if err != nil {log.Error(err)return
    }
    
  2. 通过解析出来message内部的Header结构可以获取指定key的信息
    常见key有: From​、To​、Subject​、Date​、Content-Type​、Content-Transfer-Encoding

    from := message.Header.Get("From")
    to := message.Header.Get("To")
    subject := message.Header.Get("Subject")
    date := message.Header.Get("Date")
    message.Header.Get("Content-Type")
    message.Header.Get("Content-Transfer-Encoding")
    
  3. Header实际是个map, 所以可以通过for循环的方式遍历

    for k, v := range message.Header {blog.Info("key: ", k, "  value:", v)
    }
    

解析body

通常body会带有多行, 并且最后带有.结束符, 所以我们需要将这些过滤掉

	str := strings.ReplaceAll(string(body), "\r\n", "")str = strings.ReplaceAll(str, ".", "")

具体需要使用解析算法需要注根据Content-Transfer-Encoding来判断,比较常用的两种邮件编码方式为BASE64和Quoted-printable。后来的扩展 SMTP协议允许直接在邮件中传递二进制数据,而不用对它们进行邮件编码,人们将这种没有进行邮件编码的二进制数据的邮件内容称为8bit编码,为了与此 相区别,人们将没有进行邮件编码的纯ASCII码字符的邮件称为7bit编码。MIME消息体的邮件编码方式通过MIME消息头中的Content- Transfer- Encoding头字段指定,每种邮件编码方式的介绍如下:

  • 7Bit: 指消息体内容全部是没有经过编码的ASCII字符。
  • 8Bit: 指消息体内容是没有经过编码的原始数据,且其中包含有非ASCII字符的数据。现在的邮件服务器基本上都支持8Bit编码,使用支持8Bit编码的邮件服务器可以简化邮件的处理过程。
  • BASE64: Base64是将二进制数据转换成可打印的ASCII字符的一种最常见的编码方式,它的基本原理是将一组连续的字节数据按6个bit位进行分组,然后对每组数据用一个ASCII字符来表示。
  • Quoted-printable: 一种将二进制数据转换成可打印的ASCII字符的编码方式,它对ASCII字符不进行转换,只对非ASCII字符的数据进行编码转 化。

指消息体内容全部是没有经过编码的ASCII字符。

使用base64解析示例:

	decodedBody, err := base64.StdEncoding.DecodeString(str)if err != nil {log.Fatal("Error decoding Base64:", err)}log.Info(string(decodedBody))

参考资料

inbucket smtp,pop3,restful服务

pop3协议分析

docker使用Inbucket搭建邮件服务器用于接受邮件

rfc1939

Multipurpose Internet Mail Extensions(RFC2045)

相关文章:

POP3协议详解

基本介绍 POP3是一种用于从邮件服务器获取电子邮件的协议。它允许邮件客户端连接到邮件服务器&#xff0c;检索服务器上存储的邮件&#xff0c;并将邮件下载到客户端设备上。POP3的工作原理如下&#xff1a; 连接和身份验证&#xff1a; 邮件客户端通过TCP/IP连接到邮件服务器…...

电子病历编辑器源码,提供电子病历在线制作、管理和使用的一体化电子病历解决方案

概述&#xff1a; 电子病历是指医务人员在医疗活动过程中,使用医疗机构信息系统生成的文字、符号、图表、图形、数据、影像等数字化信息,并能实现存储、管理、传输和重现的医疗记录,是病历的一种记录形式。 医院通过电子病历以电子化方式记录患者就诊的信息&#xff0c;包括&…...

WT2605C高品质音频蓝牙语音芯片:外接功放实现双声道DAC输出的优势

在音频处理领域&#xff0c;双声道DAC输出能够提供更为清晰、逼真的音效&#xff0c;增强用户的听觉体验。针对这一需求&#xff0c;唯创知音的WT2605C高品质音频蓝牙语音芯片&#xff0c;通过外接功放实现双声道DAC输出&#xff0c;展现出独特的应用优势。 一、高品质音频处理…...

IntelliJ IDEA 2023.3 最新版如何如何配置?IntelliJ IDEA 2023.3 最新版试用方法

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…...

如何查看内存卡使用记录-查看的设备有:U盘、移动硬盘、MP3、SD卡等-供大家学习研究参考

主要功能 USB Viewer&#xff08;USB移动存储设备使用记录查看器&#xff09;可用于查看本机的USB移动存储设备使用记录。可查看的设备有&#xff1a;U盘、移动硬盘、MP3、SD卡……等。   可用于兵器、航空、航天、政府、军队等对保密要求较高的单位&#xff0c;可在计算机保…...

九、W5100S/W5500+RP2040之MicroPython开发<HTTPOneNET示例>

文章目录 1. 前言2. 平台操作流程2.1 创建设备2.2 创建数据流模板 3. WIZnet以太网芯片4. 示例讲解以及使用4.1 程序流程图4.2 测试准备4.3 连接方式4.4 相关代码4.5 烧录验证 5. 注意事项6. 相关链接 1. 前言 在这个智能硬件和物联网时代&#xff0c;MicroPython和树莓派PICO正…...

在 Laravel 中,清空缓存大全

在 Laravel 中&#xff0c;清空缓存通常涉及到清除应用程序中的缓存文件和数据库查询缓存。以下是一些常用的清空缓存方法&#xff1a; 1. 清除路由缓存&#xff1a; Laravel 的路由缓存可以提高应用程序的性能&#xff0c;但在开发过程中&#xff0c;你可能需要频繁地更改路…...

【贪心】单源最短路径Python实现

文章目录 [toc]问题描述Dijkstra算法Dijkstra算法的正确性贪心选择性质最优子结构性质 Dijkstra算法应用示例时间复杂性Python实现 个人主页&#xff1a;丷从心 系列专栏&#xff1a;贪心算法 问题描述 给定一个带权有向图 G ( V , E ) G (V , E) G(V,E)&#xff0c;其中每…...

Spark Shell的简单使用

简介 Spark shell是一个特别适合快速开发Spark原型程序的工具&#xff0c;可以帮助我们熟悉Scala语言。即使你对Scala不熟悉&#xff0c;仍然可以使用这个工具。Spark shell使得用户可以和Spark集群交互&#xff0c;提交查询&#xff0c;这便于调试&#xff0c;也便于初学者使用…...

Springsecurty【2】认证连接MySQL

1.前期准备 基于Spring Initializr创建SpringBoot项目&#xff08;基于SpringBoot 2.7.12版本&#xff09;&#xff0c;实现与MyBatisPlus的项目整合。分别导入&#xff1a;CodeGenerator和MyBatisPlusConfig。 CodeGenerator&#xff1a;用于MybatisPlus代码生成&#xff1b;…...

.Net 访问电子邮箱-LumiSoft.Net,好用

序言&#xff1a; 网上找了很多关于.Net如何访问电子邮箱的方法&#xff0c;但是大多数都达不到想要的需求&#xff0c;只有一些 收发邮件。因此 花了很大功夫去看 LumiSoft.Net.dll 的源码&#xff0c;总算做出自己想要的结果了&#xff0c;果然学习诗人进步。 介绍&#xff…...

谷粒商城-商品服务-新增商品功能开发(商品图片无法展示问题没有解决)

在网关配置路由 - id: member_routeuri: lb://gulimemberpredicates:- Path/api/gulimember/**filters:- RewritePath/api/(?<segment>.*),/$\{segment}并将所有逆向生成的工程调式出来 获取分类关联的品牌 例如&#xff1a;手机&#xff08;分类&#xff09;-> 品…...

Open3D 点云数据处理基础(Python版)

Open3D 点云数据处理基础&#xff08;Python版&#xff09; 文章目录 1 概述 2 安装 2.1 PyCharm 与 Python 安装 2.3 Anaconda 安装 2.4 Open3D 0.13.0 安装 2.5 新建一个 Python 项目 3 点云读写 4 点云可视化 2.1 可视化单个点云 2.2 同一窗口可视化多个点云 2.3…...

使用vue-qr,报错in ./node_modules/vue-qr/dist/vue-qr.js

找到node_modules—>vue-qr/dist/vue-qr.js文件&#xff0c;搜…e,将…去掉&#xff0c;然后重新运行项目。...

百川2大模型微调问题解决

之前用https://github.com/FlagAlpha/Llama2-Chinese微调过几个模型&#xff0c;总体来说llama2的生态还是比较好的&#xff0c;过程很顺利。微调百川2就没那么顺利了&#xff0c;所以简单做个记录 1. 数据准备&#xff0c;我的数据是单轮对话&#xff0c;之前微调llama2已经按…...

MySQL的事务-原子性

MySQL的事务处理具有ACID的特性&#xff0c;即原子性&#xff08;Atomicity)、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;Isolation&#xff09;和持久性&#xff08;Durability&#xff09;。 1. 原子性指的是事务中所有操作都是原子性的&#xff0c;要…...

D3839|完全背包

完全背包&#xff1a; 首先01背包的滚动数组中的解法是内嵌的循环是从大到小遍历&#xff0c;为了保证每个物品仅被添加一次。 for(int i 0; i < weight.size(); i) { // 遍历物品for(int j bagWeight; j > weight[i]; j--) { // 遍历背包容量dp[j] max(dp[j], dp[j…...

Java之Synchronized与锁升级

Synchronized与锁升级 一、概述 在多线程并发编程中 synchronized 一直是元老级角色&#xff0c;很多人都会称呼它为重量级锁。但是&#xff0c;随着 Java SE 1.6 对 synchronized 进行了各种优化之后&#xff0c;有些情况下它就并不那么重了。 本文详细介绍 Java SE 1.6 中为…...

kitex出现:open conf/test/conf.yaml: no such file or directory

open conf/test/conf.yaml: no such file or directory https://github.com/cloudwego/cwgo/issues/120 https://github.com/cloudwego/cwgo/issues/29 在使用Kitex生成的代码中&#xff0c;单元测试时回报错&#xff0c;如标题所示。出现该错的原因是&#xff0c;biz/servic…...

sql server多表查询

查询目标 现在有学生表和学生选课信息表&#xff0c;stu和stuSelect&#xff0c;stu中包含学生用户名、名字&#xff0c;stuSelect表中包含学生用户名&#xff0c;所选课程名 学生表&#xff1a; nameusername李明Li Ming李华Li Hua 学生选课表&#xff1a; usernameCourse…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

uniapp 字符包含的相关方法

在uniapp中&#xff0c;如果你想检查一个字符串是否包含另一个子字符串&#xff0c;你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的&#xff0c;但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

【51单片机】4. 模块化编程与LCD1602Debug

1. 什么是模块化编程 传统编程会将所有函数放在main.c中&#xff0c;如果使用的模块多&#xff0c;一个文件内会有很多代码&#xff0c;不利于组织和管理 模块化编程则是将各个模块的代码放在不同的.c文件里&#xff0c;在.h文件里提供外部可调用函数声明&#xff0c;其他.c文…...

基于stm32F10x 系列微控制器的智能电子琴(附完整项目源码、详细接线及讲解视频)

注&#xff1a;文章末尾网盘链接中自取成品使用演示视频、项目源码、项目文档 所用硬件&#xff1a;STM32F103C8T6、无源蜂鸣器、44矩阵键盘、flash存储模块、OLED显示屏、RGB三色灯、面包板、杜邦线、usb转ttl串口 stm32f103c8t6 面包板 …...

云原生时代的系统设计:架构转型的战略支点

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 一、云原生的崛起&#xff1a;技术趋势与现实需求的交汇 随着企业业务的互联网化、全球化、智能化持续加深&#xff0c;传统的 I…...

VSCode 没有添加Windows右键菜单

关键字&#xff1a;VSCode&#xff1b;Windows右键菜单&#xff1b;注册表。 文章目录 前言一、工程环境二、配置流程1.右键文件打开2.右键文件夹打开3.右键空白处打开文件夹 三、测试总结 前言 安装 VSCode 时没有注意&#xff0c;实际使用的时候发现 VSCode 在 Windows 菜单栏…...

篇章一 论坛系统——前置知识

目录 1.软件开发 1.1 软件的生命周期 1.2 面向对象 1.3 CS、BS架构 1.CS架构​编辑 2.BS架构 1.4 软件需求 1.需求分类 2.需求获取 1.5 需求分析 1. 工作内容 1.6 面向对象分析 1.OOA的任务 2.统一建模语言UML 3. 用例模型 3.1 用例图的元素 3.2 建立用例模型 …...