我是在python3.7.5上学习asyncio的人,所以我选择实现telentd作为任务
现在经过一些编码后,我发现它几乎完成了,除了一件奇怪的事情,下面的代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import asyncio
import os
import pty
import sys
import termios
from subprocess import Popen
async def copyto(src, dst):
while True:
if getattr(src, 'read', None):
# print(f"src has read")
buf = await src.read(1024)
else:
# print(f"src dont have read")
buf = os.read(src, 1024)
if getattr(dst, 'write', None):
# print(f"dst has write")
dst.write(buf)
await dst.drain()
else:
# print(f"dst dont has write")
os.write(dst, buf)
async def handlerCommand(reader, writer):
# command = 'podman run -it --rm alpine bash'
command = 'bash'
oldTty = termios.tcgetattr(sys.stdin)
# open pseudo-terminal to interact with subprocess
masterFd, slaveFd = pty.openpty()
# force remote side into character mode
writer.write(b"\xff\xfd\x22\xff\xfb\x01")
# use os.setsid() make it run in a new process group, or bash job control will not be enabled
Popen(
command,
preexec_fn=os.setsid,
stdin=slaveFd,
stdout=slaveFd,
stderr=slaveFd,
universal_newlines=True)
os.write(masterFd, b"ls\n")
await asyncio.gather(
copyto(masterFd, writer),
copyto(reader, masterFd)
)
# restore tty settings back
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldTty)
async def main(host, port):
server = await asyncio.start_server(
handlerCommand,
host,
port
)
addr = server.sockets[0].getsockname()
print(f'Serving on {addr}')
async with server:
await server.serve_forever()
if __name__ == "__main__":
asyncio.run(main('127.0.0.1', 8888))
这是一个完全可运行的代码,如您所见,当用户连接时,我将启动bash进程并将其连接到用户的套接字。但是现在我的问题是用户可以看到流程的输出,而无法将任何击键发送给流程。
我认为这可能是我对异步收集的理解。但我不确定
我只是在客户端使用
telnet 127.0.0.1 8888
以下服务器的分解版本,这些术语仍然无法解决 但是在异步级别上,我认为尽管解决方案太重了,问题还是解决了
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
a telnet server sample
"""
import asyncio
import os
import pty
import tty
from subprocess import Popen
import concurrent.futures
import telnetlib3
async def sock2f(sock, fobj):
"""
copy from socket to fobj
"""
while True:
buf = await sock.read(1)
if not buf:
print(f"sock2f break")
break
print(f"sock2fd {buf}")
fobj.write(buf)
fobj.flush()
async def f2sock(fobj, sock):
"""
copy from fobj to sock
"""
print(f"f2sock 开始")
def _block_read(length):
return fobj.read(length)
loop = asyncio.get_running_loop()
with concurrent.futures.ThreadPoolExecutor() as pool:
while True:
print(f"f2sock readbuf")
buf = await loop.run_in_executor(pool, _block_read, 32)
print(f"f2sock readbuf done")
if not buf:
print(f"f2sock break")
break
print(f"f2sock {buf}")
sock.write(buf)
await sock.drain()
async def handlerCommand(reader, writer):
"""
ha
"""
# command = 'podman run -it --rm alpine bash'
command = 'bash'
# open pseudo-terminal to interact with subprocess
masterFd, slaveFd = pty.openpty()
tty.setraw(slaveFd)
tty.setraw(masterFd)
Popen(
command,
bufsize=0,
preexec_fn=os.setsid,
stdin=slaveFd,
stdout=slaveFd,
stderr=slaveFd,
universal_newlines=True,
# close_fds=True,
shell=True
)
os.write(masterFd, b"ls\n")
m_read = os.fdopen(masterFd, 'rb')
m_write = os.fdopen(masterFd, 'wb')
await asyncio.gather(
f2sock(m_read, writer),
sock2f(reader, m_write),
)
loop = asyncio.get_event_loop()
coro = telnetlib3.create_server(
port=6023,
term="xterm-256color",
encoding=False,
shell=handlerCommand
)
server = loop.run_until_complete(coro)
loop.run_until_complete(server.wait_closed())