广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Python+Socket实现多人聊天室,功能:好友聊天、群聊、图片、表情、文件等
  • 363
分享到

Python+Socket实现多人聊天室,功能:好友聊天、群聊、图片、表情、文件等

pythonflaskmysql 2023-08-17 11:08:07 363人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

一、项目简介 本项目主要基于python实现的多人聊天室,主要的功能如下: 登录注册添加好友与好友进行私聊创建群聊邀请/申请加入群聊聊天发送图片聊天发送表情聊天发送文件聊天记录保存在本地中聊天过程中发送的文件保存本地 二、环境介绍 pyth

一、项目简介

本项目主要基于python实现的多人聊天室,主要的功能如下:

  • 登录注册
  • 添加好友
  • 与好友进行私聊
  • 创建群聊
  • 邀请/申请加入群聊
  • 聊天发送图片
  • 聊天发送表情
  • 聊天发送文件
  • 聊天记录保存在本地中
  • 聊天过程中发送的文件保存本地

二、环境介绍

  • python3.8
  • Mysql8.0
  • tkinter:作为程序的gui库
  • flask :主要用于登录/注册、表情下载、信息修改等Http请求等
  • Socket:主要用户聊天过程中消息发送、对方在线状态更新等
  • pygame:用于播放新消息提示音

三、运行展示

登录:
登录页面
注册:
image-20220427221842887
登录后主界面:
image-20220427221858349
点击右上方“修改资料”:
image-20220427221942224
添加好友或群:
image-20220427222002416
双击好友或群打开聊天窗口:
image-20220427222032094
点击表情按钮选择发送的表情:
image-20220427222102584
发送图片可以预览,点击文件名称直接打开:
image-20220427222217499

四、关键代码

配置文件:server.conf

配置服务器ip、http端口、socket端口、数据库的账号密码、是否启用新消息提示音

[server]SERVER_IP = 127.0.0.1HTTP_PORT = 8000SOCKET_PORT = 8001sqlALCHEMY_DATABASE_URI = mysql://root:root@127.0.0.1:3306/chatdbENABLE_MUSIC = 0

服务端主要代码:ChatServer.py

维持Socket通信、开启Flask进行http

