客户端没有通过TCP套接字接收数据

时间:2013-09-09 18:02:20

标签: python sockets networking

我已经开始使用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,服务器脚本没有问题。

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并为您添加它(或者至少是假的)。但如果你想坚持你的基本设计,你必须自己做。

所以,有两种选择:

  • 让Tkinter完全控制。要求它每隔1/20秒调用一次你的函数,并在函数中进行非阻塞检查。或者可以围绕非阻塞检查循环,直到没有什么可读的。
  • 给出套接字控件。要求Tkinter在每次有机会时调用您的函数,并在返回Tkinter之前阻止1/20秒检查数据。

当然,对于许多应用来说,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()调用。

相关问题