⽤python实现多⼈聊天室⼩项⽬笔记
写在前⾯的絮絮叨叨
本项⽬⽤python实现⼀个简单的⽹络多⼈聊天室,我把代码都详细注释了⼀下,以便后期翻阅学习。代码涉及⽹络编程的些许知识,假如有⼈对⽹络编程有兴趣的话,我建议学学Java⽹络编程。
运⾏环境:需安装python3.x和对应python3.x的wxpython库
项⽬代码
server.py
import asynchat
import asyncore
import time
# 定义端⼝
PORT =6666
# 定义结束异常类
class EndSession(Exception):
pass
class ChatServer(asyncore.dispatcher):
"""
创建⼀个⽀持多⽤户连接的聊天服务器
"""
#重写构造⽅法
def__init__(self, port):
#显式调⽤⽗类构造⽅法
asyncore.dispatcher.__init__(self)
# 创建socket
# 设置 socket 为可重⽤
self.set_reuse_addr()
# 监听端⼝
self.bind(('', port))
#设置最⼤连接数为5,超出排队
self.listen(5)
self.users ={}
self.main_room = ChatRoom(self)
def handle_accept(self):
#阻塞式监听,等待客户端的连接,⽣成连接对象(SSL通道,客户端地址)
conn, addr = self.accept()
#建⽴会话
ChatSession(self, conn)
class ChatSession(asynchat.async_chat):
"""
负责和客户端通信的会话类
"""
def__init__(self, server, sock):
asynchat.async_chat.__init__(self, sock)
self.server = server
#设置数据终⽌符
self.set_terminator(b'\n')
#设置数据列表
self.data =[]
self.name =None
<(LoginRoom(server))
def enter(self, room):
# 从当前房间移除⾃⾝,然后添加到指定房间
try:
cur =
except AttributeError:
pass
else:
< = room
room.add(self)
#重写处理客户端发来数据的⽅法
def collect_incoming_data(self, data):
# 接收客户端的数据并解码
self.data.append(data.decode("utf-8"))
#重写发现数据中终⽌符号时的处理⽅法
def found_terminator(self):
socket编程聊天室基本流程#将数据列表中的内容整合为⼀⾏
line =''.join(self.data)
#清理数据列表
self.data =[]
try:
# 退出聊天室的处理
except EndSession:
self.handle_close()
def handle_close(self):
# 当 session 关闭时,将进⼊ LogoutRoom
asynchat.async_chat.handle_close(self)
<(LogoutRoom(self.server))
class CommandHandler:
"""
命令处理类
"""
#定义未知命令的处理⽅法
def unknown(self, session, cmd):
# 通过 aynchat.async_chat.push ⽅法发送消息,向客户端发送错误提⽰        session.push(('不知名命令 {} \n'.format(cmd)).encode("utf-8"))
def handle(self, session, line):
#解码
line = line.decode()
#判断去掉空格后是否还有数据
if not line.strip():
return
#把数据以空格分隔符分割⽣成列表,最⼤分割数为1
parts = line.split(' ',1)
#分割的第⼀部分为命令
cmd = parts[0]
#将分割后的第⼆部分去除空格保存到变量
try:
line = parts[1].strip()
except IndexError:
line =''
#获取指定名称的⽅法对象
method =getattr(self,'do_'+ cmd,None)
#调⽤获取到的⽅法对象
try:
method(session, line)
except TypeError:
self.unknown(session, cmd)
class Room(CommandHandler):
"""
包含多个⽤户的环境,负责基本的命令处理和⼴播
包含多个⽤户的环境,负责基本的命令处理和⼴播
"""
def__init__(self, server):
self.server = server
#会话列表
self.sessions =[]
def add(self, session):
# ⼀个⽤户进⼊房间
self.sessions.append(session)
def remove(self, session):
# ⼀个⽤户离开房间
ve(session)
#定义⼴播信息的处理⽅法
def broadcast(self, line):
#遍历所有⽤户会话,再使⽤ asynchat.asyn_chat.push ⽅法发送数据for session in self.sessions:
session.push(line)
def do_logout(self, session, line):
# 退出房间
raise EndSession
class LoginRoom(Room):
"""
处理登录⽤户
"""
def add(self, session):
# ⽤户连接成功的回应
Room.add(self, session)
# 使⽤ asynchat.asyn_chat.push ⽅法发送数据到客户端
session.push('连接成功'.encode('utf-8'))
def do_login(self, session, line):
# ⽤户登录逻辑
name = line.strip()
# 获取⽤户名称
if not name:
session.push('⽤户名为空'.encode('utf-8'))
# 检查是否有同名⽤户
elif name in self.server.users:
session.push('⽤户名已存在'.encode('utf-8'))
# ⽤户名检查成功后,进⼊主聊天室
else:
session.name = name
<(self.server.main_room)
class LogoutRoom(Room):
"""
处理退出⽤户
"""
def add(self, session):
# 从服务器中⽤户字典中移除相关记录
try:
del self.server.users[session.name]
except KeyError:
pass
class ChatRoom(Room):
"""
"""
聊天⽤的房间
"""
def add(self, session):
# ⼴播新⽤户进⼊
session.push('登录成功'.encode('utf-8'))
self.broadcast((session.name +' 进⼊房间\n').encode("utf-8"))
#向服务器的⽤户字典添加与会话的⽤户名相对应的会话
self.server.users[session.name]= session
Room.add(self, session)
def remove(self, session):
# ⼴播⽤户离开
self.broadcast((session.name +' 离开房间\n').encode("utf-8"))
def do_say(self, session, line):
# 客户端发送消息
print(line)
self.broadcast(('time:'+time.strftime('%H:%M:%S',time.localtime(time.time()))+'\n'+session.name +': '+ line +'\n').encode("utf-8"))
def do_look(self, session, line):
# 查看在线⽤户
session.push('在线⽤户:\n'.encode('utf-8'))
for other in self.sessions:
session.push((other.name +'\n').encode("utf-8"))
if __name__ =='__main__':
s = ChatServer(PORT)
try:
print("chat serve run at '127.0.0.1:{0}'".format(PORT))
#开启循环监听⽹络事件
asyncore.loop()
except KeyboardInterrupt:
print("chat server exit")
client.py
import wx
import telnetlib
from time import sleep
import _thread as thread
class LoginFrame(wx.Frame):
"""
登录窗⼝类,继承wx.Frame类
"""
#初始化,添加控件
def __init__(self, parent, id, title, size):
wx.Frame.__init__(self, parent, id, title)
#设置窗体⼤⼩
self.SetSize(size)
#放置正中央
self.Center()
#服务器地址框标签
self.serverAddressLabel = wx.StaticText(self, label="服务器地址", pos=(10, 50), size=(120, 25))
#⽤户名框标签
self.userNameLabel = wx.StaticText(self, label="⽤户名", pos=(40, 100), size=(120, 25))
#服务器地址框
self.serverAddress = wx.TextCtrl(self, pos=(120, 47), size=(150, 25))
#⽤户名框
self.userName = wx.TextCtrl(self, pos=(120, 97), size=(150, 25))
#登录按钮
self.loginButton = wx.Button(self, label='登录', pos=(80, 145), size=(130, 30))
self.loginButton = wx.Button(self, label='登录', pos=(80, 145), size=(130, 30))
#登录按钮上绑定登录⽅法
self.loginButton.Bind(wx.EVT_BUTTON, self.login)
#显⽰组件
self.Show()
def login(self, event):
# 登录处理
try:
serverAddress = self.serverAddress.GetLineText(0).split(':')
con.open(serverAddress[0], port=int(serverAddress[1]), timeout=10)
response = ad_some()
if response !='连接成功'.encode('utf-8'):
self.showDialog('Error', '连接失败!', (200, 100))
return
con.write(('login ' + str(self.userName.GetLineText(0)) + '\n').encode("utf-8"))
response = ad_some()
if response =='⽤户名为空'.encode('utf-8'):
self.showDialog('Error', '⽤户名为空!', (200, 100))
elif response =='⽤户名已存在':
self.showDialog('Error', '⽤户名已存在!', (200, 100))
else:
self.Close()
ChatFrame(None, 2, title='阿坨聊天室', size=(500, 400))
except Exception:
self.showDialog('Error', '连接失败!', (195, 120))
def showDialog(self, title, content, size):
# 显⽰错误信息对话框
dialog = wx.Dialog(self, title=title, size=size)
dialog.Center()
wx.StaticText(dialog, label=content)
#显⽰对话窗⼝
dialog.ShowModal()
class ChatFrame(wx.Frame):
"""
聊天窗⼝类,继承wx.Frame类
"""
def __init__(self, parent, id, title, size):
# 初始化,添加控件
wx.Frame.__init__(self, parent, id, title)
self.SetSize(size)
self.Center()
#显⽰对话⽂本框,style设置其⽂本⾼亮显⽰和只读
self.chatFrame = wx.TextCtrl(self, pos=(5, 5), size=(490, 310), style=wx.TE_MULTILINE | wx.TE_READONLY)
self.sendButton = wx.Button(self, label="Send", pos=(310, 320), size=(58, 25))
self.usersButton = wx.Button(self, label="Users", pos=(373, 320), size=(58, 25))
self.closeButton = wx.Button(self, label="Close", pos=(436, 320), size=(58, 25))
# 发送按钮绑定发送消息⽅法
self.sendButton.Bind(wx.EVT_BUTTON, self.send)
# Users按钮绑定获取在线⽤户数量⽅法
self.usersButton.Bind(wx.EVT_BUTTON, self.lookUsers)
# 关闭按钮绑定关闭⽅法
self.closeButton.Bind(wx.EVT_BUTTON, self.close)
#调⽤thread模块中的start_new_thread()来产⽣新线程负责接收服务器信息
#第⼀个参数为线程要执⾏函数的函数名,第⼆个参数为需要传递给函数的实参,为tuple,若该函数不需要参数也要传⼊空tuple              thread.start_new_ive, ())
self.Show()
def send(self, event):
# 发送消息
message = ssage.GetLineText(0)).strip()
if message !='':
#这⾥的'say '不可随意变动,为呼应server.py中命令处理类定义的handle(),实现⽂字聊天协议⽽存在

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。