参照 RFC854、RFC855 文档,设计一个 TELNET 终端程序。
telnet协议详解
import selectors
import socket
import sys
__all__ = ["Telnet"]
DEBUGLEVEL = 0
TELNET_PORT = 23
# Telnet protocol characters (don't change)
IAC = bytes([255])
DONT = bytes([254])
DO = bytes([253])
WONT = bytes([252])
WILL = bytes([251])
theNULL = bytes([0])
SE = bytes([240])
SB = bytes([250])
# Telnet protocol options code (don't change)
ECHO = bytes([1]) # echo
NOOPT = bytes([0])
if hasattr(selectors, 'PollSelector'):
_TelnetSelector = selectors.PollSelector
else:
_TelnetSelector = selectors.SelectSelector
class Telnet:
def __init__(self, host=None, port=0,
timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
self.host = host
self.port = port
self.timeout = timeout
self.sock = None
self.rawq = b''
self.irawq = 0
self.cookedq = b''
self.eof = 0
self.iacseq = b'' # Buffer for IAC
self.sb = 0 # flag for SB and SE
self.sbdataq = b''
self.option_callback = None
if host is not None:
self.open(host, port, timeout)
def open(self, host, port=0, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
self.eof = 0
if not port:
port = TELNET_PORT
self.host = host
self.port = port
self.timeout = timeout
self.sock = socket.create_connection((host, port), timeout)
def __del__(self):
self.close()
def close(self):
sock = self.sock
self.sock = None
self.eof = True
self.iacseq = b''
self.sb = 0
if sock:
sock.close()
def get_socket(self):
return self.sock
def fileno(self):
return self.sock.fileno()
def write(self, buffer):
if IAC in buffer:
buffer = buffer.replace(IAC, IAC + IAC)
self.sock.sendall(buffer)
def read_eager(self):
self.process_rawq()
while not self.cookedq and not self.eof and self.sock_avail():
self.fill_rawq()
self.process_rawq()
return self.read_very_lazy()
def read_very_lazy(self):
buf = self.cookedq
self.cookedq = b''
if not buf and self.eof and not self.rawq:
raise EOFError('telnet connection closed')
return buf
def process_rawq(self):
buf = [b'', b'']
try:
while self.rawq:
c = self.rawq_getchar()
if not self.iacseq:
if c == theNULL:
continue
if c == b"\021":
continue
if c != IAC:
buf[self.sb] = buf[self.sb] + c
continue
else:
self.iacseq += c
elif len(self.iacseq) == 1:
# 'IAC: IAC CMD [OPTION only for WILL/WONT/DO/DONT]'
if c in (DO, DONT, WILL, WONT):
self.iacseq += c
continue
self.iacseq = b''
elif len(self.iacseq) == 2:
cmd = self.iacseq[1:2]
self.iacseq = b''
opt = c
if cmd in (DO, DONT):
self.sock.sendall(IAC + WONT + opt)
elif cmd in (WILL, WONT):
self.sock.sendall(IAC + DONT + opt)
except EOFError: # raised by self.rawq_getchar()
self.iacseq = b'' # Reset on EOF
self.sb = 0
pass
self.cookedq = self.cookedq + buf[0]
self.sbdataq = self.sbdataq + buf[1]
def rawq_getchar(self):
if not self.rawq:
self.fill_rawq()
if self.eof:
raise EOFError
c = self.rawq[self.irawq:self.irawq + 1]
self.irawq = self.irawq + 1
if self.irawq >= len(self.rawq):
self.rawq = b''
self.irawq = 0
return c
def fill_rawq(self):
if self.irawq >= len(self.rawq):
self.rawq = b''
self.irawq = 0
buf = self.sock.recv(50)
# self.msg("recv %r", buf)
self.eof = (not buf)
self.rawq = self.rawq + buf
def sock_avail(self):
with _TelnetSelector() as selector:
selector.register(self, selectors.EVENT_READ)
return bool(selector.select(0))
def mt_interact(self):
import _thread
_thread.start_new_thread(self.listener, ())
while 1:
line = sys.stdin.readline()
if not line:
break
self.write(line.encode('ascii'))
def listener(self):
while 1:
try:
data = self.read_eager()
except EOFError:
print('*** Connection closed by remote host ***')
return
if data:
sys.stdout.write(data.decode('ascii'))
else:
sys.stdout.flush()
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.close()
def test():
host = '******' # 远程服务器centos的ip 远程服务器需要先安装telnet服务并且运行
port = 23
with Telnet() as tn:
tn.open(host, port, timeout=0.5)
tn.mt_interact()
if __name__ == '__main__':
test()