文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-08-17 11:28

关注

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

登录:
登录页面
注册:
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

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