前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用Python和GUI实现Socket多线程通信方案

用Python和GUI实现Socket多线程通信方案

原创
作者头像
华科云商小徐
发布2024-05-14 09:48:31
1540
发布2024-05-14 09:48:31
举报
文章被收录于专栏:小徐学爬虫小徐学爬虫

下面是一个使用 Python 和 Tkinter GUI 库实现 Socket 多线程通信的简单示例。在这个示例中,我是创建了一个简单的聊天应用,其中服务器和客户端可以通过 Socket 进行通信。

1、问题背景

这个问题与在 Python 应用中使用 pyGTK、线程和套接字相关。开发者遇到了一个奇怪的错误,但由于涉及多个模块,他无法确定错误的具体位置。通过使用一些打印语句进行调试,开发者认为错误可能出现在以下代码片段中:

代码语言:javascript
复制
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect(("localhost", 5005))
self.collectingThread = threading.Thread(target=self.callCollect)
self.collectingThread.daemon = True
self.collectingThread.start()

开发者想要做的是设置一个套接字,连接到一个本地运行的服务器脚本,并创建一个单独的线程来收集来自服务器脚本的所有传入数据。此线程被设置为每 500 毫秒运行一次 collectData 方法。在 collectData 方法中插入打印语句后,开发者在运行程序时发现以下现象:

  • 一开始 GUI 完全正常运行。
  • 然后在终端中打印以下内容:
代码语言:javascript
复制
hello
**all data received from server script and printed here**
return
hello
  • 在终端中打印文本后,GUI 变为完全不正常状态(无法按下按钮等),并且必须强制退出才能关闭应用程序。 开发者的分析是,线程先打印“hello”,然后打印来自服务器的数据,最后打印“return”。500 毫秒后,它再次运行 collectData 方法,打印“hello”,然后尝试从服务器打印数据。但是,由于没有数据了,它引发了一个异常,但出于某种未知原因,它没有执行异常块中的代码,一切都从那里挂起。

2、解决方案

问题的核心在于使用了 timeout_add 将操作安排在主线程上,导致接收阻塞主线程,因此 GUI 也被阻塞,除非设置了超时或将套接字设置为非阻塞。

为了获得所需的效果,我们需要将接收委托给线程而不是相反,比如让线程等待一个事件对象,然后每 500 毫秒由安排的操作对事件发送信号。

修改后的代码示例:

代码语言:javascript
复制
import socket
import threading
import gobject
​
class MyClass:
    def __init__(self):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.connect(("localhost", 5005))
​
        self.collectingThread = threading.Thread(target=self.callCollect)
        self.collectingThread.daemon = True
        self.collectingThread.start()
​
        self.event = gobject.Event()
​
    def callCollect(self):
        while True:
            self.event.wait()
            self.collectData()
​
    def collectData(self):
        try:
            data = self.sock.recv(1024)
            if not data:
                return
            print("Received data:", data)
        except Exception as e:
            print("Error receiving data:", e)
        finally:
            self.event.set()
            gobject.timeout_add(500, self.wakeUp)
​
    def wakeUp(self):
        self.event.wakeUp()
        return True
​
if __name__ == "__main__":
    MyClass()
    gobject.MainLoop().run()

在上面的例子中,我们创建了一个 Event 对象 self.event,并使用 timeout_add 每 500 毫秒调用 wakeUp 方法。在 wakeUp 方法中,我们使用 self.event.wakeUp() 唤醒 self.event,从而导致 callCollect 方法中的线程从 self.event.wait() 返回,然后调用 collectData 方法来接收数据。

这两个代码示例分别实现了服务器端和客户端。服务器端监听本地 9999 端口,并等待客户端连接。每当有客户端连接时,服务器端会创建一个新的线程来处理该客户端的通信。客户端通过输入文本框来发送消息,同时接收来自服务器端和其他客户端的消息。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档