如何在Python

时间:2018-02-14 06:43:21

标签: python python-3.x sockets pty

我试图在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()的循环将新的读取数据传递给其他套接字 write

我尝试过:

  1. 使用socket.fromfd使用其中一个PTY文件描述符构建套接字。这导致OSError: [Errno 88] Socket operation on non-socket。此代码的示例,其中clisock是有效的套接字:
  2. 
        def sockToPty(clisock,addr):
            (master, slave) = os.openpty()
            print('PTY: Opening {}'.format(os.ttyname(slave)))
            ptymsock = socket.fromfd(master, socket.AF_UNIX, socket.SOCK_STREAM)
            ptyssock = socket.fromfd(slave,  socket.AF_UNIX, socket.SOCK_STREAM)
            print('CLISOCK: {}'.format(clisock))
            print('PTYMSOCK: {}'.format(ptymsock))
            print('PTYSSOCK: {}'.format(ptyssock))
            peer[clisock] = ptymsock
            peer[ptyssock] = clisock
            while True:
                iready, oready, eready = select.select([clisock, ptyssock], [], [])
    
                for sock in iready:
                    data = sock.recv(4096)
                    if not len(data):
                        return
                    peer[sock].send(data)
    
    

    会导致此错误:

    
        PTY: Opening /dev/pts/2
        CLISOCK: 
        PTYMSOCK: 
        PTYSSOCK: 
        Exception in thread Thread-1:
        Traceback (most recent call last):
          File "/usr/lib/python3.6/threading.py", line 916, in _bootstrap_inner
            self.run()
          File "/usr/lib/python3.6/threading.py", line 864, in run
            self._target(*self._args, **self._kwargs)
          File "./tcpmux.py", line 62, in sockToPty
            peer[sock].send(data)
        OSError: [Errno 88] Socket operation on non-socket
    
    

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

    1. 使用os.dup2()将SSLsocket的fileno()映射到stdin,stdout和stderr,然后运行os.openpty()(la tcp_pty_bind.py)。这不会产生任何错误,但是当我使用screen打开PTY时,没有明显的数据流。
    2. 几乎所有的Python PTY示例都集中在pty.spawn()上,这使得很难找到这个(通常是模糊的)使用PTY的示例代码。

      旁注:我之所以使用os.openpty而不是pty.openpty只是因为它似乎不是一个重点。 pty模块似乎被宣传为更具可移植性,但没有更多的可移植性,而且我在Linux上工作,因此可移植性不是问题。由于我将有效的文件描述符转换为正确的os.ttyname(),我没有理由怀疑openpty()是否正常工作。

1 个答案:

答案 0 :(得分:0)

这可以使用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层。作为数据。