2011-11-24 102 views
3

我在設置連接到「分銷商」服務器以發送特定數據的客戶端時遇到了問題。 服務器的目的是從客戶端獲取數據,然後將該數據發送給所有連接的客戶端。服務器沒有任何問題。主要客戶端也應該作爲IRC bot工作。 下面是它應該如何工作的文本示例:Python扭曲:函數調用不正確?

(IRC)John:你好!

1. IRC客戶端收到該消息,我們現在需要將其發送給經銷商。

2.經銷商應該得到這個「約翰:你好!」字符串並將其發回給所有連接的客戶端。

3.如果其他客戶端將數據發送到分配器,其中這將廣播到所有的客戶端,IRC客戶端應輸出在它的打開所接收的數據到一指定的通道

下面的代碼是IRC殭屍客戶端(ircbot.py):

import sys 
import socket 
import time 
import traceback 
from twisted.words.protocols import irc 
from twisted.internet import reactor 
from twisted.internet import protocol 

VERBOSE = True 
f = None 

class IRCBot(irc.IRCClient): 
    def _get_nickname(self): 
     return self.factory.nickname 
    nickname = property(_get_nickname) 

    def signedOn(self): 
     self.msg("NickServ", "id <password_removed>") # Identify the bot 
     time.sleep(0.1) # Wait a little... 
     self.join(self.factory.channel) # Join channel #chantest 
     print "Signed on as %s." % (self.nickname,) 

    def joined(self, channel): 
     print "Joined %s." % (channel,) 

    def privmsg(self, user, channel, msg): 
     name = user.split('!', 1)[0] 
     prefix = "%s: %s" % (name, msg) 
     print prefix 
     if not user: 
      return 
     if self.nickname in msg: 
      msg = re.compile(self.nickname + "[:,]* ?", re.I).sub('', msg) 
      print msg 
     else: 
      prefix = '' 
     if msg.startswith("!"): 
      if name.lower() == "longdouble": 
       self.msg(channel, "Owner command") # etc just testing stuff 
      else: 
       self.msg(channel, "Command") 
     if channel == "#testchan" and name != "BotName": 
      EchoClient().sendData('IRC:'+' '.join(map(str, [name, msg]))) 
      # This should make the bot send chat data to the distributor server (NOT IRC server) 

    def irc_NICK(self, prefix, params): 
     """Called when an IRC user changes their nickname.""" 
     old_nick = prefix.split('!')[0] 
     new_nick = params[0] 
     self.msg(, "%s is now known as %s" % (old_nick, new_nick)) 

    def alterCollidedNick(self, nickname): 
     return nickname + '1' 

class BotFactory(protocol.ClientFactory): 
    protocol = IRCBot  
    def __init__(self, channel, nickname='BotName'): 
     self.channel = channel 
     self.nickname = nickname 

    def clientConnectionLost(self, connector, reason): 
     print "Lost connection (%s), reconnecting." % (reason,) 
     connector.connect() 

    def clientConnectionFailed(self, connector, reason): 
     print "Could not connect: %s" % (reason,) 


class EchoClient(protocol.Protocol): 
    def connectionMade(self): 
     pass 

    def sendData(self, data): 
      self.transport.write(data) 

    def dataReceived(self, data): 
     if VERBOSE: 
      print "RECV:", data 
     IRC.msg("#chantest", data) 
     #This one should send the received data from the distributor to the IRC channel 

     def connectionLost(self, reason): 
      print "Connection was lost." 

class EchoFactory(protocol.ClientFactory): 
    def startedConnecting(self, connector): 
     print 'Started to connect.' 

    def buildProtocol(self, addr): 
     print 'Connected to the Distributor' 
     return EchoClient() 
    def clientConnectionFailed(self, connector, reason): 
     print "Cannot connect to distributor! Check all settings!" 
     reactor.stop() 

    def clientConnectionLost(self, connector, reason): 
     print "Distributor Lost connection!!" 
     reactor.stop() 


