我在设置连接到“分发服务器”服务器以发送某些数据的客户端时遇到了问题.
服务器的目的是从客户端获取数据,然后将数据发送到所有连接的客户端.服务器正常运行.
主要客户也应该充当IRC机器人.
这是一个文本示例,说明其工作方式:
(IRC)约翰:你好!
1. IRC客户端收到消息,我们现在需要将其发送给分发者.
2.发行人应该得到这个“约翰:你好!”字符串并将其发送回所有连接的客户端.
3.如果其他客户端将数据发送到分发服务器,并分发给所有客户端,则IRC客户端应在输出时将接收到的数据输出到指定频道
以下代码是IRC bot客户端(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):
(这可以正常工作,但也许可能对进一步参考很有用)
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在指定的通道中输出?
欢迎任何建议和可能的解决方案.
如果我错过任何其他详细信息,请告诉我.
感谢您的时间!
解决方法:
您应该避免这样编写阻塞代码:
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实例.反应器.connectTCP调用创建一个新的连接和一个新的EchoClient实例,并将它们彼此关联.
您要使用工厂创建的EchoClient实例,而不是EchoClient().sendData(…):
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事件以清除连接属性.这意味着您可能会认为当连接已经丢失时,您正在将数据发送到分布式服务器.同样,不能保证在IRC客户端第一次尝试使用它时就已经建立了与分布式服务器的连接,因此在尝试使用f.connection.sendData时可能会出现AttributeError.
但是,解决这些问题并不需要很大的飞跃.像其他任何方法一样,修复全局变量的用法-通过将参数传递给函数,将对象另存为其他对象上的引用等.通过处理可能的AttributeError或直到创建分布式对象之后才创建IRC连接来修复可能的AttributeError.通过将属性值重置为None或其他一些标记来处理丢失的连接,并在尝试使用分布式客户端连接发送任何数据之前注意IRC代码中的这种情况.