如何使客户端套接字以非阻塞方式从另一个套接字接收数据?

时间:2017-04-23 16:37:02

标签: python python-3.x sockets tkinter

我正在尝试使我的客户端套接字无阻塞。但不成功。我不确定如何解决它。 我正在尝试构建一个简单的聊天框,我正在使用tkinter进行GUI。

要注意的主要部分是我在代码中分段的 while循环

我的客户代码是:

    .
    .
    .
    class GroupFrame(Frame):
        def __init__(self, master):
            super().__init__(master)
            global memList
            global rootHome
            self.master.geometry("400x500")
            self.master.resizable(width=FALSE, height=FALSE)
            self.master.title("PowerPuff Chat Girls")

            #TextArea
            self.ChatLog = Text(self, bd=0, bg="light grey", height="13", width="55", font="Arial")
            self.ChatLog.insert(END, 'Welcome to the PowerPuff Chat, ' + username + '\n', 'INIT')
            self.ChatLog.config(state=DISABLED)
            self.ChatLog.tag_config('INIT', foreground='red', justify=CENTER)
            self.ChatLog.tag_config('BLUE', foreground='blue', justify=LEFT)
            self.ChatLog.tag_config('BLK', foreground='black', justify=RIGHT)

            #ScrollBar
            self.scrollbar = Scrollbar(self, command = self.ChatLog.yview, cursor="heart")
            self.ChatLog['yscrollcommand'] = self.scrollbar.set

            #EntryBox
            self.EntryBox = Text(self, bg="white", width="29", height="5", font="Arial")
            self.EntryBox.bind("<KeyRelease-Return>", lambda event: sendData(self.EntryBox.get(1.0, END)))

            #SendButton
            self.SendButton = Button(self, font=30, text="Send", width="11", height=1,
                            bg="white", fg='navy blue', activebackground="#FACC2E",  command=lambda: sendData(self.EntryBox.get(1.0, END)))

            #Place them on Screen
            self.scrollbar.place(x=380, y=6, height=386)
            self.ChatLog.place(x=8, y=6, height=405, width=370)
            self.EntryBox.place(x=128, y=425, height=60, width=248)
            self.SendButton.place(x=6, y=425, height=60)

            def sendData(param):
                if param == '\n\n':
                    self.EntryBox.delete(1.0, END)
                    return
                if len(param) > 1:
                    if '\n\n' in param:
                        # strip both the carriage return and appened with only one.
                        param = param.rstrip('\n')
                        param = param + '\n'
                    self.EntryBox.delete(1.0, END)
                    insertText(1, '>>' + param)
                    s.sendall(str.encode(param))
                    # data = s.recv(4500)

            def inspectData(data):
                if 'joined the chat **\n' in data.decode('utf-8'):
                    insertText(3, data.decode('utf-8'))
                else:
                    insertText(2, '>>' + data.decode('utf-8'))
                self.ChatLog.see(END)  # this shows the END of the chatlog; auto scroll down

            def insertText(num, param):
                self.ChatLog.config(state=NORMAL)
                if num == 1:
                    self.ChatLog.insert(END, param, 'BLK')
                if num ==2 :
                    self.ChatLog.insert(END, param, 'BLUE')
                if num ==3:
                    self.ChatLog.insert(END, param, 'INIT')
                self.ChatLog.config(state=DISABLED)

  *******************************************************
             while True:
                try:
                    data = s.recv(4096)
                    if(data):
                         inspectData(data)
                except:
                    break
 *******************************************************

page = Tk()
page.geometry("400x500")
page.resizable(width=FALSE, height=FALSE)
pf = HomeFrame(page)
page.mainloop()

使用我当前的代码,当我运行它时,客户端只是在当前位于 GroupFrame 框架时冻结。 我不确定为什么会这样。如果我没有明白我的问题请告诉我,这样我就可以提供一些有用的图像和部分代码。

2 个答案:

答案 0 :(得分:1)

所呈现的代码存在一些不同的问题。主要问题是while函数中的__init__循环。这将使__init__永远运行(假设套接字阻塞)。反过来,这将阻止Tk的主循环,从而阻止整个程序。

使用Tk时,必须意识到Tk拥有主循环。这意味着必须始终允许Tk运行,以使发布到其内部事件队列的事件出列。如果您的程序需要执行其他操作,例如,监听套接字,则必须在单独的线程中完成此操作,或者将其合并到Tk循环中。

不幸的是,没有通用的方法来使Tk循环的套接字输入部分。但是,我们可以做的是使用after发布某个事件,以确保主循环在一定时间后调用预定义函数。这可用于创建解决方案,在某些时候可以轮询套接字,并检查是否有新数据要处理。

另一种方法是在单独的线程中运行套接字,将消息(通过同步的Queue)发布到Tk线程。这可能是一个更通用的解决方案,但它仍然需要主循环轮询队列而不是套接字。

这一切都有点消化。我建议您尝试进一步减少您的示例,然后在较小的示例中询问是否存在仍然难以掌握的部分。

答案 1 :(得分:0)

我设法解决了我的问题。如果其他人有类似的问题轮询从Tkinter循环内的另一个套接字接收的数据。这就是我修复它的方法:

创建一个函数,轮询框架内的套接字,并在该框架内创建一个单独的线程来运行它。

代码:

def polling():
    while True:
        try:
            polledData = s.recv(4096)
            print(polledData.decode('utf-8'))
        except:
            pass #or you can print out the exception if you need to.

threading.Thread(target = polling, args=[]).start()