线程UDP侦听器功能

时间:2013-01-22 15:22:26

标签: python multithreading gtk udp

我对Python很陌生,所以如果我的问题是业余的,我会道歉。

我正在尝试创建一个简单的程序,允许用户查看正在广播的UDP消息。为了使程序不锁定,我当然需要创建一个单独的线程来监听广播。

在我尝试实现线程之前,该程序运行良好。它只是等待UDP消息,并在收到消息时在TextView中显示它们。当然,这锁定了主线程。

这是我的代码:(是的,我知道我的代码有几个不相关的问题,但请忽略它们。例如,我从不关闭socket。我稍后会解决这些问题。)

import socket
import select
import sys
import threading

try:
    import pygtk
    pygtk.require("2.0")
except:
    pass
try:
    import gtk
except:
    print("GTK not available!")
    sys.exit(1)


class GUI:
    def __init__(self):
        self.builder = gtk.Builder()
        self.builder.add_from_file("UDPListener.ui")
        dic = {"on_mainWindow_destroy" : self.quit,
             "on_listenButton_clicked" : self.startThread,
         "on_stopListenButton_clicked" : self.stopListening}
        self.builder.connect_signals(dic)

        self.listenerThread = threading.Thread(target = self.listen)

    def startThread(self, widget):
        self.listenerThread.start()

    def listen(self):
        bufferSize = 1024                                                          
        IPAddress = "0.0.0.0"
        IPAddress: 0.0.0.0
        portNumber = 50000
        udpClient = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        textView = self.builder.get_object("messagesReceivedEntry").get_buffer()
        textView.text = textView.get_text(textView.get_start_iter(),textView.get_end_iter())

        textView.set_text("Listening...")

        try:
            udpClient.bind((IPAddress, portNumber))
            udpClient.setblocking(0)
        except:
            print "Cannot connect."

        try:
            while True:
                result = select.select([udpClient],[],[])
                message = result[0][0].recv(bufferSize)
                print message
        except:
            print "Cannot receive message"

    def quit(self, widget):
        sys.exit(0)

    def stopListening(self, widget):
        pass

gui = GUI()
gtk.main() 

代码将运行并且一切运行正常,但它从不打印任何消息(我自己广播)。


更新

感谢J.F. Sebastian的回答,我修改了我的代码以包含以下内容:

import socket
import select
import sys
import threading
import gobject

try:
    import pygtk
    pygtk.require("2.0")
except:
    pass
try:
    import gtk
except:
    print("GTK not available!")
    sys.exit(1)

gobject.threads_init()

class GUI:
    def __init__(self, UDPClient):
        self.udpClient = UDPClient

        self.builder = gtk.Builder()
        self.builder.add_from_file("TestUDPListenerREORG.ui")
        dic = {"on_mainWindow_destroy" : self.quit,
             "on_listenButton_clicked" : self.startThread,
         "on_stopListenButton_clicked" : self.stopListening}
        self.builder.connect_signals(dic)

        self.textView = self.builder.get_object("messagesReceivedEntry").get_buffer()
        self.textViewText = self.textView.get_text(self.textView.get_start_iter(),self.textView.get_end_iter())

        self.listening = False
        self.listenerThread = threading.Thread(target = self.listen)

    def startThread(self, widget):
        self.listenerThread.start()
        pass

    def listen(self):
        try:
            self.udpClient.connect()
        except:
            print "Cannot connect."
        try:
            while True:
                result = select.select([self.udpClient.client],[],[])
                message = result[0][0].recv(1024)
                print message
                gobject.idle_add(self.updateGUI, message)
        except:
            print "Cannot receive message"

    def updateGUI(self, message):
        print "updating..."
        self.textView.set_text(self.textViewText + "\n" + message)
        print message

    def quit(self, widget):
        self.udpClient.close()
        sys.exit(0)

    def stopListening(self, widget):
        pass



class UDPClient:
    def __init__(self, IPAddress, portNumber):
        self.IPAddress = IPAddress
        self.portNumber = portNumber
        self.bufferSize = 1024

        self.client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    def connect(self):
        try:
            self.client.bind((self.IPAddress, self.portNumber))
            self.client.setblocking(0)
        except: print "Cannot connect."

    def close(self):
        try:
            self.client.close()
        except:
            pass

udpClient = UDPClient("0.0.0.0", 50000)
gui = GUI(udpClient)
gtk.main() 

到目前为止一切都完美无缺。

1 个答案:

答案 0 :(得分:1)

我发现适用于多线程gtk应用程序的模型是在主线程中运行所有GUI代码。 gtk在后台线程中运行的唯一方法是gobject.idle_add()来安排在主线程中执行gui回调。

在运行任何其他gtk代码之前,将gobject.threads_init()放在模块顶部的某处。

Here's an example for Gtk 3但Gtk 2的原则是相同的。Another example

另一种方法是使用gobject.io_add_watch()之类的东西,避免使用多个线程。