[英]How make a twisted python client with readline functionality
我正在嘗試使用Python Twisted為簡單的TCP服務器編寫客戶端。 當然我對Python很新,剛剛開始關注Twisted,所以我可能做錯了。
服務器很簡單,你打算使用nc或telnet。 沒有身份驗證。 您只需連接並獲得一個簡單的控制台。 我想寫一個添加了一些readline功能的客戶端(歷史記錄和電子郵件,比如ctrl-a / ctrl-e就是我所追求的)
下面是我編寫的代碼,與在命令行中使用netcat一樣好,就像這個nc localhost 4118
from twisted.internet import reactor, protocol, stdio
from twisted.protocols import basic
from sys import stdout
host='localhost'
port=4118
console_delimiter='\n'
class MyConsoleClient(protocol.Protocol):
def dataReceived(self, data):
stdout.write(data)
stdout.flush()
def sendData(self,data):
self.transport.write(data+console_delimiter)
class MyConsoleClientFactory(protocol.ClientFactory):
def startedConnecting(self,connector):
print 'Starting connection to console.'
def buildProtocol(self, addr):
print 'Connected to console!'
self.client = MyConsoleClient()
self.client.name = 'console'
return self.client
def clientConnectionFailed(self, connector, reason):
print 'Connection failed with reason:', reason
class Console(basic.LineReceiver):
factory = None
delimiter = console_delimiter
def __init__(self,factory):
self.factory = factory
def lineReceived(self,line):
if line == 'quit':
self.quit()
else:
self.factory.client.sendData(line)
def quit(self):
reactor.stop()
def main():
factory = MyConsoleClientFactory()
stdio.StandardIO(Console(factory))
reactor.connectTCP(host,port,factory)
reactor.run()
if __name__ == '__main__':
main()
輸出:
$ python ./console-console-client.py
Starting connection to console.
Connected to console!
console> version
d305dfcd8fc23dc6674a1d18567a3b4e8383d70e
console> number-events
338
console> quit
我看了看
這真的不適合我。 示例代碼工作得很好但是當我介紹網絡時,我似乎與stdio有競爭條件。 這個較舊的鏈接似乎提倡類似的方法(在單獨的線程中運行readline)但我沒有做到這一點。
我也研究了扭曲的海螺侮辱,但除了演示示例之外,我沒有任何運氣。
制作基於終端的客戶端以提供readline支持的最佳方法是什么?
http://twistedmatrix.com/documents/current/api/twisted.conch.stdio.html
看起來很有希望,但我很困惑如何使用它。
http://twistedmatrix.com/documents/current/api/twisted.conch.recvline.HistoricRecvLine.html
也似乎提供了對例如處理向上和向下箭頭的支持,但我無法切換控制台繼承自HistoricRecVLine而不是LineReceiver來運行。
也許扭曲是錯誤的框架使用或我應該使用所有海螺類。 我只是喜歡它的事件驅動風格。 是否有更好/更容易的方法在扭曲的客戶端中使用readline或readline作為支持?
我通過不使用Twisted框架來解決這個問題。 這是一個很棒的框架,但我認為這是一個錯誤的工具。 相反,我使用了telnetlib
, cmd
和readline
模塊。
我的服務器是異步的,但這並不意味着我的客戶端需要這樣,所以我使用telnetlib
進行與服務器的通信。 這使得很容易地創建一個ConsoleClient
類的子類cmd.Cmd
並獲得歷史和emacs般的快捷方式。
#! /usr/bin/env python
import telnetlib
import readline
import os
import sys
import atexit
import cmd
import string
HOST='127.0.0.1'
PORT='4118'
CONSOLE_PROMPT='console> '
class ConsoleClient(cmd.Cmd):
"""Simple Console Client in Python. This allows for readline functionality."""
def connect_to_console(self):
"""Can throw an IOError if telnet connection fails."""
self.console = telnetlib.Telnet(HOST,PORT)
sys.stdout.write(self.read_from_console())
sys.stdout.flush()
def read_from_console(self):
"""Read from console until prompt is found (no more data to read)
Will throw EOFError if the console is closed.
"""
read_data = self.console.read_until(CONSOLE_PROMPT)
return self.strip_console_prompt(read_data)
def strip_console_prompt(self,data_received):
"""Strip out the console prompt if present"""
if data_received.startswith(CONSOLE_PROMPT):
return data_received.partition(CONSOLE_PROMPT)[2]
else:
#The banner case when you first connect
if data_received.endswith(CONSOLE_PROMPT):
return data_received.partition(CONSOLE_PROMPT)[0]
else:
return data_received
def run_console_command(self,line):
self.write_to_console(line + '\n')
data_recved = self.read_from_console()
sys.stdout.write(self.strip_console_prompt(data_recved))
sys.stdout.flush()
def write_to_console(self,line):
"""Write data to the console"""
self.console.write(line)
sys.stdout.flush()
def do_EOF(self, line):
try:
self.console.write("quit\n")
self.console.close()
except IOError:
pass
return True
def do_help(self,line):
"""The server already has it's own help command. Use that"""
self.run_console_command("help\n")
def do_quit(self, line):
return self.do_EOF(line)
def default(self, line):
"""Allow a command to be sent to the console."""
self.run_console_command(line)
def emptyline(self):
"""Don't send anything to console on empty line."""
pass
def main():
histfile = os.path.join(os.environ['HOME'], '.consolehistory')
try:
readline.read_history_file(histfile)
except IOError:
pass
atexit.register(readline.write_history_file, histfile)
try:
console_client = ConsoleClient()
console_client.prompt = CONSOLE_PROMPT
console_client.connect_to_console()
doQuit = False;
while doQuit != True:
try:
console_client.cmdloop()
doQuit = True;
except KeyboardInterrupt:
#Allow for ^C (Ctrl-c)
sys.stdout.write('\n')
except IOError as e:
print "I/O error({0}): {1}".format(e.errno, e.strerror)
except EOFError:
pass
if __name__ == '__main__':
main()
我做的一個更改是刪除從服務器返回的提示並使用Cmd.prompt
顯示給用戶。 我的理由是支持Ctrl-c更像shell。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.