# controller定义@app.route('/login', methods=['POST'])def login():    try:        params = request.values        login_name = params['loginName']        pwd = params['pwd']        md5 = hashlib.md5()        md5.update(pwd.encode(encoding='utf-8'))        passWord = md5.hexdigest()        users = Users.query.filter(Users.loginName == login_name)\            .filter(Users.pwd == password).all()        if len(users) == 0:            return Result.fail('账号不存在或密码错误')        else:            # 服务返回uid,客户端打开好友界面后,凭借此uid与服务器进行socket连接            uid = users[0].id            # 已存在uid:已登录,重新登录,原登录退出连接,退出程序            if uid in online_users.keys():                # loGout                connection = online_users[int(uid)]                send_msg = {'type': UtilsAndConfig.SYSTEM_LOGOUT}                connection.send(JSON.dumps(send_msg).encode())            online_users[uid] = None            return Result.success(uid)    except Exception as e:        return Result.fail('参数异常')# 监听socketdef socket_listen_thread():    while True:        connection, address = mySocket.accept()        # 用户连接携带的uid,判断是否和服务器相同        data_dic = json.loads(connection.recv(1024).decode())        uid = None        if data_dic['type'] == UtilsAndConfig.CONNECTION_REQUEST:            uid = int(data_dic['uid'])        else:            connection.send(UtilsAndConfig.CONNECTION_NOT_ALLOWED.encode())        if uid in online_users.keys():            # 可建立连接            online_users[uid] = connection            connection.send(UtilsAndConfig.CONNECTION_ALLOWED.encode())            # 通知好友们,我上线了            friends = get_friends_by_uid(uid)            for f in friends:                if f.id in online_users.keys():                    friend_connection = online_users[f.id]                    send_msg = {'type': UtilsAndConfig.FRIENDS_ONLINE_CHANGED, 'uid': uid, 'online': 1}                    friend_connection.send(json.dumps(send_msg).encode())            # 创建子线程,保持通信            keep_link_thread = threading.Thread(target=socket_keep_link_thread, args=(connection, ))            keep_link_thread.setDaemon(True)            keep_link_thread.start()        else:            connection.send(UtilsAndConfig.CONNECTION_NOT_ALLOWED.encode())def socket_keep_link_thread(connection):    while True:        try:            msg = connection.recv(1024).decode()            if not msg:                if connection in online_users.values():                    uid = list(online_users.keys())[list(online_users.values()).index(connection)]                    online_users.pop(uid)                    friends = get_friends_by_uid(uid)                    for f in friends:                        if f.id in online_users.keys():friend_connection = online_users[f.id]send_msg = {'type': UtilsAndConfig.FRIENDS_ONLINE_CHANGED, 'uid': uid, 'online': 0}friend_connection.send(json.dumps(send_msg).encode())                    connection.close()                return            else:                msg_json = json.loads(str(msg))                # 发消息                if msg_json['type'] == UtilsAndConfig.CHAT_SEND_MSG:                    to_id = msg_json['toId']                    is_friend = msg_json['isFriend']                    from_uid = msg_json['fromId']                    send_time = msg_json['sendTime']                    msg_text = msg_json['msgText']                    data = {'from_uid': from_uid, 'to_id': to_id, 'send_time': send_time, 'msg_text': msg_text,'is_friend': is_friend, 'type': '', 'msg_type': 'train'}                    # 通知接收方,收到新消息                    if is_friend == 1:                        if to_id in online_users.keys():friend_connection = online_users[to_id]data['type'] = UtilsAndConfig.CHAT_HAS_NEW_MSGfriend_connection.send(json.dumps(data).encode())# 通知发送方,发送成功data['type'] = UtilsAndConfig.CHAT_SEND_MSG_SUCCESSconnection.send(json.dumps(data).encode())                        else:# 通知发送方,发送失败,对方不在线data['type'] = UtilsAndConfig.CHAT_SEND_MSG_ERRconnection.send(json.dumps(data).encode())                    else:                        # 群                        members = get_group_members(to_id)                        members_online = False                        for m in members:if m.uId in online_users.keys() and m.uId != from_uid:    members_online = True    member_connection = online_users[m.uId]    data['type'] = UtilsAndConfig.CHAT_HAS_NEW_MSG    member_connection.send(json.dumps(data).encode())                        if members_online:# 通知发送方,发送成功data['type'] = UtilsAndConfig.CHAT_SEND_MSG_SUCCESSconnection.send(json.dumps(data).encode())                        else:# 通知发送方,发送失败,对方不在线data['type'] = UtilsAndConfig.CHAT_SEND_MSG_ERRconnection.send(json.dumps(data).encode())                if msg_json['type'] == UtilsAndConfig.CHAT_SEND_FILE:                    from_id = msg_json['from_id']                    to_id = msg_json['to_id']                    is_friend = msg_json['is_friend']                    send_date = msg_json['send_date']                    file_length = msg_json['file_length']                    file_suffix = msg_json['file_suffix']                    file_name = msg_json['file_name']                    file_save_name = str(uuid.uuid1()) + '.' + file_suffix                    return_file_path = '/static/tmp/' + file_save_name                    file_path = os.path.abspath(os.path.dirname(__file__)) + return_file_path                    if not os.path.exists(os.path.dirname(file_path)):                        os.makedirs(os.path.dirname(file_path))                    data = {'from_uid': from_id, 'to_id': to_id, 'send_time': send_date, 'file_name': file_name,'is_friend': is_friend, 'type': UtilsAndConfig.CHAT_SEND_FILE_SUCCESS,'file_path': return_file_path}                    if is_friend == 1:                        if to_id not in online_users.keys():# 通知发送方,发送失败,对方不在线data['type'] = UtilsAndConfig.CHAT_SEND_MSG_ERRconnection.send(json.dumps(data).encode())continue                    else:                        members = get_group_members(to_id)                        flag = True                        for m in members:if m.uId in online_users.keys() and m.uId != from_id:    flag = False    break                        if flag:# 通知发送方,发送失败,对方不在线data['type'] = UtilsAndConfig.CHAT_SEND_MSG_ERRconnection.send(json.dumps(data).encode())continue                    # 接收文件                    total_data = b''                    file_data = connection.recv(1024)                    total_data += file_data                    num = len(file_data)                    while num < file_length:                        file_data = connection.recv(1024)                        num += len(file_data)                        total_data += file_data                    with open(file_path, "wb") as f:                        f.write(total_data)                    connection.send(json.dumps(data).encode())                    # 通知接收方,收到新文件消息                    if is_friend == 1:                        friend_connection = online_users[to_id]                        data['type'] = UtilsAndConfig.CHAT_HAS_NEW_FILE                        friend_connection.send(json.dumps(data).encode())                    else:                        members = get_group_members(to_id)                        for m in members:if m.uId in online_users.keys() and m.uId != from_id:    member_connection = online_users[m.uId]    data['type'] = UtilsAndConfig.CHAT_HAS_NEW_FILE    member_connection.send(json.dumps(data).encode())        except ConnectionAbortedError:            if connection in online_users.values():                uid = list(online_users.keys())[list(online_users.values()).index(connection)]                online_users.pop(uid)                friends = get_friends_by_uid(uid)                for f in friends:                    if f.id in online_users.keys():                        friend_connection = online_users[f.id]                        send_msg = {'type': UtilsAndConfig.FRIENDS_ONLINE_CHANGED, 'uid': uid, 'online': 0}                        friend_connection.send(json.dumps(send_msg).encode())                connection.close()            return        except ConnectionResetError:            if connection in online_users.values():                uid = list(online_users.keys())[list(online_users.values()).index(connection)]                online_users.pop(uid)                friends = get_friends_by_uid(uid)                for f in friends:                    if f.id in online_users.keys():                        friend_connection = online_users[f.id]                        send_msg = {'type': UtilsAndConfig.FRIENDS_ONLINE_CHANGED, 'uid': uid, 'online': 0}                        friend_connection.send(json.dumps(send_msg).encode())                connection.close()            return# 主线程if __name__ == '__main__':    # 启动socket线程    socketThread = threading.Thread(target=socket_listen_thread)    socketThread.setDaemon(True)    socketThread.start()    # 启动Flask服务器    app.run(host=serverConfig.SERVER_IP, port=serverConfig.HTTP_PORT, debug=False)

