我正在使用 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 没有 loseConnectionconnectionLost 方法。相反,它有 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,因此无论如何都没有任何有用的阻止。
  • 通过加入正在运行的线程来等待 react 器停止运行(这将阻塞直到 launchServer 返回,一旦 reactor.run() 返回它就会这样做,一旦 react 器停止,它就会这样做)。

  • 但是,您可能还想考虑完全不以这种方式使用线程。没有特别的理由这样做,至少我无法从这个最小的例子中确定。如果您有其他一些对您来说必要的线程使用,那可能会成为另一个 SO 问题的好素材。 :)

    关于python - “端口”对象没有属性 'socket',我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16379504/

    10-14 08:15