RMI规范–第十章 RMI 通信协议

RMI规范–第十章 RMI 通信协议 (转贴)

10.1 概述
RMI 协议使用另两个协议作为其在通信格式:Java 对象序列化和 HTTP。对象序列化协议用于编组调用和返回数据。HTTP 协议用于“投寄”远程方法调用,并在情况允许时获得返回数据。

每种协议都有专门的语法文档。产品规则中的非终结符号可能会引用其它协议(对象序列化或 HTTP)所管理的规则。在跨协议边界时,后续产品将使用该嵌入的协议。 
关于语法符号的说明 
我们使用与 Java 语言规范(参见 JLS 的第 2.3 节)中类似的符号。 
流中的控制代码由十六进制形式的文字值表示。 
语法中的有些非终结符号表示调用中提供的与应用程序有关的值。这种非终结符号的定义由其 Java 类型组成。语法后面是将这些非终结字符映射到相应类型的表。 
10.2 RMI 传输协议
RMI 的通信格式由 Stream 表示。这里所采用的术语是从客户机的角度来讲的。 
out 指输出消息,而 in 指输入消息。传输标题的内容并未用对象序列化进行格式化。 
Stream: Out In 
RMI 所 用的输入和输出流是成对出现的。每个 out 流都有相应的 in 流。在语法中,out 流映射到套接字的输出流(从客户机角度)。in 流(在语法 中)将与相应套接字的输入流配对。由于输出与输入流是成对的,所以输入流中唯一需要的标题信息就是一个表示是否理解协议的确认;其他标题信息(例如魔数和 版本号)都将由流对的上下文所隐含。 
10.2.1 输出流格式 
RMI 中的输出流由传输标题信息后跟一个消息序列组成。此外,输出流也可包含嵌入在 HTTP 协议中的调用。 
Out: Header Messages HttpMessage 
Header: 0x4a 0×52 0x4d 0×49 Version Protocol 
Version: 0×00 0×01 
Protocol: StreamProtocol SingleOpProtocol MultiplexProtocol 
StreamProtocol: 0x4b 
SingleOpProtocol: 0x4c 
MultiplexProtocol: 0x4d 
Messages: Message Messages Message 
Messages 将 包装在 Protocol 指定的特定协议内。对于 SingleOpProtocol,Header 的后面可能只有一个 Message,且 该 Message 没有包装其它数据。SingleOpProtocol 用于 HTTP 请求中所嵌入的调用,其中请求和响应都只能为一个。

