'port'对象没有属性'socket'

时间:2013-05-04 22:21:06

标签: python ssh twisted

我正在使用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.

2 个答案:

答案 0 :(得分:4)

listenTCP返回IListeningPort; IListeningPort没有loseConnectionconnectionLost方法。相反,它有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不是线程安全的。