一家名为 Outdaters 的公司运营着一个小型计算机有线网络,称为 Outernet。与互联网不同,Outernet 并非基于 TCP/IP 协议。由于资金匮乏,Outernet 中的计算机并非都能直接相互通信。
Outdaters 已经找到了解决方案。他们创建了一种协议,使网络中的所有计算机都成为应用代理。应用代理可以从一台连接的计算机接收数据,并将其发送到另一台连接的计算机。因此,通过在 Outernet 中使用该协议,如果一台计算机想要向未直接连接的计算机发送信息,它必须将信息发送给一台连接的计算机/应用代理,并请求其帮助将信息转发给目的地或另一个连接的计算机/应用代理。
该协议描述如下:
A. 端口
应用代理使用端口来标识每一台连接的计算机。端口号是 0 到 32,767 之间的整数。对于应用代理而言,0 表示应用代理本身,其他每个端口号代表连接到该应用代理的一台唯一计算机。
B. 命令
应用代理仅接受 3 种命令(区分大小写):TO、DATA、QUIT。 对于每个传入的命令,应用代理在处理完该命令后,会向传入端口返回一行 3 位的结果代码。 格式:
xxx< LF > xxx -- the 3-digit result code
结果代码:
- 100: OK。无错误/数据已路由至目的地
- 101: OK。数据已路由至应用程序。(目标计算机即为应用代理本身。)
- 200: 会话结束(对 QUIT 命令的响应)
- 300: 未知命令
- 301: 未知目的地
- 302: 未开始会话
- 303: 不允许循环(当传入端口 = 传出端口时)
各命令详情:
TO:<目标计算机名称><LF>- 告知应用代理,后续数据需要发送至 <目标计算机名称>,并取消上一个 "TO" 命令的效果(向原目标计算机发送 "QUIT" 命令)。如果 TO 命令失败(结果代码既不是 100 也不是 101),应用代理的状态将不会改变。
- 可能的结果代码:
- 100: 在路由表中找到目标计算机,且不是应用代理本身。
- 101: 在路由表中找到目标计算机,且正是应用代理本身。
- 301: 在路由表中未找到目标计算机。
- 303: 在路由表中找到目标计算机,但传入端口 = 传出端口。
DATA<LF><数据><点 "."><LF>- 将 <数据> 发送至目标计算机。<数据> 将被视为数据流,如果目标计算机不是应用代理本身,则数据将原样发送至目的地。反斜杠 ("\") 是元字符,"." 表示一个简单的点 ".",而不是结束标志,"\" 表示 "\"。
- 可能的结果代码:
- 100: 在路由表中找到目标计算机,且不是应用代理本身。数据被路由至相应的传出端口。
- 101: 在路由表中找到目标计算机,且正是应用代理本身。数据被路由至运行在此应用代理上的应用程序。
- 302: 未开始会话,此命令被忽略。
QUIT<LF>- 结束此通信会话。
- 可能的结果代码:
- 200: 会话结束(对 QUIT 命令的响应)
- 302: 未开始会话,此命令被忽略。
C. 会话
当一台计算机(请求者)向应用代理发送 "TO" 命令时,通信会话开始;当向应用代理发送 "QUIT" 命令时,会话结束。在会话中,请求者可以向应用代理发送多个 "TO" 和 "DATA" 命令以发送多条消息。
应用代理能够同时处理来自不同端口的会话。
D. 路由表
每个应用代理都持有一个路由表。它使用该表来查找目标计算机名称应使用的端口。路由表中的每一行包含 2 个字段,第一个是目标计算机名称,第二个是传出端口号。这意味着,发送给具有该目标计算机名称的计算机的数据,将通过该传出端口号发送出去。端口号 0 表示数据应路由至运行在此应用代理上的应用程序;该目标计算机名称实际上就是应用代理的名称。
E. 路由
如果路由可行,应用代理使用相同的 "TO"、"DATA"、"QUIT" 命令来路由传入的数据。 在路由表中搜索后,如果找到了传出端口,应用代理必须为每个有效的传入 "TO" 命令在传出端口上创建一个完整的会话:开头一个 "TO" 命令,零个或多个用于路由数据的 DATA 命令,如果传入会话结束或收到另一个传入 "TO" 命令,则在最后发送一个 "QUIT" 命令。
端口 0 的处理方式与其他传出端口相同,只是不会实际发送传出命令,即所有命令的结果代码都将发送到传入端口,但不会向任何传出端口发送命令。 现在,Outdaters 聘请您编写引擎来实现该应用代理的协议。
输入格式
输入由一系列测试用例组成。每个测试用例以应用代理的路由表开始,随后是应用代理的传入请求。
路由表包括:一行包含一个整数 $M$ ($1 \leq M \leq 32\,768$),表示路由表中的行数;接着是 $M$ 行,每行包含一条路由信息。每条路由信息包含一个唯一的目标计算机名称(路由表中为 1 到 15 个字母数字字符)和一个传出端口号(0 到 32,767 的整数),中间用空格分隔,计算机名称区分大小写。
应用代理的传入请求包括来自已连接计算机的若干请求会话。一行以数字符号 "#" 开头,后跟一个整数 $P$ ($-1$, $1$ 到 $32\,767$),表示后续输入来自端口 $P$,$P < 0$ 表示测试用例结束。请求会话中的命令不会被 "#" 行中断。为简化输入处理,输入文件中的数据命令仅包含 "0"-"9"、"a"-"z"、"A"-"Z"、"@"、"#"、"_"、"+"、"-"、"*"、"/"、"\"、"?"、","、"." 和
输入以单个零结束。
输出格式
对于每个测试用例,按对应输入的顺序打印所有发送数据的端口的输出。对于每个端口的输出,一行以数字符号 "#" 开头,后跟一个整数 $P$ ($-1$, $1$ 到 $32\,767$),表示后续命令是在端口 $P$ 输出的,$P=-1$ 表示当前测试用例的输出结束。在 "#" 行之后是该端口输出的命令,直到遇到下一个 "#" 行。仅当需要更改端口号时才需要 "#" 行。
#< 端口号 >< LF > 在该端口输出的命令 #< 另一个端口号 >< LF > 在该端口输出的命令 ... #-1< LF >
样例
样例输入 1
5
RED 0
YELLOW 1
GREEN 2
BLUE 3
WHITE 3
#1
TO:GREEN
DATA
HELLO
.
#4
TO:WHITE
#1
Quit
QUIT
#2
TO:GREEN
DATA
A JOKE to myself
.
QUIT
#3
TO:ORANGE
QUIT
#4
QUIT
#-1
0
样例输出 1
#2
TO:GREEN
#1
100
#2
DATA
HELLO
.
#1
100
#3
TO:WHITE
#4
100
#1
300
#2
QUIT
#1
200
#2
303
302
302
#3
301
302
QUIT
#4
200