2014-02-19 131 views
0

我試圖將數據發送到客戶端,作爲Python中聊天服務器開發過程的一部分。目前客戶端有一個GUI,但是服務器是從命令行運行的。我已經在下面列出了完整的代碼。Python - Tkinter和socket.recv()循環

客戶

from tkinter import * 
import socket 

host = socket.gethostname() 
port = 8000 

s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 

class Application(Frame): 
    def __init__(self, master=None): 
     #Create master frame 
     Frame.__init__(self,master) 
     self.grid() 
     self.master.title("Test 1") 
     self.conn=False #State of connection to server 


     #Configure main frame 
     for r in range (4): 
      self.master.rowconfigure(r, weight=1) 
     for c in range (2): 
      self.master.columnconfigure(c) 

     #Create sub frames 
     TopFrame=Frame(master, bg="red") 
     TopFrame.grid(row=0, column=0, rowspan=3) 
     BottomFrame=Frame(master, bg="blue") 
     BottomFrame.grid(row=4, column=0) 
     SideFrame=Frame(master, bg="green") 
     SideFrame.grid(column=1, row=0, rowspan=4) 

     #Create Chat log 
     self.chatlog=Text(TopFrame) 
     self.chatlog.pack(padx=5, pady=5) 


    #Create entry field 
     self.e1=StringVar() 
     self.e1=Entry(BottomFrame) 
     self.e1.pack(pady=5, padx=5) 

     #Create buttons 
     b1=Button(SideFrame, text="Connect", command=self.connect) 
     b1.grid(row=0, column=0, padx=5, pady=5) 
     b2=Button(SideFrame, text="Disconnect", command=self.disconnect) 
     b2.grid(row=1, column=0, padx=5, pady=5) 
     b3=Button(SideFrame, text="Send", command=self.sendmessage) 
     b3.grid(row=2, column=0, padx=5, pady=5) 

    def connect(self): #Connect to server 
     self.chatlog['state'] = NORMAL 
     self.chatlog.insert(END, ("===ATTEMPTING TO CONNECT TO SERVER\n")) 
     self.chatlog['state'] = DISABLED 
     self.chatlog.yview(END) 
     try: 
      s.connect((host,port)) 
      self.chatlog['state'] = NORMAL 
      self.chatlog.insert(END, ("===CONNECTED TO SERVER\n")) 

      self.chatlog['state'] = DISABLED 
      self.chatlog.yview(END) 
      self.conn=True 
      print("Connected") #Connection successful 

      #Receive messages 
      self.receive_msg() 

     except ConnectionRefusedError: #Can't find server 
      self.chatlog['state'] = NORMAL 
      self.chatlog.insert(END, ("===SERVER COULD NOT BE FOUND\n" + "===PLEASE MAKE SURE THE SERVER IS ON, AND YOU'RE CONNECTED TO THE NETWORK\n")) 
      self.chatlog['state'] = DISABLED 
      self.chatlog.yview(END) 
     except: #Other errors 
      self.chatlog['state'] = NORMAL 
      self.chatlog.insert(END, ("===THERE'S AN ERROR WITH THE PROGRAM\n" + "===PLEASE TURN IT OFF AND ON AGAIN\n")) 
      self.chatlog['state'] = DISABLED 
      self.chatlog.yview(END) 

     # When attempting to connect a second time, produces OS error: an operation was attempted on something that is not a socket 

    def disconnect(self): 
     if self.conn: #Tests to see if client is connected 
      s.close() 
      self.chatlog['state'] = NORMAL 
      self.chatlog.insert(END, ("===DISCONNECTED FROM SERVER.\n")) 
      self.chatlog['state'] = DISABLED 
      self.chatlog.yview(END) 
      self.conn=False 
     else: #Prevents attempting to disconnect when already disconnected 
      self.chatlog['state'] = NORMAL 
      self.chatlog.insert(END, ("===YOU AREN'T CURRENTLY CONNECTED.\n")) 
      self.chatlog['state'] = DISABLED 
      self.chatlog.yview(END) 


    def sendmessage(self): 
     if self.conn: #Prevents sending if not connected 
      self.msg=self.e1.get() 
      if self.msg == "": #Empty message catcher 
       self.chatlog['state'] = NORMAL 
       self.chatlog.insert(END, ("===YOU CANNOT SEND AN EMPTY MESSAGE.\n")) 
       self.chatlog['state'] = DISABLED 
       self.chatlog.yview(END) 
      else: 
       self.chatlog['state'] = NORMAL 
       self.chatlog.insert(END, ('You: ' + self.msg + '\n')) 
       self.chatlog['state'] = DISABLED 
       self.chatlog.yview(END) 
       self.send_data(self.msg) #Sends message 
     else: 
       self.chatlog['state'] = NORMAL 
       self.chatlog.insert(END, ("===YOU ARE NOT CONNECTED TO A SERVER. YOU CANNOT SEND A MESSAGE.\n")) 
       self.chatlog['state'] = DISABLED 
       self.chatlog.yview(END) 

    def receive_msg(self): 
     #Prepared to receive message 
     print("Preparing to receive") 
     while 1: 
      data=s.recv(1024) 
      decoded_data=data.decode('UTF-8') 
      print(decoded_data) #Only printing to cmd line for now, to resolve errors 
      #self.mainloop() ##Only fix I've found. This loop seems to crash the mainloop used to update GUI 

    def send_data(self, message): 
     try: 
      s.send(message.encode('UTF-8')) 
     except: 
       self.chatlog['state'] = NORMAL 
       self.chatlog.insert(END, ("===THE PREVIOUS MESSAGE DIDN'T SEND. THIS IS POSSIBLY DUE TO A SERVER ERROR.\n")) 
       self.chatlog['state'] = DISABLED 
       self.chatlog.yview(END) 


