转义序列中断 pyserial 客户端
Escape sequences interrupt pyserial client
我正在使用 pyserial
为网络设备编写客户端,该客户端在登录到设备后并在设备上收到提示之前挂起几秒钟。跟踪线上的字节,我看到以下转义序列是造成阻塞的原因:
0003a0 73 65 20 6c 65 76 65 6c 0d 0a 0d 1b 5b 39 39 39 |se level....[999|
0003b0 39 42 0d 1b 5b 39 39 39 39 42 1b 5a 20 20 1b 5b |9B..[9999B.Z .[|
0003c0 36 6e 0d 0d 0d 0d 5b 61 64 6d 69 6e 40 73 77 69 |6n....[admin@swi|
0003d0 74 63 68 5d 20 3e 20 20 20 20 20 20 20 20 20 20 |tch] > |
当使用 screen
连接到设备时,没有延迟,因为 screen
正在正确响应。
0003a0 73 65 20 6c 65 76 65 6c 0d 0a 0d 1b 5b 39 39 39 |se level....[999|
0003b0 39 42 0d 1b 5b 39 39 39 39 42 1b 5a 20 |9B..[9999B.Z |
---=== WRITE ===---
000000 1b 5b 3f 31 3b 32 63 |.[?1;2c |
---=== READ ===---
000000 20 1b 5b 36 6e | .[6n |
---=== WRITE ===---
000000 1b 5b 35 37 3b 33 52 |.[57;3R |
---=== READ ===---
000000 1b 5b 34 6c 1b 5b 32 30 6c 1b 5b 3f 34 37 6c 1b |.[4l.[20l.[?47l.|
000010 5b 3f 37 68 1b 5b 3f 35 6c 1b 5b 3f 32 35 68 1b |[?7h.[?5l.[?25h.|
000020 5b 48 1b 5b 39 39 39 39 42 1b 5b 36 6e |[H.[9999B.[6n |
---=== WRITE ===---
000000 1b 5b 35 37 3b 31 52 |.[57;1R |
---=== READ ===---
000000 1b 5b 48 1b 5b 39 39 39 39 42 1b 44 1b 5b 39 39 |.[H.[9999B.D.[99|
000010 39 39 41 1b 5b 36 6e |99A.[6n |
---=== WRITE ===---
000000 1b 5b 31 3b 31 52 |.[1;1R |
---=== READ ===---
000000 1b 5b 48 1b 5b 39 39 39 39 43 1b 5b 36 6e |.[H.[9999C.[6n |
---=== WRITE ===---
000000 1b 5b 31 3b 31 34 31 52 |.[1;141R |
---=== READ ===---
000000 1b 5b 48 c4 9b 48 1b 5b 36 6e 0d 20 20 20 |.[H..H.[6n. |
---=== WRITE ===---
000000 1b 5b 31 3b 33 52 |.[1;3R |
---=== READ ===---
000000 1b 5b 48 1b 5b 39 39 39 39 43 1b 5b 36 6e 20 1b |.[H.[9999C.[6n .|
---=== WRITE ===---
000000 1b 5b 31 3b 31 34 31 52 |.[1;141R |
---=== READ ===---
000000 5b 36 6e 20 1b 5b 36 6e |[6n .[6n |
---=== WRITE ===---
000000 1b 5b 31 3b 31 34 32 52 1b 5b 32 3b 32 52 |.[1;142R.[2;2R |
---=== READ ===---
000000 1b 5b 33 3b 35 72 1b 5b 48 1b 5b 36 6e 0a 0a |.[3;5r.[H.[6n.. |
---=== WRITE ===---
000000 1b 5b 31 3b 31 52 |.[1;1R |
---=== READ ===---
000000 0a 0a 0a 0a 0a 1b 5b 36 6e 1b 5b |......[6n.[ |
---=== WRITE ===---
000000 1b 5b 35 3b 31 52 |.[5;1R |
---=== READ ===---
000000 39 39 39 39 42 1b 5b 36 6e 1b 5b 72 |9999B.[6n.[r |
---=== WRITE ===---
000000 1b 5b 35 3b 31 52 |.[5;1R |
---=== READ ===---
000000 1b 5b 31 3b 39 39 39 39 72 0d 0d 0d 1b 5b 39 39 |.[1;9999r....[99|
000010 39 39 42 5b 61 64 6d 69 6e 40 73 77 69 74 63 68 |99B[admin@switch|
000020 5d 20 3e 20 |] > |
这一系列转义序列到底在做什么,在我的客户端中处理这个问题的最佳方法是什么?
from asyncio import Protocol, get_event_loop
from serial.aio import create_serial_connection
class Serial(Protocol):
def connection_made(self, transport):
self.transport = transport
def data_received(self, data):
self.buffer += data.decode("utf-8")
self.handle()
def send(self, line):
self.transport.write("{}\r\n".format(line).encode())
loop = get_event_loop()
coro = create_serial_connection(loop, Serial, "/dev/ttyUSB0")
loop.run_until_complete(coro)
loop.run_forever()
带有 [6n
的块(以及它前面的转义字符)要求 终端 告诉它光标在哪里,作为确定屏幕大小的一部分。显然pyserial
不明白,你必须等待一段时间让程序放弃并继续。
CSI Ps n Device Status Report (DSR).
...
Ps = 6 -> Report Cursor Position (CPR) [row;column].
其中CSI是控制序列发起者escape[
.
当你运行screen
时,它interprets这个控制序列(即,它读取,理解 和 适当地回复 ):
case 'n':
if (a1 == 5) /* Report terminal status */
Report(win, "3[0n", 0, 0);
else if (a1 == 6) /* Report cursor position */
Report(win, "3[%d;%dR", win->w_y + 1, win->w_x + 1);
resize
等一些程序使用状态报告控制序列来确定终端屏幕的大小。它的工作原理是将光标移动到不可能远的地方 lower-right-corner,然后(因为终端有限制)询问它到达了多远。这个特定的例子没有使用 resize
,它为每个坐标发送 999
(但是否存在需要四位数字的真实场景是值得商榷的)。
正在发送设备状态控制序列的应用程序使用 escape[=79 将光标 向下移动 =][9999B
,显然稍后在设置以 [= 结尾的序列的滚动边距时使用它20=]。这是 frequently-used VT100 功能 而不是 ECMA-48(r
表示 私人使用序列,即未标准化)。
如果你给它一个虚假的光标位置值,你可能会得到不好的结果。
它还尝试使用两个 不同的 控制序列将光标从 screen
[=102] =]:
1b 5b 48 c4 9b 48
(0x9b
是 0x1b
、0x5b
或 escape[=79= 的 8 位等价物][
).
所以你的选择很少:
- 消除 screen-size 或
的(可能是不必要的)查询
- 修改您的 pyserial 脚本以处理设备状态控制序列
我正在使用 pyserial
为网络设备编写客户端,该客户端在登录到设备后并在设备上收到提示之前挂起几秒钟。跟踪线上的字节,我看到以下转义序列是造成阻塞的原因:
0003a0 73 65 20 6c 65 76 65 6c 0d 0a 0d 1b 5b 39 39 39 |se level....[999|
0003b0 39 42 0d 1b 5b 39 39 39 39 42 1b 5a 20 20 1b 5b |9B..[9999B.Z .[|
0003c0 36 6e 0d 0d 0d 0d 5b 61 64 6d 69 6e 40 73 77 69 |6n....[admin@swi|
0003d0 74 63 68 5d 20 3e 20 20 20 20 20 20 20 20 20 20 |tch] > |
当使用 screen
连接到设备时,没有延迟,因为 screen
正在正确响应。
0003a0 73 65 20 6c 65 76 65 6c 0d 0a 0d 1b 5b 39 39 39 |se level....[999|
0003b0 39 42 0d 1b 5b 39 39 39 39 42 1b 5a 20 |9B..[9999B.Z |
---=== WRITE ===---
000000 1b 5b 3f 31 3b 32 63 |.[?1;2c |
---=== READ ===---
000000 20 1b 5b 36 6e | .[6n |
---=== WRITE ===---
000000 1b 5b 35 37 3b 33 52 |.[57;3R |
---=== READ ===---
000000 1b 5b 34 6c 1b 5b 32 30 6c 1b 5b 3f 34 37 6c 1b |.[4l.[20l.[?47l.|
000010 5b 3f 37 68 1b 5b 3f 35 6c 1b 5b 3f 32 35 68 1b |[?7h.[?5l.[?25h.|
000020 5b 48 1b 5b 39 39 39 39 42 1b 5b 36 6e |[H.[9999B.[6n |
---=== WRITE ===---
000000 1b 5b 35 37 3b 31 52 |.[57;1R |
---=== READ ===---
000000 1b 5b 48 1b 5b 39 39 39 39 42 1b 44 1b 5b 39 39 |.[H.[9999B.D.[99|
000010 39 39 41 1b 5b 36 6e |99A.[6n |
---=== WRITE ===---
000000 1b 5b 31 3b 31 52 |.[1;1R |
---=== READ ===---
000000 1b 5b 48 1b 5b 39 39 39 39 43 1b 5b 36 6e |.[H.[9999C.[6n |
---=== WRITE ===---
000000 1b 5b 31 3b 31 34 31 52 |.[1;141R |
---=== READ ===---
000000 1b 5b 48 c4 9b 48 1b 5b 36 6e 0d 20 20 20 |.[H..H.[6n. |
---=== WRITE ===---
000000 1b 5b 31 3b 33 52 |.[1;3R |
---=== READ ===---
000000 1b 5b 48 1b 5b 39 39 39 39 43 1b 5b 36 6e 20 1b |.[H.[9999C.[6n .|
---=== WRITE ===---
000000 1b 5b 31 3b 31 34 31 52 |.[1;141R |
---=== READ ===---
000000 5b 36 6e 20 1b 5b 36 6e |[6n .[6n |
---=== WRITE ===---
000000 1b 5b 31 3b 31 34 32 52 1b 5b 32 3b 32 52 |.[1;142R.[2;2R |
---=== READ ===---
000000 1b 5b 33 3b 35 72 1b 5b 48 1b 5b 36 6e 0a 0a |.[3;5r.[H.[6n.. |
---=== WRITE ===---
000000 1b 5b 31 3b 31 52 |.[1;1R |
---=== READ ===---
000000 0a 0a 0a 0a 0a 1b 5b 36 6e 1b 5b |......[6n.[ |
---=== WRITE ===---
000000 1b 5b 35 3b 31 52 |.[5;1R |
---=== READ ===---
000000 39 39 39 39 42 1b 5b 36 6e 1b 5b 72 |9999B.[6n.[r |
---=== WRITE ===---
000000 1b 5b 35 3b 31 52 |.[5;1R |
---=== READ ===---
000000 1b 5b 31 3b 39 39 39 39 72 0d 0d 0d 1b 5b 39 39 |.[1;9999r....[99|
000010 39 39 42 5b 61 64 6d 69 6e 40 73 77 69 74 63 68 |99B[admin@switch|
000020 5d 20 3e 20 |] > |
这一系列转义序列到底在做什么,在我的客户端中处理这个问题的最佳方法是什么?
from asyncio import Protocol, get_event_loop
from serial.aio import create_serial_connection
class Serial(Protocol):
def connection_made(self, transport):
self.transport = transport
def data_received(self, data):
self.buffer += data.decode("utf-8")
self.handle()
def send(self, line):
self.transport.write("{}\r\n".format(line).encode())
loop = get_event_loop()
coro = create_serial_connection(loop, Serial, "/dev/ttyUSB0")
loop.run_until_complete(coro)
loop.run_forever()
带有 [6n
的块(以及它前面的转义字符)要求 终端 告诉它光标在哪里,作为确定屏幕大小的一部分。显然pyserial
不明白,你必须等待一段时间让程序放弃并继续。
CSI Ps n Device Status Report (DSR).
...
Ps = 6 -> Report Cursor Position (CPR) [row;column].
其中CSI是控制序列发起者escape[
.
当你运行screen
时,它interprets这个控制序列(即,它读取,理解 和 适当地回复 ):
case 'n': if (a1 == 5) /* Report terminal status */ Report(win, "3[0n", 0, 0); else if (a1 == 6) /* Report cursor position */ Report(win, "3[%d;%dR", win->w_y + 1, win->w_x + 1);
resize
等一些程序使用状态报告控制序列来确定终端屏幕的大小。它的工作原理是将光标移动到不可能远的地方 lower-right-corner,然后(因为终端有限制)询问它到达了多远。这个特定的例子没有使用 resize
,它为每个坐标发送 999
(但是否存在需要四位数字的真实场景是值得商榷的)。
正在发送设备状态控制序列的应用程序使用 escape[=79 将光标 向下移动 =][9999B
,显然稍后在设置以 [= 结尾的序列的滚动边距时使用它20=]。这是 frequently-used VT100 功能 而不是 ECMA-48(r
表示 私人使用序列,即未标准化)。
如果你给它一个虚假的光标位置值,你可能会得到不好的结果。
它还尝试使用两个 不同的 控制序列将光标从 screen
[=102] =]:
1b 5b 48 c4 9b 48
(0x9b
是 0x1b
、0x5b
或 escape[=79= 的 8 位等价物][
).
所以你的选择很少:
- 消除 screen-size 或 的(可能是不必要的)查询
- 修改您的 pyserial 脚本以处理设备状态控制序列