客户端主界面:ChatHome.py

与服务器保持Socket通信、与服务端进行http交互

class ChatHome:    def run(self):        pygame.mixer.init()        # Socket连接        self.socket.connect((self.server_config.SERVER_IP, self.server_config.SOCKET_PORT))        send_data = {'type': UtilsAndConfig.CONNECTION_REQUEST, 'uid': self.uid}        self.socket.send(json.dumps(send_data).encode())        socket_result = self.socket.recv(1024).decode()        if socket_result != UtilsAndConfig.CONNECTION_ALLOWED:            tkinter.messagebox.showwarning('提示', '参数出错,socket连接被拒绝!')            sys.exit()        # 创建子线程保持socket通信        keep_link_thread = threading.Thread(target=self.socket_keep_link_thread)        keep_link_thread.setDaemon(True)        keep_link_thread.start()        # 基本信息        self.root = tk.Tk()        self.root.title('ChatRoom')        self.root.geometry('320x510+100+0')        # 用户名        self.frame_user_info = Frame(self.root, relief=RAISED, width=320, borderwidth=0, height=70, bg='#4F7DA4')        self.frame_user_info.place(x=0, y=0)        self.init_user_info()        # 中间画布canvas        self.frame_mid = Frame(self.root, width=320, height=340)        self.frame_mid.place(x=0, y=70)        # # 画布中的frame        self.init_friends_and_group_view()        # 下方按钮        frame_bottom_button = Frame(self.root, relief=RAISED, borderwidth=0, width=320, height=50)        frame_bottom_button.place(x=0, y=420)        button_bottom_add_friends = Button(frame_bottom_button, width=11,               text='加好友/加群', command=self.open_add_friends)        button_bottom_add_friends.place(x=55, y=10)        button_bottom_create_groups = Button(frame_bottom_button, width=11,                 text='创建群', command=self.open_create_groups)        button_bottom_create_groups.place(x=165, y=10)        # 新消息        frame_message = Frame(self.root, relief=RAISED, borderwidth=0, width=320, height=50)        frame_message.place(x=0, y=460)        self.label_message_tip = Label(frame_message)        self.label_message_tip.place(x=55, y=12)        self.refresh_message_count()        button_message_open = Button(frame_message, width=7,         text='查看', command=self.open_message_window)        button_message_open.place(x=193, y=10)        self.root.mainloop()    # 保持socket通信    def socket_keep_link_thread(self):        while True:            try:                back_msg = self.socket.recv(1024).decode()                msg = json.loads(back_msg)                # 好友状态改变                if msg['type'] == UtilsAndConfig.FRIENDS_ONLINE_CHANGED:                    self.frames_friend_view[msg['uid']].online_type_change(msg['online'])                # 有新验证消息                if msg['type'] == UtilsAndConfig.MESSAGE_NEW_MSG:                    self.refresh_message_count()                    self.play_new_msg_music()                # 好友/群数量改变                if msg['type'] == UtilsAndConfig.FRIENDS_GROUPS_COUNT_CHANGED:                    self.init_friends_and_group_view()                    self.refresh_message_count()                # 有新文本消息, 写入缓存,更新显示                if msg['type'] == UtilsAndConfig.CHAT_HAS_NEW_MSG:                    from_uid = msg['from_uid']                    to_id = msg['to_id']                    is_friend = msg['is_friend']                    txt = {'type': 'get', 'from_uid': from_uid, 'datetime': msg['send_time'],                           'msg': msg['msg_text'], 'msg_type': 'train'}                    UtilsAndConfig.add_one_chat_record(self.uid, is_friend, from_uid, to_id,                           json.dumps(txt, cls=UtilsAndConfig.MyJSONEncoder,          ensure_ascii=False), False)                    # 是否打开聊天界面,打开则更新,未打开则好友列表提示新消息                    if self.window_chat_context is not None and self.window_chat_context.to_id == from_uid\and self.window_chat_context.is_friend == 1 and is_friend == 1:                        self.window_chat_context.get_new_msg()                        pass                    elif self.window_chat_context is not None and self.window_chat_context.to_id == to_id\and self.window_chat_context.is_friend == 0 and is_friend == 0:                        self.window_chat_context.get_new_msg()                    else:                        if is_friend == 1:self.frames_friend_view[from_uid].new_msg_comming()                        else:self.frames_group_view[to_id].new_msg_comming()                    self.play_new_msg_music()                # 发送文本消息成功, 写入本地缓存,更新显示                if msg['type'] == UtilsAndConfig.CHAT_SEND_MSG_SUCCESS:                    from_uid = msg['from_uid']                    to_id = msg['to_id']                    send_time = msg['send_time']                    msg_text = msg['msg_text']                    is_friend = msg['is_friend']                    txt = {'type': 'send', 'datetime': send_time, 'msg': msg_text, 'msg_type': 'train'}                    UtilsAndConfig.add_one_chat_record(self.uid, is_friend, from_uid, to_id,                           json.dumps(txt, cls=UtilsAndConfig.MyJSONEncoder,          ensure_ascii=False), True)                    self.window_chat_context.get_new_msg()                # 发送文件成功                if msg['type'] == UtilsAndConfig.CHAT_SEND_FILE_SUCCESS:                    to_id = msg['to_id']                    send_time = msg['send_time']                    file_name = msg['file_name']                    is_friend = msg['is_friend']                    txt = {'type': 'send', 'datetime': send_time, 'msg': file_name, 'msg_type': 'file'}                    UtilsAndConfig.add_one_chat_record(self.uid, is_friend, self.uid, to_id,                           json.dumps(txt, cls=UtilsAndConfig.MyJSONEncoder,          ensure_ascii=False), True)                    self.window_chat_context.get_new_msg()                    self.window_chat_context.sending_file(False)                # 收到文件                if msg['type'] == UtilsAndConfig.CHAT_HAS_NEW_FILE:                    to_id = msg['to_id']                    from_uid = msg['from_uid']                    send_time = msg['send_time']                    file_name = msg['file_name']                    is_friend = msg['is_friend']                    file_path = msg['file_path']                    files_dir = os.path.abspath(os.path.dirname(__file__)) + '/static/LocalCache/' \    + str(self.uid) + '/files/'                    if not os.path.exists(os.path.dirname(files_dir)):                        os.makedirs(os.path.dirname(files_dir))                    all_file_name = file_name.split('/')[-1]                    file_suffix = all_file_name.split('.')[-1]                    end_index = len(all_file_name) - len(file_suffix) - 1                    file_name = all_file_name[0:end_index]                    file_save_path = files_dir + file_name + '.' + file_suffix                    i = 1                    while os.path.exists(file_save_path):                        file_save_path = files_dir + file_name + '(' + str(i) + ')' + '.' + file_suffix                        i += 1                    # http下载文件,保存到本地                    try:                        url = self.server_config.HTTP_SERVER_ADDRESS + file_path                        res = requests.get(url)                        file_content = res.content                        file = open(file_save_path, 'wb')                        file.write(file_content)                        file.close()                    except requests.exceptions.InvalidSchema:                        pass                        # 服务器中文件不存在                    txt = {'type': 'get', 'from_uid': from_uid, 'datetime': send_time,                           'msg': file_save_path, 'msg_type': 'file'}                    UtilsAndConfig.add_one_chat_record(self.uid, is_friend, from_uid, to_id,                           json.dumps(txt, cls=UtilsAndConfig.MyJSONEncoder,          ensure_ascii=False), False)                    if self.window_chat_context is not None and self.window_chat_context.to_id == from_uid\and self.window_chat_context.is_friend == 1 and is_friend == 1:                        self.window_chat_context.get_new_msg()                        pass                    elif self.window_chat_context is not None and self.window_chat_context.to_id == to_id\and self.window_chat_context.is_friend == 0 and is_friend == 0:                        self.window_chat_context.get_new_msg()                    else:                        if is_friend == 1:self.frames_friend_view[from_uid].new_msg_comming()                        else:self.frames_group_view[to_id].new_msg_comming()                    self.play_new_msg_music()                    # 告诉服务器 文件下载完成,可删除                    url = self.server_config.HTTP_SERVER_ADDRESS + '/downloadFileSuccess?path=' + file_path                    requests.get(url)                # 发送聊天消息失败,不写入缓存,提示对方已下线                if msg['type'] == UtilsAndConfig.CHAT_SEND_MSG_ERR:                    tkinter.messagebox.showwarning('提示', '对方已下线,不能发送消息')                # 服务器强制下线                if msg['type'] == UtilsAndConfig.SYSTEM_LOGOUT:                    self.socket.close()                    tkinter.messagebox.showwarning('提示', '此账号已在别处登录!')                    self.root.destroy()                    return            except ConnectionAbortedError:                tkinter.messagebox.showwarning('提示', '与服务器断开连接!')                self.root.destroy()                return            except ConnectionResetError:                tkinter.messagebox.showwarning('提示', '与服务器断开连接!')                self.root.destroy()                return

五、私聊或评论告诉我,获取源码

来源地址:https://blog.csdn.net/be_your1/article/details/124462875

--结束END--

本文标题: Python+Socket实现多人聊天室,功能:好友聊天、群聊、图片、表情、文件等

本文链接: https://www.lsjlt.com/news/372805.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作