【问题标题】:How to connect INET socket to PTY device in Python如何在 Python 中将 INET 套接字连接到 PTY 设备
【发布时间】:2018-02-14 06:43:21
【问题描述】:

我正在尝试在 Python 中复制此 socat 命令的行为:

socat PTY,link=/tmp/mypty tcp-listen:1234

到服务器端口 1234 的 TCP 连接将连接到 /tmp/mypty(到 /dev/pts/N 的链接),并且运行 screen /tmp/mypty 将允许用户与另一端的任何东西进行交互那个连接。换句话说,如果它用于连接到那个监听端口:

socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp-connect:172.16.30.3:1234

然后运行screen /tmp/mypty 会将用户连接到在TCP 连接另一端运行的bash 实例。

获取 TCP 套接字很简单。我可以使用 os.openpty() 获得 PTY,这会产生一对文件描述符,它们只是整数。

我无法做的是以一种可以无缝地来回传递数据的方式挂钩该套接字和那些文件描述符。如果它们都是套接字,那将是微不足道的;使用带有 select() 的循环将新的 read 数据传递给另一个套接字的 write

我尝试过的:

  1. 使用 socket.fromfd 使用 PTY 文件描述符之一构建套接字。这导致OSError: [Errno 88] Socket operation on non-socket。此代码示例,其中 clisock 是一个有效的套接字:
def sockToPty(clisock,addr): (主,从)= os.openpty() print('PTY: 打开 {}'.format(os.ttyname(slave))) ptymsock = socket.fromfd(master, socket.AF_UNIX, socket.SOCK_STREAM) ptyssock = socket.fromfd(从属,socket.AF_UNIX,socket.SOCK_STREAM) print('CLISOCK: {}'.format(clisock)) print('PTYMSOCK: {}'.format(ptymsock)) print('PTYSSOCK: {}'.format(ptyssock)) 对等[clisock] = ptymsock 对等[ptyssock] = clisock 而真: iready, orready, eready = select.select([clisock, ptyssock], [], []) 准备好袜子: 数据 = sock.recv(4096) 如果不是 len(数据): 返回 对等[袜子].send(数据)

导致此错误:

PTY:打开 /dev/pts/2 CLISOCK: PTYMSOCK: PTYSSOCK: 线程 Thread-1 中的异常: 回溯(最近一次通话最后): _bootstrap_inner 中的文件“/usr/lib/python3.6/threading.py”,第 916 行 自我运行() 文件“/usr/lib/python3.6/threading.py”,第 864 行,运行中 self._target(*self._args, **self._kwargs) sockToPty 中的文件“./tcpmux.py”,第 62 行 对等[袜子].send(数据) OSError: [Errno 88] 非套接字上的套接字操作

如果我使用 AF_INET 而不是 AF_UNIX 则没有区别。

  1. 使用 os.dup2() 将 SSLsocket 的 fileno() 映射到标准输入、标准输出和标准错误,然后运行 ​​os.openpty()(如 tcp_pty_bind.py)。这不会产生任何错误,但是当我使用screen 打开 PTY 时没有明显的数据流。

几乎所有 Python PTY 示例都集中在 pty.spawn() 上,因此很难找到任何使用 PTY(诚然晦涩)的示例代码。

旁注:我使用 os.openpty 而不是 pty.openpty 只是因为似乎没有意义。 pty 模块似乎被宣传为更多的可移植性,但没有更多的可移植性,而且我在 Linux 上工作,所以可移植性不是问题。因为我得到了正确的 os.ttyname() 的有效文件描述符,所以我没有理由质疑 openpty() 不能正常工作。

【问题讨论】:

    标签: python python-3.x sockets pty


    【解决方案1】:

    这可以使用 select.poll() 而不是 select.select() 来完成,因为 select.poll() 将适用于文件描述符和套接字。

    def sockToPty(clisock,addr):
        (master, slave) = os.openpty()
        tty.setraw(master, termios.TCSANOW)
        print('PTY: Opened {} for {}'.format(os.ttyname(slave), addr))
        mypoll = select.poll()
        mypoll.register(clisock, select.POLLIN)
        mypoll.register(master, select.POLLIN)
        try:
            while True:
                fdlist = mypoll.poll(1000)
                for fd,event in fdlist:
                    # We treat sockets and FDs differently
                    if fd == master:
                        data = os.read(fd, 4096)
                        if len(data) == 0:
                            print('PTY: {}, {} exiting due to Zero read.'.format(addr, os.ttyname(slave)))
                            raise GetOut
                        clisock.send(data)
                    else:
                        data = clisock.recv(4096)
                        if len(data) == 0:
                            print('PTY: {}, {} exiting due to Zero read.'.format(addr, os.ttyname(slave)))
                            raise GetOut
                        os.write(master, data)
        except GetOut:
            os.close(master)
            os.close(slave)
            clisock.shutdown(socket.SHUT_RDWR)
            clisock.close()
    

    此代码比简单套接字所需的更复杂 - 使用 TCP 套接字,您可以简单地将该套接字的 fileno() 用作文件描述符,然后以相同的方式处理双方(os.read (), os.write())。但是,如果您打算将它与 SSLSocket 一起使用,则需要上面更复杂的版本,因为如果您从 SSLSocket 获取 fileno(),它预计您将处理 TLS 层以及数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多