对 于 StreamProtocol 和 MultiplexProtocol,服务器必须用字节 0x4e(表示支持该协议) 和 EndpointIdentifier(包含主机名和端口号,服务器可以看到它们在被客户机使用)进行响应。如果由于安全原因而无法执行该操作,客户 机即可使用该信息来确定其主机名。随后,客户机必须用另一个包含接受连接的缺省端点的 EndpointIdentifier 进行响应。对 于 MultiplexProtocol,服务器可以用它来标识客户机。 
对于 StreamProtocol,本次端点协商后,将在输出流上发 送 Messages,而不对数据进行进一步打包。对于 MultiplexProtocol,套接字将连接用作多路复用连接的具体连接,如 第 10.6 节“RMI 的多路复用协议”中所述。在该多路复用连接上初始化的虚拟连接由一系列 Messages 组成,如下所述。 
输出消息共有三种:Call、Ping 和 DgcAck。Call 将对方法调用进行编码。Ping 是一个传输级消息,用于测试远程虚拟机的活动性。DGCAck 是一个对服务器的分布式垃圾收集器的确认,指示客户机已经接收到服务器返回值中的远程对象。 
Message: Call Ping DgcAck 
Call: 0×50 CallData 
Ping: 0×52 
DgcAck: 0×54 UniqueIdentifier 
10.2.2 输入流格式 
当前输入信息共有三种:ReturnData、HttpReturn 和 PingAck。ReturnData 是“正常”RMI 调用的结果。HttpReturn 是 HTTP 协议中嵌入调用的返回结果。PingAck 是对 Ping 消息的确认。 
In: ProtocolAck Returns ProtocolNotSupported HttpReturn 
ProtocolAck: 0x4e 
ProtocolNotSupported: 0x4f 
Returns: Return Returns Return 
Return: ReturnData PingAck 
ReturnData: 0×51 ReturnValue 
PingAck: 0×53 
10.3 RMI 对对象序列化协议的使用
RMI 调 用中的调用和返回数据将使用 Java 对象序列化协议进行格式化。每个方法调用的 CallData 表示为 ObjectIdentifier(调用 的目标)、Operation(代表要调用方法的数字)、Hash(检验客户机 stub 与远程对象 skeleton 是否使用同一 stub 协议 的数字),后跟该调用的零个或多个参数列表。 
在 JDK1.1 stub 协议中,Operation 代表方法号(由 rmic 分配), 而 Hash 是 stub/skeleton 散列,它是该 stub 的接口散列。在 JDK1.2 stub 协议(利用带 -v1.2 选项 的 rmic 生成 JDK1.2 stub)中, Operation 的值为 -1 且 Hash 代表了所要调用方法的散列。散列将在 “ RemoteRef 接口”一节中介绍。 
CallData: ObjectIdentifier Operation Hash Argumentsopt 
ObjectIdentifier: ObjectNumber UniqueIdentifier 
UniqueIdentifier: Number Time Count 
Arguments: Value Arguments Value 
Value: Object Primitive 
RMI 调用的 ReturnValue 由指示正常或异常返回的返回代码、标记返回值的 UniqueIdentifier(用于在必要时发送 DGCAck)后跟以下返回结果组成:返回的值或抛出的异常。 
ReturnValue: 0×01 UniqueIdentifier Valueopt 0×02 UniqueIdentifier Exception 
注 意 – ObjectIdentifier、UniqueIdentifier 和 EndpointIdentifier 并不是用缺省序列化编写的, 而是各自使用自己的 write 方法(但不是对象序列化所用的 writeObject 方法);每种标识符的 write 方法都将其组件数据连续添 加到输出流中。 
10.3.1 类注解和类加载 
RMI 分别覆盖了 ObjectOutputStream 和 ObjectInputStream 的 annotateClas 
s 和 resolveClass 方 法。每个类都用 codebase URL(加载该类的位置)进行注解。annotateClass 方法中将查询加载该类的类加载器以得到 其 codebase URL。如果类加载器非空且其 codebase 也为非空,则将使 用 ObjectOutputStream.writeObject 方法将该 codebase 写入流中;否则将使用 writeObject 方法 将空值写入流中。注意:最好不要注解“java”包中的类,因为它们对于接收者来说总是可用的。 
类注解是在序列化恢复期间 用 ObjectInputStream.resolveClass 方法解析的。resolveClass 方法首先 用 ObjectInputStream.readObject 方法读取注解。如果注解(codebase URL)非空,它就获得该 URL 的类加 载器并试图加载该类。利用 java.net.URLConnection 获取类字节,即可对该类进行加载(与 web 浏览器的 applet 类加 载器所用的机制相同)。 
10.4 RMI 对 HTTP POST 协议的使用
为了通过防火墙调用远程方法,有些 RMI 调用使用了 HTTP 协议,尤其是 HTTP POST。在传递标题中指定的 URL 可以为下列内容之一: 
http://<host>:<port>/ http://<host>:80/cgi-bin/java-rmi?forward=<port> 
第一个 URL 用于与特定 host 和 port 上的 RMI 服务器直接通信。第二种形式的 URL 用于调用服务器上的“cgi”脚本,后者将把调用转发给指定 port 上的服务器。 
HttpPostHeader 是 POST 请 求的标准 HTTP 标题。HttpResponseHeader 是对传递过程的标准 HTTP 响应。如果响应状态代码不是 200,则认为没有返回 值。注意一个 HTTP POST 请求中只能嵌入一个 RMI 调用。 
HttpMessage: HttpPostHeader Header Message 
HttpReturn: HttpResponseHeader Return 
注意 – 只有 SingleOpProtocol 出现在 HttpMessage 的标题中。HttpReturn 不包含用于确认协议的字节。 
10.5 RMI 的与应用程序有关的值
本表列表出 RMI 所用的代表与应用程序有关的值的非终结符号。该表将每个符号映射为相应的类型。每个符号都分别使用它所嵌入在其中的协议进行格式化。 
Count short 
Exception java.lang.Exception 
Hash long 
Hostname UTF 
Number int 
Object java.lang.Object 
ObjectNumber long 
Operation int 
PortNumber int 
Primitive byte, int, short, long… 
Time long 
10.6 RMI 的多路复用协议
多 路复用的目的是提供一种模型,其中两个端点都可打开多个到另一端点的全双工连接,而在相同环境下,使用其他工具(例如 TCP 连接)时,只有一个端点能 打开这样的双向连接。利用这种简单的多路复用协议,RMI 即可允许客户在某些其他协议无能为力的情况下,连接到 RMI 的服务器对象上。例如,有 些 applet 环境的安全管理器不允许创建服务器套接字监听到来的连接,以防止这种 applet 从直接套接字连接上导出 RMI 对象及提供远程 调用服务。但是,如果该 applet 可以打开到其 codebase 主机的正常套接字连接,它就可以在该连接上使用多路复用协议,从而允 许 codebase 主机调用该 applet 所导出的 RMI 对象的方法。本节介绍了多路复用协议的格式和规则。 
10.6.1 定义 
本节定义一些将在协议其余部分使用的术语。 
端点是用多路复用协议连接的两个用户之一。 
多路复用协议必须位于已有的双向可靠字节流之上,假设由一个端点向另一个端点进行初始化。在当前的 RMI 用法中,它通常是 TCP 连接,由 java.net.Socket 对象建立。该连接称为具体连接。 
多 路复用协议有助于虚拟连接的使用。虚拟连接本身就是双向的可靠字节流,代表两个端点之间的特定会话。一个连接上两个端点之间的虚拟连接集组成一个多路复用 连接。使用多路复用协议,虚拟连接可以由任一端点打开和关闭。虚拟连接相对给定端点的状态由在具体连接上发送和接收的多路复用协议元素定义。该状态涉及连 接是打开还是关闭、传送的实际数据及相关的流控制机制。如果没有特别说明,本节中其余部分将使用术语连接表示虚拟连接。 
给定多路复用连接内的虚拟连接由一个 16 位整数标识,称为连接标识符。因而,一个多路复用连接中可能存在 65,536 个虚拟连接。实现可能会限制能同时使用的虚拟连接数。 
10.6.2 连接状态和流控制 
连接由用多路复用协议定义的各种操作来控制。下面是协议所定义的操作名:OPEN、CLOSE、CLOSEACK、REQUEST 和 TRANSMIT。所有操作的准确格式和规则将在第 10.6.3 节 “协议格式”中详细介绍。 
OPEN、CLOSE 和 CLOSEACK 操作控制连接的打开和关闭,而 REQUEST 和 TRANSM 
IT 操作用于在流控制机制的限制内在打开的连接上传输数据。 
连接状态 
如果端点发送连接的 OPEN 操作或接收到连接的 OPEN 操作(且随后没有关闭它),则该虚拟连接相对于该端点即为打开的。下面介绍不同的协议操作。 
如果端点发送连接的 CLOSE 操作,但随后没有接收到该连接的 CLOSE 或 CLOSEACK 操作,则该虚拟连接相对于该端点是等待关闭的。 
如果端点从来没有打开过连接或接收到连接的 CLOSE 或 CLOSEACK 操作(且随后没有打开),则该虚拟连接相对于该端点是关闭的。 
流控制 
多 路复用协议使用简单的包流控制机制允许多个虚拟连接并存于同一具体连接上。流控制机制的高级要求是所有虚拟连接的状态都是独立的;一个连接的状态不会影响 其他连接。例如,如果处理来自某个连接的数据的数据缓冲区已满,应不会防碍其他连接的数据传输和处理。如果连接的继续依赖于另一个连接的结束(例如递 归 RMI 调用时),则这一点将至关重要。因而,它的实际意义是实现必须总能消耗和处理在具体连接上(假定它遵循该规范)准备输入的所有多路复用协议数 据。 
每个端点具有两个与各连接相关联的状态值:该端点已经请求但尚未接收到的数据字节数(输入请求数)和另一端点请求但该端点尚未提供的数据字 节数(输出请求数)。端点的输出请求数在从其他端点接收到 REQUEST 操作时将增大,而在它发送 TRANSMIT 操作时将减小。端点的输入请求 数在它发送 REQUEST 操作时将增大,而在它接收到 TRANSMIT 操作时将减小。这些值如果为负就将违反协议。 
如果端点发送 REQUEST 操作而导致其输入请求数增大并超过其当前可以无阻塞处理的字节数,则违反协议。但如果连接的用户在等待读取数据,则应确保其输入请求数大于零。 
如 果端点发送的 TRANSMIT 操作包含有比其输出请求数更多的字节,则违反协议。它可以缓冲外流的数据,直到连接用户请求显式刷新写入到连接中的数 据。但如果因为显式的刷新或实现的输入缓冲区满而必须在连接上发送数据,则连接用户可能被阻塞,直到有足够 TRANSMIT 操作。 
在满足上述规则的前提下,实现可以相对自由地发送 REQUEST 和 TRANSMIT 操作。例如,如果其输入缓冲区不空,则端点可以请求连接的更多数据。 
10.6.3 协议格式 
多路复用协议的字节流格式由连续的可变长度记录序列组成。记录的第一个字节是一个操作码,它可以识别记录的操作并可确定其内容其余部分的格式。我们定义了下列合法的操作码: 
值 名称 
0xE1 OPEN 
0xE2 CLOSE 
0xE3 CLOSEACK 
0xE4 REQUEST 
0xE5 TRANSMIT 
如果记录的第一个字节不是所定义的操作码,则违反协议。下面各节介绍了每种操作码的记录格式。 
OPEN 操作 
下面是 OPEN 操作的记录格式: 
大小(字节) 名字 描述 
1 opcode 操作码 (OPEN) 
2 ID 连接标识符 
端点将发送 OPEN 操作以打开指定的连接。如果ID 指向对发送端点当前已打开或即将关闭的连接,则违反协议。打开连接后,连接的输入和请求数状态在两个端点上都为零。 
接收到 OPEN 操作表示另一端点正在打开指定的连接。打开连接后,连接的输出和请求数状态在两个端点处都为零。 
为 防止两端点间的标识符冲突,有效连接标识符空间将根据最高位的值分为两半。每个端点仅允许打开高位为某一特定值的连接。启动具体连接的端点必须只打开高位 为标识符中的连接,另一端点必须只打开高位为零的连接。例如,如果不能创建服务器套接字的 RMI applet 启动了与其 codebase 主机的 多路复用连接,则该 applet 可以打开标识符范围为 0×8000-7FFF 的虚拟连接,而服务器可以打开标识符范围为 0-0x7FFF 的虚 拟连接。 
CLOSE 操作 
以下是 CLOSE 操作的记录格式: 
大小(字节) 名字 描述 
1 opcode 操作代码 (OPEN) 
2 ID 连接标识符 
端 点发送 CLOSE 操作以关闭指定的连接。如果 ID 指向对发送端点当前已关闭或即将关闭的连接(如果它已发送过此连接的 CLOSE 操作,也可能 是对接收端点即将关闭的连接),则违反协议。 发送 CLOSE 后,连接就成为对发送端点即将关闭的连接。因此,该端点将不能重新打开该连接,直到它从 另一端点接收到 CLOSE 或 CLOSEACK 为止。 
接收到 CLOSE 操作表示另一端点已关闭指定的连接,因此该连接已在接收端点上 被关闭。虽然接收端点可能不再为此连接发送其它操作(直到被再次打开),但它仍应为此连接的读者提供实现的输入缓冲区中的数据。如果连接已经被打开(而不 是即将关闭),则接收端点必须用 CLOSEACK 操作作为响应。 
CLOSEACK 操作 
以下是 CLOSEACK 操作的记录格式: 
大小(字节) 名字 描述 
1 opcode 操作代码 (OPEN) 
2 ID 连接标识符 
端点发送 CLOSEACK 操作以表明已收到来自接收端点的 CLOSE 操作。如果收到操作时 ID 指向的连接不是对接收端点将要关闭的连接,则违反协议。 
接收到 CLOSEACK 操作可将指定连接的状态从即将关闭改为已关闭,因此以后还可重新打开连接。 
REQUEST 操作 
以下是 REQUEST 操作的记录格式: 
大小(字节) 名字 描述 
1 opcode 操作代码 (OPEN) 
2 ID 连接标识符 
4 count 请求的额外字节数 
端点发送 REQUEST 操作以增大指定连接的输入请求数。如果 ID 未指向发送端点打开的连接,则违反协议。端点的输入请求数按值 count 递增。count 的值是 32 位有符号整数。如果为负数或零,则违反协议。 
接收到 REQUEST 操作将使指定连接的输出请求数按 count 增加。如果接收端点即将关闭连接,则将忽略 REQUEST 操作。 
TRANSMIT 操作 
以下是 TRANSMIT 操作的记录格式。 
大小(字节) 名字 描述 
1 opcode 操作码 (OPEN) 
2 ID 连接标识符 
4 count 传输的字节数 
count data 传输数据 
端 点发送 TRANSMIT 操作后,才真正在指定连接上传输数据。如果 ID 未指向对发送端点打开的连接,则违反协议。端点的输出请求按 值 count 递减。count 的值是 32 位有符号整数。如果为负数或零,则违反协议。如果 TRANSMIT 操作导致输出请求数成为负数,则 也违反协议。 
接收到 TRANSMIT 操作时从连接中可读到的字节队列将增加 count 字节的数据。 
接收端点的输入请求数按值 count 递减。如果这会使输入请求数成为零,而该连接的用户却试图读取更多数据,则该端点应用另一个 REQUEST 操作作为响应。如果接收端点即将关闭连接,则将忽略 TRANSMIT 操作。 
违反协议 
如果出现上述违反协议的现象,或者在具体连接中检测到通讯错误,则多路复用连接即被关闭。实际连接将被终止,而所有虚拟连接也被立即关闭。连接的用户可以读取虚拟连接中已经可以读取的数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值