我试图在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 。
我尝试过:
OSError: [Errno 88] Socket operation on non-socket
。此代码的示例,其中clisock是有效的套接字: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则没有区别。
screen
打开PTY时,没有明显的数据流。几乎所有的Python PTY示例都集中在pty.spawn()上,这使得很难找到这个(通常是模糊的)使用PTY的示例代码。
旁注:我之所以使用os.openpty而不是pty.openpty只是因为它似乎不是一个重点。 pty模块似乎被宣传为更具可移植性,但没有更多的可移植性,而且我在Linux上工作,因此可移植性不是问题。由于我将有效的文件描述符转换为正确的os.ttyname(),我没有理由怀疑openpty()是否正常工作。
答案 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层。作为数据。