我已经开始使用Python进行网络编程,并且正在开发一个基本的点对点聊天客户端 - 服务器应用程序。我让它适用于控制台,但在开发GUI时遇到问题。
这是我的客户端脚本的代码。它正在向服务器发送数据,但无法接收/显示从服务器发送的数据,我不知所措。请在我的代码和解决方案中显示错误。
from socket import *
from tkinter import *
host="127.0.0.1"
port=1420
buffer=1024
server=(host,port)
clientsock=socket(AF_INET,SOCK_STREAM)
clientsock.connect(server)
class ipbcc(Frame):
def __init__(self,master):
Frame.__init__(self,master)
self.grid()
self.create()
self.connect()
def write(self,event):
msg=self.e.get()
clientsock.send(msg.encode())
def create(self):
self.pic=PhotoImage(file="logo.gif")
self.label=Label(self,image=self.pic)
self.label.grid(column=0)
self.wall=Text(self,width=70,height=20,wrap=WORD)
self.wall.grid(row = 0, column = 1, columnspan = 2, sticky = W)
self.e=Entry(self,width=50)
self.e.grid(row = 1, column = 1, sticky = W)
self.e.bind('<Return>',self.write)
def add(self,data):
self.wall.insert(END,data)
def connect(self):
def xloop():
while 1:
data=clientsock.recv(buffer).decode()
print(data)
self.add(data)
root=Tk()
root.title("IPBCC v0.1")
app=ipbcc(root)
root.mainloop()
PS:Python版本3.3,服务器脚本没有问题。
答案 0 :(得分:1)
你定义xloop
,但是你从来没有真正调用它。
我建议你研究一下使用线程 - 标准库中的threading模块是一种可行的方法。然后,在您的代码中,您将能够创建一个运行xloop
函数的线程,而无需停止其余的代码。或者,您可以从xloop
删除循环(或者实际上只是将函数中的代码放入connect
函数中)并使用widget.after(milliseconds, a_function)
我还想提一下from amodule import *
被认为是不好的做法(尽管tkinter是此规则的例外之一)。
答案 1 :(得分:1)
您的connect
函数定义了一个名为xloop
的函数,但它不会调用该函数,也不会将其返回,或将其存储在某个地方以供其他人调用。你需要调用该函数才能执行任何操作。
当然,如果你只是直接在线调用它,它将永远运行,这意味着你永远不会回到事件循环,并且UI冻结并停止响应用户。
有两种选择:线程或轮询。
显而易见的方法是使用a background thread。基本思路非常简单:
def connect(self):
def xloop():
while 1:
data=clientsock.recv(buffer).decode()
print(data)
self.add(data)
self.t = threading.Thread(target=xloop)
self.t.start()
然而,这有两个问题。
首先,没有办法停止后台线程。当您尝试退出程序时,它将等待后台线程停止 - 这意味着它将永远等待。
有一个简单的解决方案:如果你把它变成一个“守护程序线程”,它将在主程序退出时被立即杀死。对于那些在中间被中断的情况下可能被破坏的线程来说,这显然没有好处,但在你的情况下似乎没有问题。所以,只需更改一行:
self.t = threading.Thread(target=xloop, daemon=True)
其次,self.add
方法需要修改Tkinter小部件。你不能从后台线程那样做。根据您的平台,它可能会无声地失败,引发异常,甚至崩溃 - 或者更糟糕的是,它可能在99%的时间内工作并且失败1%。
因此,您需要某种方式向主线程发送消息,要求 it 为您进行小部件修改。这有点复杂,但Tkinter and Threads解释了如何做到这一点。
或者,您可以使用mtTkinter
,它会拦截后台线程中的Tkinter调用并自动将它们传递给主线程,因此您不必担心它。
另一个选项是将阻塞xloop
函数更改为轮询数据的非阻塞函数。问题是你想等待Tkinter GUI事件,但你也想等待套接字。
如果您可以将套接字集成到主事件循环中,那将很容易:进入的新消息将像任何其他事件一样处理。像Qt这样的一些更强大的GUI框架为您提供了实现此目的的方法,但Tkinter却没有。像Twisted这样的反应堆框架可以将自己绑定到Tkinter并为您添加它(或者至少是假的)。但如果你想坚持你的基本设计,你必须自己做。
所以,有两种选择:
当然,对于许多应用来说,1/20秒可能不是正确的长度,没有答案是非常正确的。无论如何,这是一个简单的例子:
def poll_socket(self):
r, w, x = select.select([clientsock], [], [], 0)
if r:
data=clientsock.recv(buffer).decode()
print(data)
self.add(data)
self.after(50, self.poll_socket)
def connect(self):
self.after(50, self.poll_socket)
答案 2 :(得分:0)
遵循流程可能会有所帮助。 “app = ipbcc(root)”步骤将调用“self.connect()”并且具有“def xloop():”,其具有步骤“data = clientsock.recv”。但是,有人需要调用xloop()。谁那样做?顺便问一下,为什么在方法中有一个函数?
另外,我没有看到有人通过write()方法调用“clientsock.send(msg.encode())”。我不熟悉Tinker部分(以及mainloop()的作用),所以请你检查是否有调用者发送()和recv()调用。