将字符串消息发送到多个线程

1 投票
2 回答
2732 浏览
提问于 2025-04-18 09:41

我有一个IRC客户端,它通过一个套接字接收消息。

从这个客户端,我创建了几个机器人,这些机器人可以连接到其他人的Twitch聊天频道。(这些机器人是经过授权的,不是垃圾邮件机器人!)

每个机器人都是在一个单独的线程中创建的,它会接收频道名称和其他一些参数。

我遇到的问题是,我的IRC套接字只能绑定到一个端口,这个端口处理所有的IRC消息。每条消息的第三个单词是一个#频道字符串,用来指向特定的频道。每个机器人都可以处理这些消息,因为它们知道自己对应的频道名称。

我的问题是;我该如何将通过套接字接收到的字符串发送到多个线程呢?

import time
import socket
import threading
import string
import sys
import os

class IRCBetBot:
    #irc ref
    irc = None

    def __init__(self,IRCRef,playerName,channelName,currencyName):

        #assign variables
        self.irc = IRCRef
        self.channel = '#' + channelName

        self.irc.send(('JOIN ' + self.channel + '\r\n') .encode("utf8"))

        #create readbuffer to hold strings from IRC
        readbuffer = ""

        # This is the main loop
        while 1:

            ##readbuffer## <- need to send message from IRC to this variable 

            for line in temp:
                line=str.rstrip(line)
                line=str.split(line)

                if (len(line) >= 4) and ("PRIVMSG" == line[1]) and (self.channel == line[2]) and not ("jtv" in line[0]):
                    #call function to handle user message
                if(line[0]=="PING"):
                    self.irc.send(("PONG %s\r\n" % line[0]).encode("utf8"))




def runAsThread(ircref,userName, channelName, currencyPrefix):
    print("Got to runAsThread with : " + str(userName) + " " + str(channelName) + " " + str(currencyPrefix))
    IRCBetBot(ircref,userName,channelName,currencyPrefix)

# Here we create the IRC connection
#IRC connection variables
nick = 'mybot'                  #alter this value with the username used to connect to IRC eg: "username".
password = "oauth:mykey"        #alter this value with the password used to connect to IRC from the username above.
server = 'irc.twitch.tv'
port = 6667

#create IRC socket
irc = socket.socket()

irc.connect((server, port))

#sends variables for connection to twitch chat
irc.send(('PASS ' + password + '\r\n').encode("utf8"))
irc.send(('USER ' + nick + '\r\n').encode("utf8"))
irc.send(('NICK ' + nick + '\r\n').encode("utf8"))

# Array to hold all the new threads 
threads = [] 
# authorised Channels loaded from file in real program
authorisedChannels = [["user1","#channel1","coin1"],["user2","#channel2","coin2"],["user3","#channel3","coin3"]]

for item in authorisedChannels:
    try:
        userName = item[0]
        channelName = item[1]
        currencyPrefix = item [2]
        myTuple = (irc,userName,channelName,currencyPrefix)
        thread = threading.Thread(target=runAsThread,args = myTuple,)
        thread.start()
        threads.append(thread)
        time.sleep(5) # wait to avoid too many connections to IRC at once from same IP
    except Exception as e:
        print("An error occurred while creating threads.")
        print(str(e))

#create readbuffer to hold strings from IRC
readbuffer = ""

# This is the main loop
while 1:
    readbuffer= readbuffer+self.irc.recv(1024).decode("utf-8")
    temp=str.split(readbuffer, "\n")
    readbuffer=temp.pop( )
    #
    #Need to send readbuffer to each IRCBetBot() created in runAsThread that contains a while 1: loop to listen for strings in its __init__() method.
    #   

print ("Waiting...")

for thread in threads:
    thread.join()

print ("Complete.")

我需要想办法把主循环中的读取缓冲区传递到每个在不同线程中创建的IRCBetBot对象里?有什么想法吗?

2 个回答

0

一种方法是创建一个读取缓冲区的数组,类似于线程的数组。然后,每个线程基本上都在等待它自己特定的读取缓冲区中的数据。

当你收到数据时,可以把它传递给你感兴趣的线程,或者把数据复制到所有的读取缓冲区,让那些感兴趣的线程来处理。使用观察者模式在这种情况下效果最好。

3

这里有一个例子,展示了如何为每个线程使用一个队列。我们不是简单地创建一个线程列表,而是创建一个字典,字典的键是频道名,值则是线程对象和一个可以用来与线程沟通的队列。

#!/usr/bin/python3

import threading
from queue import Queue


class IRCBetBot(threading.Thread):
    def __init__(self, q, playerName, channelName, currencyName):
        super().__init__()
        self.channel = channelName
        self.playerName = playerName
        self.currencyName = currencyName
        self.queue = q 

    def run(self):
        readbuffer = ""
        while 1:
            readbuffer = self.queue.get()  # This will block until a message is sent to the queue.
            print("{} got msg {}".format(self.channel, readbuffer))

if __name__ == "__main__":

    authorisedChannels = [["user1","#channel1","coin1"],
                          ["user2","#channel2","coin2"],
                          ["user3","#channel3","coin3"]]

threads = {}
for item in authorisedChannels:
    try:
        userName = item[0]
        channelName = item[1]
        currencyPrefix = item [2]
        myTuple = (userName,channelName,currencyPrefix)
        q = Queue() 
        thread = IRCBetBot(q, *myTuple )
        thread.start()
        threads[channelName] = (q, thread)
    except Exception as e:
        print("An error occurred while creating threads.")
        print(str(e))

while 1:
    a = input("Input your message (channel: msg): ")
    channel, msg = a.split(":")
    threads[channel][0].put(msg)  # Sends a message using the queue object

如你所见,当消息通过套接字进入时,我们提取出频道名(这部分你的代码已经做了),然后把消息传递给我们线程字典中对应的队列。

下面是一个示例输出(稍微调整了一下,以避免由于同时调用print而导致输出混乱):

dan@dantop:~$ ./test.py 
Input your message (channel: msg): #channel1: hi there
#channel1 got msg  hi there
Input your message (channel: msg): #channel2: another one
#channel2 got msg  another one

撰写回答