如何在协同程序中等待两项任务?

时间:2019-11-29 02:52:08

标签: python-asyncio

我是在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())

0 个答案:

没有答案