dieses wunderschöne Chatprogramm diente dazu, die Netzwerktutorials, die ich gelesen hatte, in die Tat umzusetzen. Es gibt nur drei kleine Probleme:
1. Wenn ich eine Nachricht absende, wird sie manchmal zweimal angezeigt. Alle von mir durchgeführten Debugging-Maßnahmen (mit denen der Code inzwischen gespickt ist) führten zu keinem sinnvollen Ergebnis.
2. Wenn ich auf "Stop Server" bzw. "Disconnect" klicke, beendet sich der Thread mit der asyncore.loop()-Schleife nicht, obwohl alle Channels (Client und Server) geschlossen sind.
3. Ich kenne keinen echten Separator (Spacer) für Tkinter, wie man auch im Code nachlesen kann.
Ich bin dankbar für jeden Hinweis. Hier der Code:
main.py:
Code: Alles auswählen
# Simple chat application with Python and Tkinter
# (c) 2006 by Martin Unzner
# http://m-unzner.de.vu
# -----------------------------------------------
# MAIN WINDOW
from Tkinter import *
from server import ChatServer
from client import ChatClient
import thread
import asyncore
class ChatWindow:
# Class that handles the controls of the chat program
def __init__(self,mainwnd):
#Initialising basic variables
self.status=0 #means Client
self.started=0 #if the connection has already started
Label(mainwnd,text="Welcome to PyChat, written by Martin Unzner, http://m-unzner.de.vu.").grid(column=0,row=0,columnspan=2)
self.lClSrv=Label(mainwnd,text="Select if the program should be client or server:")
self.lClSrv.grid(column=0,row=1,columnspan=2)
self.cSrv=Radiobutton(mainwnd,text="Server",value=0,command=self.serverGUI)
self.cSrv.grid(column=0,row=2)
self.cCl=Radiobutton(mainwnd,text="Client",value=1,command=self.clientGUI)
self.cCl.select()
self.cCl.grid(column=1,row=2)
Label(mainwnd,text="_______________________________________________________").grid(column=0,row=3,columnspan=2)
# Pseudo-Separator: if anyone knows how the real one is called, please tell me
Label(mainwnd,text="Host:").grid(column=0,row=4)
self.hostVar=StringVar(mainwnd) #must be saved because it will be deactivated when changed to "server"
self.eHost=Entry(mainwnd,textvariable=self.hostVar)
self.eHost.grid(column=1,row=4)
Label(mainwnd,text="Port:").grid(column=0,row=5)
self.portVar=StringVar(mainwnd)
Entry(mainwnd,textvariable=self.portVar).grid(column=1,row=5)
self.portVar.set("10000")
self.bStartSock=Button(mainwnd,text="Connect to server",command=self.startSock)
self.bStartSock.grid(column=0,row=6,columnspan=2)
Label(mainwnd,text="_______________________________________________________").grid(column=0,row=7,columnspan=2)
Label(mainwnd,text="Nickname:").grid(column=0,row=8)
self.nickVar=StringVar(mainwnd)
Entry(mainwnd,textvariable=self.nickVar).grid(column=1,row=8)
Label(mainwnd,text="Enter your message here:").grid(column=0,row=9)
self.msgVar=StringVar(mainwnd)
eMsg=Entry(mainwnd,textvariable=self.msgVar)
eMsg.bind("<Key-Return>",self.sendMessage)
eMsg.bind("<Key-KP_Enter>",self.sendMessage)
eMsg.grid(column=1,row=9)
Button(mainwnd,text="Send",command=self.sendMessage).grid(columnspan=2)
Label(mainwnd,text="Chat Protocol:").grid(column=0,row=11,columnspan=2)
self.tProtocol=Text(mainwnd)
self.tProtocol.grid(column=0,row=12,columnspan=2,rowspan=5)
def networkLoop(self,dummy1,dummy2):
asyncore.loop(5)
def startSock(self):
if not self.started:
host=self.hostVar.get()
try:
port=int(self.portVar.get())
except:
self.setText("No valid port number entered. Assuming 10000.","window.startSock")
self.portVar.set("10000")
port=10000
bTextSet=0
if self.status: #if selected server mode, status is 1 -> true
try:
self.server=ChatServer(port,self)
except:
return # no sucessful initialisation -> exiting
host="localhost"
self.bStartSock["text"]="Stop server"
bTextSet=1
try:
self.client=ChatClient(host,port,self)
except:
return # no sucessful initialisation -> exiting
#starting a thread, therefor needing at least two dummy arguments
thread.start_new_thread(self.networkLoop,(0,0))
if not bTextSet:
self.bStartSock["text"]="Disconnect"
self.cSrv["state"]=DISABLED
self.cCl["state"]=DISABLED
self.started=1
else:
self.client.close()
self.bStartSock["text"]="Connect to server"
if self.status:
self.server.close()
self.bStartSock["text"]="Start server"
self.cSrv["state"]=NORMAL
self.cCl["state"]=NORMAL
self.started=0
def serverGUI(self):
self.status=1 #means Server
self.bStartSock["text"]="Start server"
self.eHost["state"]=DISABLED
def clientGUI(self):
self.status=0 #means Client
self.bStartSock["text"]="Connect to server"
self.eHost["state"]=NORMAL
def sendMessage(self,ev=None):
try:
self.setText(self.nickVar.get()+": "+self.msgVar.get())
self.client.sendMsg(self.nickVar.get()+": "+self.msgVar.get())
except:
self.setText("ERROR: You have to be connected to a server if you want to send messages.","window.sendMessage")
def setText(self,text,f_name=None):
if f_name:
text=f_name+": "+text
self.tProtocol.insert(END,text+"\n")
if __name__=="__main__":
root=Tk()
root.title("PyChat 1.0")
ChatWindow(root)
root.mainloop()
Code: Alles auswählen
# Simple chat application with Python and Tkinter
# (c) 2006 by Martin Unzner
# http://m-unzner.de.vu
# -----------------------------------------------
# SERVER MODULE
import asynchat
import asyncore
import socket
class Connector(asynchat.async_chat):
# Class that handles the connection to a specific client
def __init__(self,server,conn):
asynchat.async_chat.__init__(self,conn)
self.set_terminator("\n")
self.server=server
self.buf=""
def collect_incoming_data(self,data):
self.buf+=data
def found_terminator(self):
#send the received data to all clients
i=0
for client in self.server.clients:
i=i+1
client.push("Server connector push: "+self.buf+"\n")
self.buf=""
def handle_close(self):
self.close()
class ChatServer(asyncore.dispatcher):
# Class that represents a server
def __init__(self,port,main):
asyncore.dispatcher.__init__(self)
self.main=main
self.clients=list() #Server stores all clients
self.create_socket(socket.AF_INET,socket.SOCK_STREAM)
try:
self.bind(("localhost",port))
except:
main.setText("Port %d is already in use." % port,"server.init")
raise
self.listen(1) #waiting for new clients
def handle_accept(self):
conn,addr=self.accept()
self.main.setText("SERVER: Client connected at %s" % addr[0],"server.handle_accept")
client=Connector(self,conn)
self.clients.append(client)
def handle_close(self):
for c in clients:
c.close() #close every client that is saved in the list
self.close()
Code: Alles auswählen
# Simple chat application with Python and Tkinter
# (c) 2006 by Martin Unzner
# http://m-unzner.de.vu
# -----------------------------------------------
# CLIENT MODULE
import asynchat
import socket
class ChatClient(asynchat.async_chat):
# Class that handles the connection to the server
def __init__(self,host,port,main):
conn=socket.socket()
try:
conn.connect((host,port))
except:
main.setText("No server available at specified host/port-address.","client.init")
raise
asynchat.async_chat.__init__(self,conn)
self.set_terminator("\n")
self.main=main
self.buf=""
def collect_incoming_data(self,data):
self.buf+=data
def found_terminator(self):
self.main.setText(self.buf,"client.found_terminator")
self.buf=""
def sendMsg(self,text):
self.push("Client push: "+text+"\n")
def handle_close(self):
self.close()