2013-05-04 14 views
3

我正在使用扭曲的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版):

的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

也許我應該注意到我正在使用Twisted 9.0,因爲這是我必須要做的,以使它與我工作的舊代碼示例兼容。 – sajattack 2013-05-18 20:18:47

回答

0

解決方案竟然完全拋棄線程,並使用與GUI接口的內置方法。 tksupport.install(root)扭曲不是線程安全的。

4

listenTCP返回IListeningPort; IListeningPort沒有loseConnectionconnectionLost方法。相反,它有stopListening。你所說的那些方法的存在是一個不幸的事故。你應該嘗試使用公開發布的接口,看看是否有效。 (另外,你應該發佈一個completely runnable bit of code,這樣我們才能真正知道你的意思是「停止並重新啓動它」,以及完整的回溯,而不僅僅是錯誤消息的片段。)

此外,Twisted APIs不能從任意線程調用。此代碼會引起未定義且難以預測的Twisted行爲:

def stopServer(): 
    global port 
    port.stopListening() 
    reactor.stop() 
    reactor.crash() 

出於以下幾個原因。首先,startServer設置應用程序並在另一個線程中啓動反應器。這意味着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反應器中的線程

  • 呼叫port.stopListening。此外,這將阻止,直到由stopListening火災返回的Deferred。這意味着當該線路完成時,該端口將不再被使用。
  • 使用reactor.callFromThread調用反應器線程中的reactor.stop。由於此調用旨在停止反應堆,因此我認爲使用blockingCallFromThread並不安全,因爲一旦反應堆停止,線程間通信機制可能不再起作用。另外,reactor.stop不會返回Deferred,因此無論如何沒有什麼用的。
  • 等待反應堆停止運行,方法是加入正在運行的線程(它將阻塞,直到launchServer返回,一旦reactor.run()返回,它將會立即停止運行)。

但是,您可能還想考慮不以這種方式使用線程。沒有特別的理由這樣做,至少我從這個最小的例子中無法確定。如果你有其他一些對你來說似乎是必要的線程,那可能會成爲另一個SO問題的好材料。 :)

+0

我覺得我的代碼有點太長太亂了。停止並重新啓動它意味着運行我用來關閉端口的兩種方法,並運行方法來偵聽端口。 – sajattack 2013-05-05 05:28:59

+0

剛試過stopListening()。我想我之前使用過它,並放棄它,因爲它不起作用。當我在關閉它後再試聽端口時,它說該地址已被使用。 – sajattack 2013-05-05 05:31:20

+0

請附上使用'stopListening'的完整代碼示例,以及該代碼的回溯。試圖根據您的描述重新構建,它對我來說工作正常。 – Glyph 2013-05-05 20:31:51