我正在使用twisted python在自定义端口上创建一个简单的SSH服务器。我用port = reactor.listenTCP(_port, sshfactory)
创建一个Port对象,其中_port是一个保存端口整数的变量。使用命令port.loseConnection()
和port.connectionLost(reason=None)
关闭服务器时,我释放了端口。如果我尝试启动服务器,停止它并再次启动它,我得到名义错误'Port'对象没有属性'socket'
编辑:完整错误消息:
Unhandled error in Deferred:
Traceback (most recent call last):
File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 1175, in mainLoop
self.runUntilCurrent()
File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 779, in runUntilCurrent
call.func(*call.args, **call.kw)
File "C:\Python27\lib\site-packages\twisted\internet\defer.py", line 238, in callback
self._startRunCallbacks(result)
File "C:\Python27\lib\site-packages\twisted\internet\defer.py", line 307, in _startRunCallbacks
self._runCallbacks()
--- <exception caught here> ---
File "C:\Python27\lib\site-packages\twisted\internet\defer.py", line 323, in _runCallbacks
self.result = callback(self.result, *args, **kw)
File "C:\Python27\lib\site-packages\twisted\internet\task.py", line 736, in <lambda>
d.addCallback(lambda ignored: callable(*args, **kw))
File "C:\Python27\lib\site-packages\twisted\internet\tcp.py", line 981, in connectionLost
self._closeSocket()
File "C:\Python27\lib\site-packages\twisted\internet\tcp.py", line 92, in _closeSocket
skt = self.socket
exceptions.AttributeError: 'Port' object has no attribute 'socket'
Edit2:stopListening代码示例(Python 2.7):
stopListening sample.py
from twisted.cred import portal, checkers, credentials
from twisted.conch import error, avatar, recvline, interfaces as conchinterfaces
from twisted.conch.ssh import factory, userauth, connection, keys, session, common
from twisted.conch.insults import insults
from twisted.application import service, internet
from twisted.internet import reactor, protocol
from zope.interface import implements
import threading
import os
class SSHDemoProtocol(recvline.HistoricRecvLine):
def __init__(self, user):
self.user = user
def connectionMade(self):
recvline.HistoricRecvLine.connectionMade(self)
self.showPrompt()
def showPrompt(self):
self.terminal.write("$ ")
class SSHDemoRealm:
implements(portal.IRealm)
def requestAvatar(self, avatarId, mind, *interfaces):
if conchinterfaces.IConchUser in interfaces:
return interfaces[0], SSHDemoAvatar(avatarId), lambda: None
else:
raise Exception("No supported interfaces found.")
def getRSAKeys():
if not (os.path.exists('public.key') and os.path.exists('private.key')):
# generate a RSA keypair
print("Generating RSA keypair...")
from Crypto.PublicKey import RSA
KEY_LENGTH = 1024
rsaKey = RSA.generate(KEY_LENGTH, common.entropy.get_bytes)
publicKeyString = keys.makePublicKeyString(rsaKey)
privateKeyString = keys.makePrivateKeyString(rsaKey)
# save keys for next time
file('public.key', 'w+b').write(publicKeyString)
file('private.key', 'w+b').write(privateKeyString)
print("done.")
else:
publicKeyString = file('public.key').read()
privateKeyString = file('private.key').read()
return publicKeyString, privateKeyString
def launchServer():
_port = 4564
password = 'password'
sshFactory = factory.SSHFactory()
sshFactory.portal = portal.Portal(SSHDemoRealm())
users = {'user': password}
sshFactory.portal.registerChecker(
checkers.InMemoryUsernamePasswordDatabaseDontUse(**users))
pubKeyString, privKeyString = getRSAKeys()
sshFactory.publicKeys = {
'ssh-rsa': keys.getPublicKeyString(data=pubKeyString)}
sshFactory.privateKeys = {
'ssh-rsa': keys.getPrivateKeyObject(data=privKeyString)}
global port
port = reactor.listenTCP(_port, sshFactory)
reactor.addSystemEventTrigger('before', 'shutdown', stopServer)
reactor.run(installSignalHandlers=False)
def startServer():
thread = threading.Thread(target=launchServer)
thread.start()
def stopServer():
global port
port.stopListening()
reactor.stop()
reactor.crash()
startServer()
stopServer()
startServer()
回溯:
>>> Exception in thread Thread-2:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 552, in __bootstrap_inner
self.run()
File "/usr/lib/python2.7/threading.py", line 505, in run
self.__target(*self.__args, **self.__kwargs)
File "/home/paul/Desktop/Down2Home/stopListening sample.py", line 62, in launchServer
port = reactor.listenTCP(_port, sshFactory)
File "/usr/local/lib/python2.7/dist-packages/Twisted-9.0.0-py2.7-linux-x86_64.egg/twisted/internet/posixbase.py", line 355, in listenTCP
p.startListening()
File "/usr/local/lib/python2.7/dist-packages/Twisted-9.0.0-py2.7-linux-x86_64.egg/twisted/internet/tcp.py", line 855, in startListening
raise CannotListenError, (self.interface, self.port, le)
CannotListenError: Couldn't listen on any:4564: [Errno 98] Address already in use.
答案 0 :(得分:4)
listenTCP
返回IListeningPort
; IListeningPort
没有loseConnection
或connectionLost
方法。相反,它有stopListening
。你所称的那些方法的存在是一个不幸的事故。您应该尝试使用公开广告的界面,看看它是否有效。
(另外,你应该发布一个completely runnable bit of code,这样我们才能真正知道你的意思是“停止并重新开始”,以及一个完整的追溯,而不仅仅是一个错误信息片段。)
此外,可能无法从任意线程调用Twisted API。此代码将引发Twisted的未定义和难以预测的行为:
def stopServer():
global port
port.stopListening()
reactor.stop()
reactor.crash()
有几个原因。首先,startServer
设置应用程序并在另一个线程中启动reactor。这意味着不允许使用port.stopListening()
,因为它是在错误的线程中调用的Twisted API。其次,reactor.crash()
实际上只是一个测试助手,即使在那个领域,也强烈建议不要使用它,因为自reactor.crash()
发明以来已经开发了更好的测试技术。
你可能会逃避这样的事情来解决这些问题:
from twisted.internet.threads import blockingCallFromThread
def startServer():
global thread
thread = threading.Thread(target=launchServer)
thread.start()
def stopServer():
global port, thread
blockingCallFromThread(reactor, port.stopListening)
reactor.callFromThread(reactor.stop)
thread.join()
thread = None
当然,全局变量的使用并不理想,但我坚持使用它们来保持代码接近原始代码。
这是做什么的:
blockingCallFromThread
在reactor线程中调用port.stopListening。此外,这将阻止Deferred
stopListening
返回reactor.callFromThread
。这意味着当该行完成后,该端口将不再使用。blockingCallFromThread
在reactor线程中调用reactor.stop。由于此调用旨在停止反应堆,因此我认为使用reactor.stop
是不安全的,因为一旦反应器停止,线程间通信机制可能不再起作用。此外,Deferred
不会返回launchServer
,因此无论如何都无法阻止。reactor.run()
返回,这将在{{1}}返回时立即执行,一旦反应堆停止)。但是,您可能还想考虑不以这种方式使用线程。没有特别的理由这样做,至少我无法从这个最小的例子中确定。如果您对其他一些线程的使用似乎是必要的,那么这可能会成为另一个SO问题的良好素材。 :)
答案 1 :(得分:0)
解决方案结果是完全放弃了线程,并使用内置的GUI接口方法。 tksupport.install(root)
Twisted不是线程安全的。