I. Introduction of the course 1. Introduction
This project lesson is the server side and the client that implements the simple chat room program.
2. Knowledge points
Server-side involves Asyncore, asynchat and socket modules, the client uses the Telnetlib, WX, time and thread these modules.
3. Required Environment
To write the client in this lesson need to use Wxpython, it is a GUI toolkit, please first install using the following command:
$ sudo apt-get install python-wxtools
Password is Shiyanlou
4. Project Effectiveness
Login window
Chat window
5. Source code Download
git clone https://github.com/shiyanlou/pythonchat.git
Note : If you do not understand the above code download method or after downloading in the environment cannot find the code, you can click to view here
Second, the project actual combat (server side) 1. Server class
First need a chat server, here inherit Asyncore dispatcher class to implement, the code is as follows
class ChatServer(dispatcher): """ 聊天服务器 """ def __init__(self, port): dispatcher.__init__(self) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind((‘‘, port)) self.listen(5) self.users = {} self.main_room = ChatRoom(self) def handle_accept(self): conn, addr = self.accept() ChatSession(self, conn)
2. Session Class
With the server class also need to be able to maintain each user's connection session, here inherit Asynchat Async_chat class to implement, the code is as follows:
class ChatSession(async_chat): """ 负责和单用户通信 """ def __init__(self, server, sock): async_chat.__init__(self, sock) self.server = server self.set_terminator(‘\n‘) self.data = [] self.name = None self.enter(LoginRoom(server)) def enter(self, room): ‘从当前房间移除自身,然后添加到指定房间‘ try: cur = self.room except AttributeError: pass else: cur.remove(self) self.room = room room.add(self) def collect_incoming_data(self, data): ‘接受客户端的数据‘ self.data.append(data) def found_terminator(self): ‘当客户端的一条数据结束时的处理‘ line = ‘‘.join(self.data) self.data = [] try: self.room.handle(self, line) except EndSession: self.handle_close() def handle_close(self): async_chat.handle_close(self) self.enter(LogoutRoom(self.server))
3. Command interpreter
Now you need a command interpreter to interpret the user's commands, such as logging in, querying online users, sending messages, and so on, with the following code:
class CommandHandler: """ 命令处理类 """ def unknown(self, session, cmd): ‘响应未知命令‘ session.push(‘Unknown command: %s\n‘ % cmd) def handle(self, session, line): ‘命令处理‘ if not line.strip(): return parts = line.split(‘ ‘, 1) cmd = parts[0] try: line = parts[1].strip() except IndexError: line = ‘‘ meth = getattr(self, ‘do_‘ + cmd, None) try: meth(session, line) except TypeError: self.unknown(session, cmd)
4. Room
Next we need to implement the room of the chat room, here we define three kinds of rooms, respectively, the user just log in the room, chat room and log out of the room, these three kinds of rooms have a common parent class, the code is as follows:
Class CommandHandler: "" "contains multiple user environments, responsible for basic command handling and broadcast" "" Def __init__ (self, server): Self.server = SE RVer self.sessions = [] def add (self, session): ' A user enters the room ' Self.sessions.append (session) def RE Move (self, session): ' A user leaves the room ' Self.sessions.remove (session) def broadcast (self, line): ' Send to all users Specify message ' for session ' in Self.sessions:session.push (line) def do_logout (self, Session, line): ' Exit Rooms ' Raise Endsessionclass loginroom (room): "" "the room of the user just logged in" "" Def Add (Self, session): ' User connection successful response ' Room.add (self, session) Session.push (' Connect Success ') def do_login (self, Session, line): ' Login command "name = Line.strip () if not Name:session.push (' UserName Empty ') elif name in Self.serve R.users:session.push (' UserName Exist ') else:session.name = name Session.enter (SE Lf.server.main_room) ClasS chatroom (room): "" "" "" "" "," "" "Def Add (Self, Session): ' Broadcast new user enters ' Session.push (' Login Success ') Self.broadcast (Session.name + ' has entered the room.\n ') Self.server.users[session.name] = Session Room.add (self, session) def remove (self, session): ' Broadcast user left ' Room.remove (self, session) SELF.BROADC AST (Session.name + ' have left the room.\n ') def Do_say (self, Session, line): ' Client sends message ' Self.broadcast (ses Sion.name + ': ' + line + ' \ n ') def do_look (self, Session, online): ' View on-site user ' Session.push (' Web users:\n ' ) for others in Self.sessions:session.push (Other.name + ' \ n ') class Logoutroom (room): "" "when the user exits "" "Def Add (Self, session): ' Remove ' from server ' Try:del Self.server.users[session.name] Excep T Keyerror:pass
5. Server-Side complete code
#!/usr/bin/python# encoding:utf-8from asyncore Import dispatcherfrom asynchat import async_chatimport socket, Asyncoreport = 6666 #端口class endsession (Exception): "" "the exception at the end of the custom session" "" Passclass CommandHandler: "" Command Processing Class "" "Def unknown (self, Session, CMD): ' Response unknown Command ' Session.push (' unknown command:%s\n '% cmd) def h Andle (self, Session, line): ' Command handling ' if not Line.strip (): Return parts = Line.split (', 1) cmd = parts[0] Try:line = Parts[1].strip () except indexerror:line = " Meth = GetAttr (self, ' do_ ' + cmd, None) Try:meth (session, line) except Typeerror:se Lf.unknown (Session, CMD) class (CommandHandler): "" "contains multiple user environments and is responsible for basic command handling and broadcast" "Def __init__ (self, server): Self.server = Server Self.sessions = [] def add (self, session): ' A user enters the room ' SELF.SESSIONS.A Ppend (session) def remove (self,session): ' A user leaves the room ' Self.sessions.remove (session) def broadcast (self, line): ' Send the specified message to all users ' For session on Self.sessions:session.push (line) def do_logout (self, Session, line): ' Exit the room ' Raise Endsessionclass Loginroom (room): "" "" "" the User's Rooms "" Def add (Self, session): ' User connection successful response ' Roo M.add (self, session) Session.push (' Connect Success ') def do_login (self, Session, line): ' Login command processing ' n ame = Line.strip () if not Name:session.push (' UserName Empty ') elif name in Self.server.users: Session.push (' UserName Exist ') else:session.name = name Session.enter (self.server.m Ain_room) class chatroom (room): "" "", "" "" "" "" "Def Add (Self, Session): ' Broadcast new user enters ' Session.push (' Lo Gin Success ') self.broadcast (Session.name + ' has entered the room.\n ') self.server.users[session.name] = SE Ssion Room.add (SELF, session) def remove (self, session): ' Broadcast user left ' Room.remove (self, session) Self.broadcast (session . Name + ' have left the room.\n ') def Do_say (self, Session, line): ' Client sends message ' Self.broadcast (Session.name + ': ' + ' + line + ' \ n ') def do_look (self, session): ' View online users ' Session.push (' users:\n ') fo R other in Self.sessions:session.push (Other.name + ' \ n ') class Logoutroom (room): "" "" "when the user exits," "D EF Add (Self, session): ' Remove ' Try:del self.server.users[session.name from the server ' except Keyerror: Passclass chatsession (async_chat): "" "responsible and single user communication" "" Def __init__ (self, server, sock): Async _chat.__init__ (self, sock) Self.server = Server Self.set_terminator (' \ n ') Self.data = [] self. Name = None Self.enter (loginroom (server)) def enter (self, rooms): ' Remove itself from the current room and add to the specified room ' try: Cur = self.room Except Attributeerror:pass else:cur.remove (self) self.room = Hostel Room.ad D (self) def-collect_incoming_data (self, data): ' Accept client-side ' Self.data.append (') def found_terminator ( Self): ' When the end of a client's data processing ' line = '. Join (self.data) Self.data = [] Try:self.room.ha Ndle (self, line) except EndSession:self.handle_close () def handle_close (self): Async_chat.han Dle_close (self) self.enter (Logoutroom (self.server)) class Chatserver (Dispatcher): "" "Chat Server" "Def __in It__ (self, port): dispatcher.__init__ (self) self.create_socket (socket.af_inet, socket. SOCK_STREAM) self.set_reuse_addr () Self.bind ((", port)) Self.listen (5) self.users = {} Self.main_room = Chatroom (self) def handle_accept (self): conn, addr = Self.accept () chatsession (self, CO NN) if __name__ = = ' __main__ ': s = chatserveR (PORT) Try:asyncore.loop () except Keyboardinterrupt:print
Third, the project actual combat (client)
After completing the server side, you need to implement the client, where the client connection server uses the Telnetlib module.
1. Login window
Here the Graphics interface bread selected Wxpython, the installation instructions, the login window by inheriting WX. The frame class is implemented with the following code:
Class Loginframe (WX. Frame): "" "Login Window" "" Def __init__ (self, parent, ID, title, size): ' Initialize, add control and bind event ' WX. Frame.__init__ (self, parent, ID, title) self. SetSize (size) self. Center () Self.serveraddresslabel = WX. Statictext (self, label = "Server Address", pos = (ten), size = (+)) Self.usernamelabel = WX. Statictext (self, label = "UserName", pos = (+, +), size = (+)) self.serveraddress = WX. Textctrl (self, pos = (+), size = (), Self.username = WX. Textctrl (self, pos = (), size = (), Self.loginbutton = WX. Button (self, label = ' Login ', pos = (+, 145), size = (+)) Self.loginButton.Bind (WX. Evt_button, Self.login) self. Show () def login (self, event): ' login processing ' try:serveraddress = Self.serverAddress.GetLineText (0). s Plit (': ') Con.open (serveraddress[0], port = Int (serveraddress[1]), timeout = ten) response = Con.read _soMe () if response! = ' Connect Success ': Self.showdialog (' Error ', ' Connect fail! ', (95, 20)) return con.write (' login ' + str (self.userName.GetLineText (0)) + ' \ n ') response = Con.read_ Some () if response = = ' UserName Empty ': Self.showdialog (' Error ', ' UserName empty! ', (135, 20)) elif response = = ' UserName Exist ': Self.showdialog (' Error ', ' UserName exist! ', (135, 20)) Else:self. Close () ChatFrame (None,-2, title = ' Shiyanlou Chat Client ', size = (+)) except Exception: Self.showdialog (' Error ', ' Connect fail! ', (+)) def showDialog (self, title, content, size): ' Display error message dialog box ' dialog = WX. Dialog (self, title = title, size = size) Dialog. Center () wx. Statictext (dialog, label = content) dialog. ShowModal ()
2. Chat window
The main thing in the Chat window is to send a message to the server and accept the message from the server, which is accepted by the child thread, and the code is as follows:
Class ChatFrame (WX. Frame): "" "Chat Window" "" Def __init__ (self, parent, ID, title, size): ' Initialize, add control and bind event ' WX. Frame.__init__ (self, parent, ID, title) self. SetSize (size) self. Center () Self.chatframe = WX. Textctrl (self, pos = (5, 5), size = (490, 310), style = Wx.te_multiline | wx.te_readonly) self.message = wx. Textctrl (self, pos = (5,.), size = (+)) Self.sendbutton = WX. button (self, label = "Send", pos = (310, + =), size = (Self.usersbutton)) = WX. button (self, label = "Users", pos = (373, + =), size = (Self.closebutton)) = WX. button (self, label = "Close", pos = (436, + =), size = (Self.sendButton.Bind)) (WX. Evt_button, Self.send) self.usersButton.Bind (WX. Evt_button, Self.lookusers) self.closeButton.Bind (WX. Evt_button, Self.close) Thread.start_new_thread (Self.receive, ()) self. Show () def send (self, event): ' send messages ' message = str (self.message.Getlinetext (0)). Strip () if Message! = ": Con.write (' say ' + message + ' \ n ') self.message.Cle AR () def lookusers (Self, event): ' View current online user ' Con.write (' look\n ') def close (self, event): ' Close window ' Con.write (' logout\n ') con.close () self. Close () def receive (self): ' Message accepting server ' while True:sleep (0.6) result = Con.read_very _eager () if result! = ": Self.chatFrame.AppendText (Result)
3. Client complete code
#!/usr/bin/python# encoding:utf-8import wximport telnetlibfrom time import sleepimport threadclass LoginFrame (WX. Frame): "" "Login Window" "" Def __init__ (self, parent, ID, title, size): ' Initialize, add control and bind event ' WX. Frame.__init__ (self, parent, ID, title) self. SetSize (size) self. Center () Self.serveraddresslabel = WX. Statictext (self, label = "Server Address", pos = (ten), size = (+)) Self.usernamelabel = WX. Statictext (self, label = "UserName", pos = (+, +), size = (+)) self.serveraddress = WX. Textctrl (self, pos = (+), size = (), Self.username = WX. Textctrl (self, pos = (), size = (), Self.loginbutton = WX. Button (self, label = ' Login ', pos = (+, 145), size = (+)) Self.loginButton.Bind (WX. Evt_button, Self.login) self. Show () def login (self, event): ' login processing ' try:serveraddress = Self.serverAddress.GetLineText (0). s Plit (': ') Con.open (sErveraddress[0], port = Int (serveraddress[1]), timeout = ten) response = Con.read_some () If response ! = ' Connect Success ': Self.showdialog (' Error ', ' Connect fail! ', (+)) return Con.write (' login ' + str (self.userName.GetLineText (0)) + ' \ n ') response = Con.read_some () if respons E = = ' UserName Empty ': Self.showdialog (' Error ', ' UserName empty! ', (135)) elif response = = ' UserName Exist ': Self.showdialog (' Error ', ' UserName exist! ', (135)) Else:sel F.close () ChatFrame (None,-2, title = ' Shiyanlou Chat Client ', size = (+)) except Exception: Self.showdialog (' Error ', ' Connect fail! ', (+)) def showDialog (self, title, content, size): ' Error displayed Information dialog box ' dialog = WX. Dialog (self, title = title, size = size) Dialog. Center () wx. Statictext (dialog, label = content) diaLog. ShowModal () class ChatFrame (WX. Frame): "" "Chat Window" "" Def __init__ (self, parent, ID, title, size): ' Initialize, add control and bind event ' WX. Frame.__init__ (self, parent, ID, title) self. SetSize (size) self. Center () Self.chatframe = WX. Textctrl (self, pos = (5, 5), size = (490, 310), style = Wx.te_multiline | wx.te_readonly) self.message = wx. Textctrl (self, pos = (5,.), size = (+)) Self.sendbutton = WX. button (self, label = "Send", pos = (310, + =), size = (Self.usersbutton)) = WX. button (self, label = "Users", pos = (373, + =), size = (Self.closebutton)) = WX. button (self, label = "Close", pos = (436, + =), size = (Self.sendButton.Bind)) (WX. Evt_button, Self.send) self.usersButton.Bind (WX. Evt_button, Self.lookusers) self.closeButton.Bind (WX. Evt_button, Self.close) Thread.start_new_thread (Self.receive, ()) self. Show () def send (self, event): ' send messages ' message = StR (Self.message.GetLineText (0)). Strip () if message! = ': Con.write (' say ' + message + ' \ n ') s Elf.message.Clear () def lookusers (Self, event): ' View current online user ' Con.write (' look\n ') def close (self, event): ' Close window ' con.write (' logout\n ') con.close () self. Close () def receive (self): ' Message accepting server ' while True:sleep (0.6) result = Con.read_very _eager () if result! = ": Self.chatFrame.AppendText (Result) ' program runs ' if __name__ = = ' __main__ ': a pp = wx. APP () con = telnetlib. Telnet () Loginframe (None,-1, title = "Login", size = (280, $)) app. Mainloop ()
Iv. Summary
Finally, you can run the program to chat, note that you need to start the server and then start the client. This project uses the Asyncore dispatcher to implement the server, Asynchat Asyn_chat to maintain the user's connection session, using Wxpython to implement the graphical interface, using Telnetlib to connect to the server, A simple chat room program is completed by accepting messages from the server in a child thread.
How to write a chat room in Python