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

时间:2014-06-13 14:07:06

标签: python multithreading sockets python-3.x

我有一个IRC客户端,可以在套接字上接收消息。

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

每个机器人都在一个单独的线程中创建,该线程获取通道名称以及一些其他参数。

我的问题是我的IRC套接字只能绑定到一个端口,它处理所有IRC消息,每条消息都有一个#channel字符串作为第三个字符串,将其指向特定通道。这些消息可以在每个机器人内部处理,因为每个机器人都知道其频道的名称。

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

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.")

我需要以某种方式将readbuffer从主循环中获取到在单独的线程中创建的每个IRCBetBot对象中?有什么想法吗?

2 个答案:

答案 0 :(得分:1)

这是一个示例,说明如何使用每个线程的队列来执行此操作。我们不是仅仅创建一个线程列表,而是创建一个以通道为键的线程的dict,并存储线程对象和可用于与dict中的线程通信的队列。

#!/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

答案 1 :(得分:0)

一种方法是使一个类似于线程数组的readBuffers数组。然后每个线程基本上都在等待它的特定readbuffer上的数据。

当您获取数据时,您可以将其传递给您感兴趣的线程,或者只是将数据复制到所有读取缓冲区,并让线程处理它们,如果他们感兴趣的话。在这种情况下,观察者模式最有效。