2013-05-04 3 views
3

Я использую витой python для создания простого SSH-сервера на пользовательском порту. Я создаю объект Port с port = reactor.listenTCP(_port, sshfactory), где _port - это переменная, которая содержит целое число порта. Я освобождаю порт при выключении сервера с командами port.loseConnection() и port.connectionLost(reason=None). Если я пытаюсь запустить сервер, остановите его и запустить его снова, я получаю титульную ошибку объекта «порт» не имеет атрибута «гнезда»Объект «Порт» не имеет атрибута «socket»

Edit: полное сообщение об ошибке:

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

ответ

0

Решение оказалось полностью канавкой потоков и использованием встроенного метода сопряжения с графическим интерфейсом. tksupport.install(root) Twisted не является потокобезопасным.

4

listenTCP возвращает IListeningPort; IListeningPort не имеет loseConnection или connectionLost методов. Вместо этого он имеет stopListening. Присутствие тех методов, которые вы вызываете, - несчастный случай. Вы должны попробовать использовать общедоступные интерфейсы и посмотреть, работает ли это.

(Кроме того, вы должны опубликовать completely runnable bit of code, так что мы на самом деле знаем, что вы имеете в виду под «остановить и запустить его снова», а также полный TRACEBACK, а не только фрагмент сообщения об ошибке.)

Кроме того, Twisted API не могут быть вызваны из произвольных потоков. Этот код спровоцирует неопределенное и трудно предсказать поведение от 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 

Конечно, использование глобалов не является идеальным, но я придерживаюсь с ними здесь, чтобы сохранить код близко к ваш оригинал.

Что это делает:

  • Вызов port.stopListening в реакторном потоке с использованием blockingCallFromThread. Кроме того, это будет заблокировано до тех пор, пока Deferred не вернется к stopListening пожарам. Это означает, что когда эта линия будет выполнена, порт больше не будет использоваться.
  • Вызовите реактор.стоп в реакторной нити, используя reactor.callFromThread. Поскольку этот вызов предназначен для остановки реактора, я не думаю, что безопасно использовать blockingCallFromThread, так как после остановки реактора механизм межпоточной связи может перестать работать. Кроме того, reactor.stop не возвращает Deferred, так что в любом случае ничего не стоит блокировать.
  • Подождите, пока реактор не остановится, соединяя резьбу, в которой он работает (который будет заблокирован до тех пор, пока не вернется launchServer, что будет сделано, как только reactor.run() вернется, что будет после остановки реактора).

Однако вы также можете рассмотреть возможность использования нитей таким образом. Нет особых оснований для этого, по крайней мере, я не могу определить из этого минимального примера. Если у вас есть какое-то другое использование потоков, которое вам кажется необходимым, это может стать хорошим кормом для другого вопроса SO. :)

+0

Я понял, что мой код был слишком длинным и беспорядочным. Остановите его и запустите его снова в основном означает запуск двух методов, которые я использовал, чтобы закрыть порт, и запустить методы для прослушивания на порту. – sajattack

+0

Только что попробовал stopListening(). Я думаю, что я использовал это раньше и отказался от него, потому что он не работает. Когда я пытаюсь снова прослушать порт после его закрытия, он говорит, что адрес уже используется. – sajattack

+0

Пожалуйста, приложите полный образец кода, который использует 'stopListening', а также трассировку из этого кода. Попытка перестроить на основе вашего описания, он отлично работает для меня. – Glyph

Смежные вопросы