多线程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 个回答
1
我发现对于多线程的gtk应用程序,最有效的做法是把所有的界面代码都放在主线程里。唯一一个可以在后台线程中运行的gtk方法是 gobject.idle_add()
,它用来安排在主线程中执行的界面回调。
在你运行任何其他gtk代码之前,记得在模块的顶部放上 gobject.threads_init()
。
这里有一个Gtk 3的例子,但Gtk 2的原理是一样的。还有另一个例子。
另外一种选择是使用 gobject.io_add_watch()
,这样就可以避免使用多个线程。