if __name__ == "__main__": 
    IRC = BotFactory('#chantest') 
    reactor.connectTCP('irc.rizon.net', 6667, IRC) # Our IRC connection 
    f = EchoFactory() 
    reactor.connectTCP("localhost", 8000, f) # Connection to the Distributor server 
    reactor.run() 

下面的代碼是在銷售服務器(distributor.py):

(這一個工作正常,但也許它湊LD是進一步參考有用)

from twisted.internet.protocol import Protocol, Factory 
from twisted.internet import reactor 


class MultiEcho(Protocol): 
    def __init__(self, factory): 
     self.factory = factory 

    def connectionMade(self): 
     print "Client connected:",self 
     self.factory.echoers.append(self) 
     self.factory.clients = self.factory.clients+1 
     #self.transport.write("Welcome to the server! There are currently "+`self.factory.clients`+" clients connected.") 

    def dataReceived(self, data): 
     print "RECV:",data 
     for echoer in self.factory.echoers: 
      echoer.transport.write(data) 

    def connectionLost(self, reason): 
     print "Client disconnected:",self 
     self.factory.echoers.remove(self) 
     self.factory.clients = self.factory.clients-1 

class MultiEchoFactory(Factory): 
    def __init__(self): 
     self.clients = 0 
     self.names = [] 
     self.echoers = [] 

    def buildProtocol(self, addr): 
     return MultiEcho(self) 

if __name__ == '__main__': 
    print "Running..." 
    reactor.listenTCP(8000, MultiEchoFactory()) 
    reactor.run() 

我希望客戶端輸出的所有傳入的聊天從IRC服務器的「分配器」服務器從「分配器」數據,並且還輸出輸入數據。 不過,我得到的錯誤是這樣的:

對於ircbot.py以下行,

EchoClient().sendData('IRC'+' '.join(map(str, [name, msg]))) 

我得到以下錯誤:

Joined #chantest. 
Longdouble: test 
Traceback (most recent call last): 
    File "C:\Python\lib\site-packages\twisted\internet\tcp.py", line 460, in doRea 
d 
    return self.protocol.dataReceived(data) 
    File "C:\Python\lib\site-packages\twisted\words\protocols\irc.py", line 2277, 
in dataReceived 
    basic.LineReceiver.dataReceived(self, data.replace('\r', '')) 
    File "C:\Python\lib\site-packages\twisted\protocols\basic.py", line 564, in da 
taReceived 
    why = self.lineReceived(line) 
    File "C:\Python\lib\site-packages\twisted\words\protocols\irc.py", line 2285, 
in lineReceived 
    self.handleCommand(command, prefix, params) 
--- <exception caught here> --- 
    File "C:\Python\lib\site-packages\twisted\words\protocols\irc.py", line 2329, 
in handleCommand 
    method(prefix, params) 
    File "C:\Python\lib\site-packages\twisted\words\protocols\irc.py", line 1813, 
in irc_PRIVMSG 
    self.privmsg(user, channel, message) 
    File "C:\Python\Traance\kwlbot\ircbot.py", line 51, in privmsg 
    EchoClient().sendData('IRC'+' '.join(map(str, [name, msg]))) 
    File "C:\Python\Traance\kwlbot\ircbot.py", line 90, in sendData 
    self.transport.write(data) 
exceptions.AttributeError: 'NoneType' object has no attribute 'write' 

而且同樣進入該線路同樣ircbot.py

IRC.msg("#chantest", data) 

- >

RECV: Hello from Distributor Server 
Traceback (most recent call last): 
    File "C:\Python\Traance\kwlbot\ircbot.py", line 96, in dataReceived 
    IRC.msg("#chantest", data) 
AttributeError: BotFactory instance has no attribute 'msg' 

我在做什麼錯?我如何從IRCbot類調用正確的函數使其將數據發送到分發服務器,並從分發服務器接收到的數據通過IRC在指定的通道中輸出?

歡迎任何建議和可能的解決方案。

如果我錯過了其他任何細節,請告訴我。