root = Tk() 
app = Application(root) 
app.mainloop() 

服務器

import socket 

host=socket.gethostname() 
port=(8000) 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 

def connect(): 
    s.bind((host,port)) 
    s.listen(2) 
    print("Server listening") 
    conn,addr=s.accept() 
    print("Connected") 
    send(conn) 

# def receive(conn): 
# while 1: 
#  try: 
#  data=conn.recv(1024) 
#  decoded_data=data.decode('UTF-8') 
#  if not decoded_data: 
#   print("No data") 
#  else: 
#   print("New data") 
#   print(decoded_data) 

def send(conn): 
    while 1: 
     data=input("Input data to send: ") 
     encoded_data=data.encode('UTF-8') 
     conn.send(encoded_data) 

我遇到的問題是,receive_msg()循環用於總是正準備採取新郵件停止主循環保持GUI更新。我已經評論過關於調用app.mainloop()的那一行,但是這意味着我只能在mainloop在receive_msg循環內無限回憶之前收到一條消息。任何人都可以幫我解決問題嗎?

+0

需要線程套接字服務器,然後使用一個全局變量來共享收到消息。由於您的套接字服務器將是寫入全局變量的唯一東西,並且gui會讀取,因此就資源鎖定而言,您應該沒有任何問題。 –

+0

謝謝。你可能會發布一些僞代碼或者簡單地編碼它,因爲我是一般的編程新手,並且不知道如何使用線程。 – QuarterGeekster

+0

實際上有兩種方法可以做到這一點。你可以在你的套接字上插入一個'select'到Tkinter主循環中(儘管它可能更容易轉向另一種方式,並使用一個網絡庫,比如Twisted,它可以將Tkinter主循環插入到它自己的網絡庫中),或者你可以使用線程。 – abarnert

回答

0

您可以使用multiprocessing.connection Listener and Client.

篩選。

服務器

from multiprocessing.connection import Listener 


listener = Listener(("", 25000), authkey=b"selam") 
client = listener.accept() 
while 1: 
    try: 
     print(client.recv_bytes()) 
    except EOFError: 
     print("Connection closed") 
     break 

客戶

from multiprocessing.connection import Client 

remote = Client(("", 25000), authkey=b"selam") 
... 
... 
class Application(Frame): 
... 
... 

    def sendmessage(self): 
     remote.send_bytes(self.e1.get().encode('ascii')) 
.... 
+0

鏈接已損壞。 –