我试图在Python中重新实现netcat:
#!/usr/bin/env python2
from sys import stdin, stdout
from twisted.internet import reactor
from twisted.internet.protocol import Protocol
from twisted.internet.endpoints import TCP4ClientEndpoint, connectProtocol
class NcClient(Protocol):
def dataReceived(self, data):
stdout.write(data)
def sendData(self, data):
self.transport.write(data)
self.transport.write("\n")
client = NcClient()
def cmdloop():
while True:
line = stdin.readline()
if line == "":
break
else:
client.sendData(line)
if reactor.running:
reactor.stop()
point = TCP4ClientEndpoint(reactor, "localhost", 6004)
connectProtocol(point, client)
reactor.callInThread(cmdloop)
reactor.run()当cmdloop检测到输入结束时,它调用reactor.stop。据我所知,reactor.stop将关机事件发送给Twisted管理的所有事物,如线程、连接等。响应这些事件,连接被关闭,线程等待它们的过程完成等等。因此,当调用reactor.stop()时,到localhost的连接:6004应该关闭,程序应该退出。
但是,这种情况不会立即发生,而只有当NcClient收到来自服务器的消息时才会发生。就好像它正在阻塞地读取这些消息一样,只在一个循环中,并且只有当它收到一个消息时,它才会继续处理关闭请求。
如何在收到消息之前关闭它?我知道reactor.crash(),但还有更礼貌的选择吗?
发布于 2015-12-10 02:02:16
您的主要问题是cmdloop运行在一个非反应堆线程中,但是它调用的是callFromThread以外的反应堆方法(具体来说是:transport.write通过client.sendData)。The 这方面的文档是非常清楚的。
除非另有说明,Twisted中的方法只能从反应堆线程调用。Twisted中很少有东西是线程安全的。
幸运的是,实现等价的netcat根本不需要线程处理。您可以简单地使用Twisted对标准I/O的内置支持作为数据源。下面是一个示例版本:
import sys
from twisted.internet import reactor
from twisted.internet.protocol import Protocol, Factory
from twisted.internet.endpoints import clientFromString
from twisted.internet.stdio import StandardIO
class NcClient(Protocol):
def __init__(self, forwardTo):
self.forwardTo = forwardTo
def connectionMade(self):
self.transport.registerProducer(self.forwardTo.transport, True)
self.forwardTo.transport.resumeProducing()
self.forwardTo.transport.registerProducer(self.transport, True)
def dataReceived(self, data):
self.forwardTo.transport.write(data)
def connectionLost(self, reason):
reactor.stop()
class StdIo(Protocol):
def connectionMade(self):
self.transport.pauseProducing()
f4p = Factory.forProtocol(lambda: NcClient(self))
d = endpoint.connect(f4p)
@d.addCallback
def connected(proto):
self.client = proto
def dataReceived(self, data):
self.client.transport.write(data)
def connectionLost(self, reason):
reactor.stop()
endpoint = clientFromString(reactor, sys.argv[1])
output = StandardIO(proto=StdIo(), reactor=reactor)
reactor.run()如果出于某种原因仍然希望使用线程,可以修改NcClient以使用callFromThread来调用sendData,而不是直接调用它。
https://stackoverflow.com/questions/33692874
复制相似问题