我正在使用 Twisted python 在自定义端口上创建一个简单的 SSH 服务器。我使用 port = reactor.listenTCP(_port, sshfactory)
创建了一个 Port 对象,其中 _port 是一个保存端口整数的变量。我在使用 port.loseConnection()
和 port.connectionLost(reason=None)
命令关闭服务器时释放了端口。如果我尝试启动服务器,停止它,然后再次启动它,我会收到标题错误“端口”对象没有属性“套接字”
编辑:完整的错误信息:
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):
停止收听示例.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.
最佳答案
listenTCP
返回一个 IListeningPort
; IListeningPort
没有 loseConnection
或 connectionLost
方法。相反,它有 stopListening
。您正在调用的那些方法的存在是一个不幸的意外。您应该尝试使用公开宣传的接口(interface),看看是否有效。
(此外,您应该发布 completely runnable bit of code ,以便我们真正了解“停止并重新启动”的含义,以及完整的回溯,而不仅仅是错误消息的片段。)
此外,不能从任意线程调用 Twisted API。此代码将引发未定义且难以预测的 Twisted 行为:
def stopServer():
global port
port.stopListening()
reactor.stop()
reactor.crash()
有几个原因。首先,
startServer
设置应用程序并在另一个线程中启动 react 器。这意味着 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
在 react 器线程中调用 port.stopListening。此外,这将阻塞,直到 Deferred
返回的 stopListening
触发。这意味着当该行完成后,该端口将不再使用。 reactor.callFromThread
在 react 器线程中调用 reactor.stop 。由于此调用旨在停止 react 器,因此我认为使用 blockingCallFromThread
并不安全,因为一旦 react 器停止,线程间通信机制可能不再起作用。此外,reactor.stop
不返回 Deferred
,因此无论如何都没有任何有用的阻止。 launchServer
返回,一旦 reactor.run()
返回它就会这样做,一旦 react 器停止,它就会这样做)。 但是,您可能还想考虑完全不以这种方式使用线程。没有特别的理由这样做,至少我无法从这个最小的例子中确定。如果您有其他一些对您来说必要的线程使用,那可能会成为另一个 SO 问题的好素材。 :)
关于python - “端口”对象没有属性 'socket',我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16379504/