謝謝你的時間!

+0

我不知道它是否只是巧合,但在'sendData'中引發第一個錯誤的行沒有正確縮進。 – mac

+0

@mac:我認爲它與'sendData'沒什麼關係,但是這些函數不希望以某種方式相互「通信」,因此它們可以將接收到的數據發送到不同的服務器,而不是「活動」的服務器。 – Longdouble

回答

4

您應該避免寫這樣的阻塞代碼:

def signedOn(self): 
    self.msg("NickServ", "id <password_removed>") # Identify the bot 
    time.sleep(0.1) # Wait a little... 
    self.join(self.factory.channel) # Join channel #chantest 
    print "Signed on as %s." % (self.nickname,) 

有關詳細信息,請參閱Tail -f log on server, process data, then serve to client via twisted

除此之外,這裏的主要問題是您正嘗試發送數據而沒有連接。當你寫類似:

EchoClient().sendData('IRC'+' '.join(map(str, [name, msg]))) 

你創建一個協議實例負責處理的連接,然後嘗試使用它,但你不創建一個連接。發送數據的嘗試失敗,因爲協議尚未附加到任何傳輸。

您的片段已經演示了正確的方法來建立一個連接,其實兩次:

IRC = BotFactory('#chantest') 
reactor.connectTCP('irc.rizon.net', 6667, IRC) # Our IRC connection 
f = EchoFactory() 
reactor.connectTCP("localhost", 8000, f) # Connection to the Distributor server 

的錯誤是創建一個新的EchoClient例如,一個沒有連接。 reactor.connectTCP調用會創建一個新連接和一個新的實例並將它們相互關聯。

相反的EchoClient().sendData(...),你想用你的工廠創建的EchoClient實例:

def buildProtocol(self, addr): 
    print 'Connected to the Distributor' 
    return EchoClient() 

buildProtocol實現創建實例,唯一缺少的是讓它保存的實例,以便它可以由您的IRC機器人使用。

考慮是這樣的:那麼

def buildProtocol(self, addr): 
    print 'Connected to the Distributor' 
    self.connection = EchoClient() 
    return self.connection 

你的IRC客戶端可以使用保存EchoClient實例:

if channel == "#testchan" and name != "BotName": 
     f.connection.sendData('IRC:'+' '.join(map(str, [name, msg]))) 
     # This should make the bot send chat data to the distributor server (NOT IRC server) 

請注意,我在這裏給出了具體的代碼是一種非常原始的方法。它使用全局變量f來查找EchoFactory實例。與大多數全局變量用法一樣,這使代碼有點難以遵循。此外,我沒有添加任何代碼來處理connectionLost事件以清除connection屬性。這意味着當連接丟失時,您可能會認爲您正在向分佈式服務器發送數據。同樣,不能保證在IRC客戶端首次嘗試使用它時已經創建了到分佈式服務器的連接,因此當您嘗試使用f.connection.sendData時,您可能有一個AttributeError

但是,修復這些並不需要太多的飛躍。修改全局變量的使用方式,就像其他任何方法一樣 - 通過將參數傳遞給函數,將對象保存爲其他對象的引用等。通過處理它或修改可能的AttributeError,直到創建分佈式連接等。並且在嘗試使用分佈式客戶端連接發送任何數據之前,通過將屬性值重置爲None或其他一些標記,並在IRC代碼中關注這種情況來處理丟失的連接。

+0

謝謝!現在一切都很清楚,我確實在弄清楚後纔開始工作。 – Longdouble

0

TFM從未在您的代碼中定義,所以我不知道交易是在那裏。

另一個錯誤是您正在實例化一個客戶端,但從未將它連接到任何東西,如reactor.connectTCP(...)endpoint.connect(...)transport屬性將爲None,直到它被設置爲止。

(這對您來說是一個很簡單的代碼,這個代碼是完整的,並且不包含所有打印的日誌消息等不必要的細節,這對您很有幫助,這讓您很難看出真正的問題。 )