├── client ├── __init__.py ├── __pycache__ │ ├── login.cpython-35.pyc │ ├── memory.cpython-35.pyc │ ├── __init__.cpython-35.pyc │ ├── chat_form.cpython-35.pyc │ ├── contact.cpython-35.pyc │ ├── register.cpython-35.pyc │ ├── security.cpython-35.pyc │ ├── client_socket.cpython-35.pyc │ ├── common_handler.cpython-35.pyc │ ├── contact_form.cpython-35.pyc │ └── socket_listener.cpython-35.pyc ├── security.py ├── memory.py ├── client_socket.py ├── login.py ├── register.py ├── common_handler.py ├── contact_form.py └── chat_form.py ├── server ├── __init__.py ├── __pycache__ │ ├── login.cpython-35.pyc │ ├── memory.cpython-35.pyc │ ├── __init__.cpython-35.pyc │ ├── chat_msg.cpython-35.pyc │ ├── register.cpython-35.pyc │ ├── DB_Handler.cpython-35.pyc │ ├── common_handler.cpython-35.pyc │ ├── manage_friend.cpython-35.pyc │ ├── manage_group.cpython-35.pyc │ ├── server_socket.cpython-35.pyc │ └── server_window.cpython-35.pyc ├── memory.py ├── register.py ├── login.py ├── manage_group.py ├── chat_msg.py ├── manage_friend.py ├── server_window.py ├── common_handler.py ├── server_socket.py └── DB_Handler.py ├── run_client.py ├── run_server.py ├── image ├── 001.jpg ├── 002.jpg ├── 003.jpg ├── 004.jpg └── 005.jpg ├── README.md └── first_time_run_server_create_database.py /client/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /run_client.py: -------------------------------------------------------------------------------- 1 | import client.login as cl 2 | 3 | 4 | cl.run() 5 | -------------------------------------------------------------------------------- /run_server.py: -------------------------------------------------------------------------------- 1 | import server.server_window as ssw 2 | 3 | 4 | ssw.run() 5 | -------------------------------------------------------------------------------- /image/001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/image/001.jpg -------------------------------------------------------------------------------- /image/002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/image/002.jpg -------------------------------------------------------------------------------- /image/003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/image/003.jpg -------------------------------------------------------------------------------- /image/004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/image/004.jpg -------------------------------------------------------------------------------- /image/005.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/image/005.jpg -------------------------------------------------------------------------------- /client/__pycache__/login.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/client/__pycache__/login.cpython-35.pyc -------------------------------------------------------------------------------- /client/__pycache__/memory.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/client/__pycache__/memory.cpython-35.pyc -------------------------------------------------------------------------------- /server/__pycache__/login.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/server/__pycache__/login.cpython-35.pyc -------------------------------------------------------------------------------- /server/__pycache__/memory.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/server/__pycache__/memory.cpython-35.pyc -------------------------------------------------------------------------------- /client/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/client/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /client/__pycache__/chat_form.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/client/__pycache__/chat_form.cpython-35.pyc -------------------------------------------------------------------------------- /client/__pycache__/contact.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/client/__pycache__/contact.cpython-35.pyc -------------------------------------------------------------------------------- /client/__pycache__/register.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/client/__pycache__/register.cpython-35.pyc -------------------------------------------------------------------------------- /client/__pycache__/security.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/client/__pycache__/security.cpython-35.pyc -------------------------------------------------------------------------------- /server/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/server/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /server/__pycache__/chat_msg.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/server/__pycache__/chat_msg.cpython-35.pyc -------------------------------------------------------------------------------- /server/__pycache__/register.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/server/__pycache__/register.cpython-35.pyc -------------------------------------------------------------------------------- /server/__pycache__/DB_Handler.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/server/__pycache__/DB_Handler.cpython-35.pyc -------------------------------------------------------------------------------- /client/__pycache__/client_socket.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/client/__pycache__/client_socket.cpython-35.pyc -------------------------------------------------------------------------------- /client/__pycache__/common_handler.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/client/__pycache__/common_handler.cpython-35.pyc -------------------------------------------------------------------------------- /client/__pycache__/contact_form.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/client/__pycache__/contact_form.cpython-35.pyc -------------------------------------------------------------------------------- /server/__pycache__/common_handler.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/server/__pycache__/common_handler.cpython-35.pyc -------------------------------------------------------------------------------- /server/__pycache__/manage_friend.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/server/__pycache__/manage_friend.cpython-35.pyc -------------------------------------------------------------------------------- /server/__pycache__/manage_group.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/server/__pycache__/manage_group.cpython-35.pyc -------------------------------------------------------------------------------- /server/__pycache__/server_socket.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/server/__pycache__/server_socket.cpython-35.pyc -------------------------------------------------------------------------------- /server/__pycache__/server_window.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/server/__pycache__/server_window.cpython-35.pyc -------------------------------------------------------------------------------- /client/__pycache__/socket_listener.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyjone/python-chatroom/HEAD/client/__pycache__/socket_listener.cpython-35.pyc -------------------------------------------------------------------------------- /server/memory.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Author:jeremyjone 3 | date:2018-5-4 4 | 5 | Keep general information for server. 6 | ''' 7 | from .DB_Handler import DB_Handler 8 | 9 | 10 | # {connect: (username, IP, PORT)} 11 | online_user = {} 12 | 13 | server_socket = None 14 | 15 | server_socket_listener = None 16 | 17 | db = DB_Handler() 18 | 19 | window = None 20 | -------------------------------------------------------------------------------- /client/security.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Author:jeremyjone 3 | date:2018-5-4 4 | 5 | Secure encryption, 6 | a cryptographic operation for a password. 7 | ''' 8 | from Crypto.Hash import MD5 9 | 10 | 11 | def loop_encrypt(pwd, n=10): 12 | # Salt encrypt and recursion 10 times. 13 | salt = 'jeremyjone' 14 | md5_obj = MD5.new() 15 | md5_obj.update((pwd + salt).encode()) 16 | # print(n, md5_obj.hexdigest()) 17 | if n == 1: 18 | return md5_obj.hexdigest() 19 | return loop_encrypt(md5_obj.hexdigest(), n - 1) 20 | -------------------------------------------------------------------------------- /server/register.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Author:jeremyjone 3 | date:2018-5-5 4 | 5 | Register handler. 6 | ''' 7 | import struct 8 | from . import memory, common_handler 9 | 10 | 11 | def register_handler(c, msg): 12 | uname = msg[1].decode() 13 | upswd = msg[2].decode() 14 | nkname = msg[3].decode() 15 | res = memory.db.register(uname, upswd, nkname) 16 | if res == 'OK': 17 | c.send(struct.pack("!L", common_handler.MessageType.register_successful)) 18 | return 19 | elif res == "NAMEEXIST": 20 | c.send(struct.pack("!L", common_handler.MessageType.username_taken)) 21 | else: 22 | c.send(struct.pack("!L", common_handler.MessageType.general_failure)) 23 | c.close() 24 | memory.online_user.pop(c) 25 | -------------------------------------------------------------------------------- /client/memory.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Author:jeremyjone 3 | date:2018-5-4 4 | 5 | Keep general information for client. 6 | ''' 7 | IP = "0.0.0.0" 8 | PORT = "5555" 9 | 10 | sc = None 11 | 12 | Login_window = None 13 | 14 | Contact_window = [] 15 | 16 | # {username: window} 17 | Chat_window = {} 18 | 19 | # {username: [(time, from_user, message1, flag), (time, from_user, message2, flag), ...]} 20 | recv_message = {} 21 | 22 | # {(1, friend_username): friend_nickname} 23 | # {(2, chatroom_name): chatroom_show_name(群 chatroom_name)} 24 | friend_list = {} 25 | 26 | # {chatroom_name: [(username1, nickname1), (username2, nickname2), ...]} 27 | chatroom_user_list = {} 28 | 29 | recv_msg_thread = None 30 | 31 | # {"username": "nickname"} 32 | current_user = {} 33 | username = "" 34 | sc = None 35 | tk_root = None 36 | -------------------------------------------------------------------------------- /server/login.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Author:jeremyjone 3 | date:2018-5-5 4 | 5 | Login handler. 6 | ''' 7 | from . import memory, common_handler 8 | 9 | 10 | def login_handler(c, ad, msg): 11 | uname = msg[1].decode() 12 | upswd = msg[2].decode() 13 | 14 | res = memory.db.login_check(uname, upswd) 15 | 16 | if res == 'OK': 17 | nickname = memory.db.get_user_nickname(uname)[0].encode() 18 | serializeMessage = common_handler.pack_message(common_handler.MessageType.login_successful, nickname) 19 | c.send(serializeMessage) 20 | memory.online_user[c] = (uname, ad[0], ad[1]) 21 | memory.window.add_user_list() 22 | else: 23 | result = b"login fail" 24 | serializeMessage = common_handler.pack_message(common_handler.MessageType.login_failed, result) 25 | c.send(serializeMessage) 26 | c.close() 27 | # memory.online_user.pop(c) 28 | 29 | 30 | def logout_handler(c): 31 | del memory.online_user[c] 32 | memory.window.add_user_list() 33 | -------------------------------------------------------------------------------- /server/manage_group.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @title: manage group 3 | @Author: jeremyjone 4 | date: 2018-5-9 5 | 6 | Manage all about chatroom handler. 7 | ''' 8 | import struct 9 | 10 | from . import memory, common_handler 11 | 12 | 13 | def chatroom_handler(s, msg): 14 | chatroom_name = msg[1].decode() 15 | res_create = memory.db.create_chatroom(chatroom_name) 16 | res_join = '' 17 | if res_create == "EXIST": 18 | m = b"EXIST" 19 | else: 20 | res_join = memory.db.chatroom_user( 21 | chatroom_name, memory.online_user[s][0], 'join') 22 | cn = b'' 23 | if res_create == res_join == "OK": 24 | m = b"OK" 25 | cn = chatroom_name.encode() 26 | else: 27 | m = b"NG" 28 | serializeMessage = common_handler.pack_message(common_handler.MessageType.create_room_res, m, cn) 29 | s.send(serializeMessage) 30 | 31 | 32 | def user_join_leave_handler(s, msg, handler): 33 | chatroom_name = msg[1].decode() 34 | name = msg[2].decode() 35 | res = memory.db.chatroom_user(chatroom_name, name, handler) 36 | if res == "OK": 37 | res = b"OK" 38 | else: 39 | res = b"NG" 40 | serializeMessage = common_handler.pack_message(common_handler.MessageType.join_leave_chatroom, res) 41 | s.send(serializeMessage) 42 | 43 | 44 | def query_chatroom_user(s, msg): 45 | chatroom_name = msg[1].decode() 46 | res = memory.db.get_chatroom_user(chatroom_name) 47 | 48 | if res == "NF": 49 | ct_user = "no more user" 50 | else: 51 | # Return friends list 52 | ct_user = " + ".join(res) 53 | total_ct_user = ct_user.encode() 54 | chatroom_name = chatroom_name.encode() 55 | 56 | serializeMessage = common_handler.pack_message(common_handler.MessageType.query_room_users_result, chatroom_name, total_ct_user) 57 | s.send(serializeMessage) 58 | -------------------------------------------------------------------------------- /server/chat_msg.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | from . import memory, server_socket, common_handler 4 | 5 | 6 | def userchat_handler(msg): 7 | send_time = msg[1].decode() 8 | target_user = msg[2].decode() 9 | from_user = msg[3] 10 | message = msg[4].decode() 11 | _online_flag = 1 12 | for _u in memory.online_user: 13 | if memory.online_user[_u][0] == target_user: 14 | # user online 15 | _online_flag = 0 16 | _time = send_time.encode() 17 | _message = message.encode() 18 | serializeMessage = common_handler.pack_message( 19 | common_handler.MessageType.on_new_message, from_user, _time, _message) 20 | _u.send(serializeMessage) 21 | # _u.send(b"hello") 22 | break 23 | 24 | # Save message to database. 25 | from_user = from_user.decode() 26 | memory.db.save_msg(from_user, target_user, _online_flag, 1, message) 27 | 28 | 29 | def unread_msg_handler(c, user): 30 | res = memory.db.get_unread_msg(user) 31 | if not res: 32 | return 33 | for r in res: 34 | uid = r[0] 35 | utime = r[1] 36 | utype = r[2] 37 | umsg = r[3].encode() 38 | 39 | uname = memory.db.get_username_by_id(uid)[0].encode() 40 | time = ("%s年%s月%s日 %s时%s分%s秒" % ( 41 | utime.year, utime.month, utime.day, 42 | utime.hour, utime.minute, utime.second)).encode() 43 | 44 | if utype == 1: 45 | # Normal msg. 46 | serializeMessage = common_handler.pack_message( 47 | common_handler.MessageType.on_new_message, uname, time, umsg) 48 | c.send(serializeMessage) 49 | 50 | elif utype == 2: 51 | # Broadcast. 52 | pass 53 | elif utype == 3: 54 | # Add friend request. 55 | request_user = uname 56 | serializeMessage = common_handler.pack_message( 57 | common_handler.MessageType.add_friend_request, request_user) 58 | c.send(serializeMessage) 59 | elif utype == 4: 60 | # Chatroom msg. 61 | pass 62 | 63 | 64 | def chatroom_handler(s, msg): 65 | send_time = msg[1] 66 | chatroom_name = msg[2].decode() 67 | from_user = msg[3] 68 | message = msg[4] 69 | users = memory.db.get_chatroom_user(chatroom_name) 70 | user_list = [] 71 | for user in users: 72 | us = user.split(":") 73 | user_list.append(us[0]) 74 | 75 | chatroom_name = chatroom_name.encode() 76 | for c in memory.online_user: 77 | for target_user in user_list: 78 | if memory.online_user[c][0] == target_user: 79 | serializeMessage = common_handler.pack_message( 80 | common_handler.MessageType.chatroom_msg, send_time, chatroom_name, from_user, message) 81 | c.send(serializeMessage) 82 | 83 | 84 | def broadcast_handler(s, msg): 85 | pass 86 | -------------------------------------------------------------------------------- /client/client_socket.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Author:jeremyjone 3 | Date:2018-5-5 4 | 5 | This module provides methods and interfaces for clients 6 | to connect to the server. 7 | ''' 8 | from socket import * 9 | import time 10 | import struct 11 | from threading import Thread 12 | 13 | from . import memory, contact_form, chat_form, common_handler 14 | 15 | 16 | def send_msg(data): 17 | memory.sc.send(data) 18 | 19 | 20 | def recv_msg(c): 21 | try: 22 | msg = c.recv(4096) 23 | except Exception: 24 | pass 25 | return msg 26 | 27 | 28 | def connect_to_server(IP, PORT): 29 | s = socket(AF_INET, SOCK_STREAM) 30 | try: 31 | s.connect((IP, int(PORT))) 32 | return s 33 | except Exception: 34 | return "connect_fail" 35 | 36 | 37 | def keep_recv(): 38 | print("线程启动") 39 | msg_buffer = None 40 | while True: 41 | # try: 42 | print("开始监听") 43 | data = memory.sc.recv(4096) 44 | msg = common_handler.unpack_message(data) 45 | # Recv large file 46 | if msg[0] == common_handler.MessageType.large_file: 47 | msg_buffer += msg[1] 48 | if msg[2] == 0: 49 | msg = msg_buffer 50 | msg_buffer = None 51 | else: 52 | continue 53 | 54 | elif msg[0] == common_handler.MessageType.query_friend_list: 55 | # friend_list 56 | print("收到好友列表") 57 | contact_form.show(msg) 58 | 59 | elif msg[0] == common_handler.MessageType.on_new_message: 60 | # chatmsg 61 | print("接收到聊天信息") 62 | chat_form.chatmsg_handler(msg) 63 | 64 | elif msg[0] == common_handler.MessageType.chatroom_msg: 65 | # chatroom_msg 66 | print("接收到群聊信息") 67 | chat_form.chatroom_msg_handler(msg) 68 | 69 | elif msg[0] == common_handler.MessageType.create_room_res: 70 | print("接收到创建聊天室反馈信息") 71 | contact_form.chatroom_handler(msg) 72 | 73 | elif msg[0] == common_handler.MessageType.query_room_users_result: 74 | print("接收到创建聊天室反馈信息") 75 | chat_form.chatroom_user_update(msg) 76 | 77 | elif msg[0] == common_handler.MessageType.user_not_exist: 78 | print("接收到希望添加的好友不存在") 79 | contact_form.recive_some_info(msg) 80 | 81 | elif msg[0] == common_handler.MessageType.add_friend_request: 82 | print("接收到添加好友请求") 83 | contact_form.recive_some_info(msg) 84 | 85 | elif msg[0] == common_handler.MessageType.add_friend_result: 86 | print("接收到确认添加好友回复") 87 | contact_form.recive_some_info(msg) 88 | 89 | elif msg[0] == common_handler.MessageType.join_leave_chatroom: 90 | print("接收到出入聊天室") 91 | contact_form.recive_some_info(msg) 92 | 93 | elif msg[0] == common_handler.MessageType.delete_friend_failed: 94 | print("接收到删除好友失败") 95 | contact_form.recive_some_info(msg) 96 | 97 | # except struct.error: 98 | # pass 99 | # except Exception as e: 100 | # print(e) 101 | memory.sc.close() 102 | 103 | 104 | def keep_connect_listener(): 105 | t = Thread(target=keep_recv) 106 | memory.recv_msg_thread = t 107 | t.start() 108 | return 109 | -------------------------------------------------------------------------------- /server/manage_friend.py: -------------------------------------------------------------------------------- 1 | ''' 2 | title: Manage_Friend method. 3 | @Author: jeremyjone 4 | date: 2018-5-6 5 | 6 | Supply to add friend & confirm add, delete friend, read friends 7 | list, convenient user management friend relationship. 8 | ''' 9 | import struct 10 | from .memory import * 11 | 12 | from . import common_handler, chat_msg, memory 13 | 14 | 15 | def add_friend_handler(c, msg): 16 | add_name = msg[1].decode() 17 | res = db.user_exist(add_name) 18 | if not res: 19 | serializeMessage = common_handler.pack_message(common_handler.MessageType.user_not_exist, b'no_user') 20 | c.send(serializeMessage) 21 | else: 22 | for i in online_user: 23 | if online_user[i][0] == res[0]: 24 | # online 25 | request_user = online_user[c][0].encode() 26 | serializeMessage = common_handler.pack_message(common_handler.MessageType.add_friend_request, request_user) 27 | i.send(serializeMessage) 28 | return 29 | # If user offline, save msg into database. 30 | db.save_msg(online_user[c][0], add_name, 1, 3, "add friend") 31 | 32 | 33 | def confirm_handler(c, msg): 34 | result = msg[1].decode() 35 | respoonse_user = msg[2].decode() 36 | request_user = msg[3].decode() 37 | for i in online_user: 38 | if online_user[i][0] == request_user: 39 | request_socket = i 40 | serializeMessage = common_handler.pack_message(common_handler.MessageType.add_friend_result, result.encode(), respoonse_user.encode()) 41 | i.send(serializeMessage) 42 | break 43 | if result == "OK": 44 | # Add to database. 45 | res = db.user_add_friend(request_user, respoonse_user) 46 | try: 47 | if res == "NG": 48 | raise ValueError("添加好友产生了一个未知错误,没有添加成功,\ 49 | 好友关系人>> {} 和 {}".format(request_user, online_user[request_socket][0])) 50 | else: 51 | get_friend_handler(request_socket) 52 | get_friend_handler(c) 53 | except ValueError as e: 54 | print(e) 55 | 56 | 57 | def del_friend_handler(c, msg): 58 | target_user = msg[1].decode() 59 | request_user = msg[2].decode() 60 | res = memory.db.user_del_friend(request_user, target_user) 61 | if res == "OK": 62 | get_friend_handler(c) 63 | for i in online_user: 64 | if online_user[i][0] == target_user: 65 | get_friend_handler(i) 66 | else: 67 | _msg = b"delete failed" 68 | serializeMessage = common_handler.pack_message(common_handler.MessageType.delete_friend_failed, _msg) 69 | c.send(serializeMessage) 70 | 71 | 72 | def get_friend_handler(c): 73 | username = online_user[c][0] 74 | res_user = db.user_friend(username) 75 | if res_user == "NF": 76 | friend = "no friend" 77 | else: 78 | # Return friends list 79 | friend = " + ".join(res_user) 80 | 81 | res_room = db.query_chatroom(username) 82 | if res_room == "NF": 83 | chatroom = 'no chatroom' 84 | else: 85 | chatroom = " + ".join(res_room) 86 | 87 | total_friend = friend.encode() 88 | total_chatroom = chatroom.encode() 89 | serializeMessage = common_handler.pack_message(common_handler.MessageType.query_friend_list, total_friend, total_chatroom) 90 | c.send(serializeMessage) 91 | chat_msg.unread_msg_handler(c, username) 92 | -------------------------------------------------------------------------------- /client/login.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Author:jeremyjone 3 | date:2018-5-4 4 | 5 | Client interface, starting main interface 6 | ''' 7 | import tkinter as tk 8 | from tkinter import messagebox 9 | from tkinter import * 10 | import struct, time 11 | 12 | from . import memory, client_socket, contact_form, register, common_handler, security 13 | 14 | 15 | class LoginForm(tk.Frame): 16 | 17 | def __init__(self, master=None): 18 | super().__init__(master) 19 | self.master = master 20 | memory.Login_window = self.master 21 | self.master.resizable(width=False, height=False) 22 | self.master.geometry('300x120') 23 | # bm2 = PhotoImage(file="logo.png") 24 | # imglabel = Label(self, image=bm2) 25 | # imglabel.grid(row=0, column=0, columnspan=2) 26 | self.label_1 = Label(self, text="用户名") 27 | self.label_2 = Label(self, text="密码") 28 | 29 | self.username = Entry(self) 30 | self.password = Entry(self, show="*") 31 | 32 | self.label_1.grid(row=1, sticky=E) 33 | self.label_2.grid(row=2, sticky=E) 34 | self.username.grid(row=1, column=1, pady=(10, 6)) 35 | self.password.grid(row=2, column=1, pady=(0, 6)) 36 | 37 | self.buttonframe = Frame(self) 38 | self.buttonframe.grid(row=3, column=0, columnspan=2, pady=(4, 6)) 39 | 40 | self.logbtn = Button(self.buttonframe, 41 | text="登录", 42 | command=self.do_login) 43 | self.logbtn.grid(row=0, column=0) 44 | 45 | self.registerbtn = Button(self.buttonframe, 46 | text="注册", 47 | command=self.do_register) 48 | self.registerbtn.grid(row=0, column=1) 49 | 50 | self.pack() 51 | self.master.title("欢迎登陆聊天室...") 52 | 53 | def do_login(self): 54 | username = self.username.get() 55 | password = self.password.get() 56 | password = security.loop_encrypt(password) 57 | if not username: 58 | messagebox.showerror("出错了", "用户名不能为空") 59 | return 60 | if not password: 61 | messagebox.showerror("出错了", "密码不能为空") 62 | return 63 | 64 | res = client_socket.connect_to_server(str(memory.IP), int(memory.PORT)) 65 | if res == "connect_fail": 66 | messagebox.showerror("无法连接到服务器", "对不起,无法连接到服务器") 67 | else: 68 | memory.sc = res 69 | 70 | # 2 packs 71 | # First one include length infomation, 72 | # The second one include complete values information. 73 | uname = username.encode() 74 | pwd = password.encode() 75 | serializeMessage = common_handler.pack_message(common_handler.MessageType.login, uname, pwd) 76 | client_socket.send_msg(serializeMessage) 77 | lg_res = client_socket.recv_msg(memory.sc) 78 | 79 | # Get result from server 80 | login_result = common_handler.unpack_message(lg_res) 81 | if login_result[0] == common_handler.MessageType.login_successful: 82 | memory.Login_window.destroy() 83 | memory.Login_window = None 84 | memory.username = username 85 | memory.current_user[username] = login_result[1].decode() 86 | contact_form.run(username) 87 | else: 88 | memory.sc.close() 89 | messagebox.showerror("输入错误!", "对不起,您输入的有误,请重新输入") 90 | 91 | def do_register(self): 92 | self.master.withdraw() 93 | reg = tk.Toplevel() 94 | register.RegisterForm(reg) 95 | 96 | 97 | def run(): 98 | root = Tk() 99 | LoginForm(root) 100 | root.mainloop() 101 | 102 | 103 | if __name__ == '__main__': 104 | run() 105 | -------------------------------------------------------------------------------- /client/register.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import messagebox 3 | from tkinter import * 4 | import struct 5 | 6 | from . import memory, client_socket, common_handler, security 7 | 8 | 9 | class RegisterForm(tk.Frame): 10 | def do_return(self): 11 | self.master.destroy() 12 | memory.Login_window.deiconify() 13 | 14 | def do_register(self): 15 | username = self.username.get() 16 | password = self.password.get() 17 | password_confirmation = self.password_confirmation.get() 18 | nickname = self.nickname.get() 19 | if not username: 20 | messagebox.showerror("出错了", "用户名不能为空") 21 | return 22 | if not password: 23 | messagebox.showerror("出错了", "密码不能为空") 24 | return 25 | if not nickname: 26 | nickname = username 27 | if password != password_confirmation: 28 | messagebox.showerror("出错了", "两次密码输入不一致") 29 | return 30 | res = client_socket.connect_to_server(str(memory.IP), int(memory.PORT)) 31 | if res == "connect_fail": 32 | messagebox.showerror("无法连接到服务器", "对不起,无法连接到服务器") 33 | else: 34 | memory.sc = res 35 | 36 | # 2 packs 37 | # First one include length infomation, 38 | # The second one include complete values information. 39 | password = security.loop_encrypt(password) 40 | uname = username.encode() 41 | pwd = password.encode() 42 | kname = nickname.encode() 43 | 44 | serializeMessage = common_handler.pack_message(common_handler.MessageType.register, uname, pwd, kname) 45 | client_socket.send_msg(serializeMessage) 46 | lg_res = struct.unpack("!L", client_socket.recv_msg(memory.sc))[0] 47 | if lg_res == common_handler.MessageType.register_successful: 48 | memory.sc.close() 49 | messagebox.showinfo("注册成功!", "恭喜您,快上来玩儿啊!") 50 | self.do_return() 51 | elif lg_res == common_handler.MessageType.username_taken: 52 | messagebox.showerror("用户名已存在!", "抱歉,您来晚了,此用户名已有小主了 ^.^!") 53 | else: 54 | messagebox.showerror("注册失败!", "可能您的输入方式不对,请换个姿势 ^_^!") 55 | 56 | def __init__(self, master=None): 57 | super().__init__(master) 58 | self.master = master 59 | # self.memory.tk_reg = self.master 60 | 61 | self.master.resizable(width=False, height=False) 62 | self.master.geometry('210x170') 63 | self.master.title("注册账户") 64 | 65 | self.label_1 = Label(self, text="用户名") 66 | self.label_2 = Label(self, text="密码") 67 | self.label_3 = Label(self, text="确认密码") 68 | self.label_4 = Label(self, text="昵称") 69 | 70 | self.username = Entry(self) 71 | self.password = Entry(self, show="*") 72 | self.password_confirmation = Entry(self, show="*") 73 | self.nickname = Entry(self) 74 | 75 | self.label_1.grid(row=0, sticky=E) 76 | self.label_2.grid(row=1, sticky=E) 77 | self.label_3.grid(row=2, sticky=E) 78 | self.label_4.grid(row=3, sticky=E) 79 | 80 | self.username.grid(row=0, column=1, pady=(10, 6)) 81 | self.password.grid(row=1, column=1, pady=(0, 6)) 82 | self.password_confirmation.grid(row=2, column=1, pady=(0, 6)) 83 | self.nickname.grid(row=3, column=1, pady=(0, 6)) 84 | 85 | self.btnframe = Frame(self) 86 | self.regbtn = Button(self.btnframe, 87 | text="注册", 88 | command=self.do_register) 89 | self.returnbtn = Button(self.btnframe, 90 | text='返回', 91 | command=self.do_return) 92 | self.regbtn.grid(row=0, column=1) 93 | self.returnbtn.grid(row=0, column=2) 94 | self.btnframe.grid(row=4, columnspan=2) 95 | self.pack() 96 | 97 | 98 | if __name__ == '__main__': 99 | root = Tk() 100 | RegisterForm(root) 101 | root.mainloop() 102 | -------------------------------------------------------------------------------- /server/server_window.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Author:jeremyjone 3 | date:2018-4-9 4 | This is the display window for the server, you can set up ports, 5 | open and close the server, at the same time, you can view the 6 | IP and port of the current connection client. 7 | ''' 8 | from tkinter import * 9 | from threading import Thread 10 | import os 11 | 12 | from . import memory, server_socket 13 | 14 | 15 | class ServerForm(Frame): 16 | 17 | def __init__(self, master=None): 18 | super().__init__(master) 19 | memory.window = self 20 | self.master = master 21 | self.master.resizable(width=False, height=False) 22 | self.port_frame = Frame(self.master) 23 | self.list_frame = Frame(self.master) 24 | self.bottom_frame = Frame(self.master) 25 | 26 | self.ip_label = Label(self.port_frame, text='地址:') 27 | self.ip_var = StringVar() 28 | self.ip_var.set('127.0.0.1') 29 | self.ip_entry = Entry(self.port_frame, textvariable=self.ip_var) 30 | self.port_label = Label(self.port_frame, text='端口:') 31 | self.port_var = StringVar() 32 | self.port_var.set('4444') 33 | self.port_entry = Entry(self.port_frame, textvariable=self.port_var) 34 | self.start_server_button = Button(self.port_frame, 35 | text='开启服务器', 36 | command=self.do_open_server) 37 | self.stop_server_button = Button(self.port_frame, 38 | text='关闭服务器', 39 | command=self.do_close_server) 40 | 41 | self.ip_label.grid(row=0, column=0, ipadx=5) 42 | self.ip_entry.grid(row=0, column=1, padx=2) 43 | self.port_label.grid(row=0, column=2, padx=2) 44 | self.port_entry.grid(row=0, column=3, padx=2, ipadx=5) 45 | self.start_server_button.grid(row=1, column=0, columnspan=2) 46 | self.stop_server_button.grid(row=1, column=2, columnspan=2) 47 | 48 | self.list_scorll = Scrollbar(self.list_frame) 49 | self.list_scorll.pack(side=RIGHT, fill=Y) 50 | self.user_list = Listbox(self.list_frame, 51 | width=50, 52 | height=30, 53 | yscrollcommand=self.list_scorll.set) 54 | self.user_list.bind('Visibility', self.add_user_list) 55 | self.user_list.pack(side=LEFT, ipadx=5, ipady=5, fill=BOTH) 56 | self.list_scorll.config(command=self.user_list.yview) 57 | 58 | self.infofreshbtn = Button(self.bottom_frame, 59 | text='刷新列表', 60 | command=self.add_user_list) 61 | self.infofreshbtn.pack(side=RIGHT) 62 | 63 | self.port_frame.grid(row=0, column=0) 64 | self.list_frame.grid(row=1, column=0) 65 | self.bottom_frame.grid(row=2, column=0) 66 | 67 | def get_ip(self): 68 | return self.ip_var.get() 69 | 70 | def get_port(self): 71 | return self.port_var.get() 72 | 73 | def do_close_server(self): 74 | memory.server_socket_listener.close() 75 | memory.server_socket.close() 76 | memory.online_user.clear() 77 | 78 | def do_open_server(self): 79 | memory.server_socket_listener = server_socket.\ 80 | server(self.get_ip(), self.get_port()) 81 | t1 = Thread(target=server_socket.server_handler, 82 | args=(memory.server_socket_listener,)) 83 | t1.start() 84 | t1.join(1) 85 | 86 | def add_user_list(self): 87 | self.user_list.delete("0", END) 88 | for key in memory.online_user: 89 | t = memory.online_user[key] 90 | self.user_list.insert(END, '{:30}{:30}{:15}' 91 | .format(t[0], t[1], t[2])) 92 | 93 | def close_window(self): 94 | try: 95 | memory.server_socket.close() 96 | except Exception: 97 | pass 98 | os._exit(0) 99 | 100 | 101 | def run(): 102 | root = Tk() 103 | ServerForm(root) 104 | root.protocol("WM_DELETE_WINDOW", memory.window.close_window) 105 | root.mainloop() 106 | 107 | 108 | if __name__ == "__main__": 109 | run() 110 | -------------------------------------------------------------------------------- /client/common_handler.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Author:jeremyjone 3 | Date:2018-5-7 4 | 5 | This is common handler module 6 | 7 | Socket send and recive data Module: 8 | struct.pack two layers, 9 | first layer pack message and data. 10 | second layer pack some necessary message incule 11 | MessageType, MessageLength, messageself/dataself. 12 | struct.unpack three layers, 13 | first layer unpack_from by fmt("LLL"), get a tuple 14 | include MessageType, MessageLength. 15 | second layer unpack and get the received message. 16 | third layer unpack can get messageself/dataself. 17 | 18 | Secure encryption Module: 19 | a cryptographic operation for a password. 20 | ''' 21 | import struct 22 | from Crypto.Cipher import AES 23 | from binascii import b2a_hex, a2b_hex 24 | 25 | 26 | class prpcrypt(): 27 | 28 | def __init__(self, key): 29 | self.key = key 30 | self.mode = AES.MODE_CBC 31 | 32 | def encrypt(self, text): 33 | ''' 34 | Encrypt emthod. 35 | 36 | The encrypt key must be 16(AES-128) / 24(AES-192) / 32(AES-256) bytes. 37 | If text not the multiplier of 16, must be complemented. 38 | After encrypt, change to Hexadecimal. 39 | ''' 40 | cryptor = AES.new(self.key, self.mode, self.key) 41 | # text = text.encode("utf-8") 42 | length = 16 43 | count = len(text) 44 | add = length - (count % length) 45 | text = text + (b'\0' * add) 46 | self.ciphertext = cryptor.encrypt(text) 47 | return b2a_hex(self.ciphertext) 48 | 49 | def decrypt(self, text): 50 | ''' 51 | Decrypt method. 52 | After decrypt, use strip() cut blanks. 53 | ''' 54 | cryptor = AES.new(self.key, self.mode, self.key) 55 | plain_text = cryptor.decrypt(a2b_hex(text)) 56 | return plain_text.rstrip(b'\0') 57 | 58 | 59 | def pack_message(MessageType, *args): 60 | fmt = '' 61 | for i in args: 62 | if type(i) == int: 63 | fmt += "L" 64 | elif type(i) == bytes: 65 | fmt += str(len(i)) + "s" 66 | elif type(i) == float: 67 | fmt += "f" 68 | print("fmt>>", fmt) 69 | serializeMessage = struct.pack(fmt, *args) 70 | fmt = fmt.encode() 71 | fmt_send = "!LLL" + str(len(fmt)) + "s" + str(len(serializeMessage)) + "s" 72 | serializeData = struct.pack(fmt_send, MessageType, len(fmt), 73 | len(serializeMessage), fmt, serializeMessage) 74 | pack_to_send = prpcrypt("jeremyjonejeremy").encrypt(serializeData) 75 | return pack_to_send 76 | 77 | 78 | def unpack_message(data): 79 | serializeMessage = prpcrypt("jeremyjonejeremy").decrypt(data) 80 | if struct.unpack_from("!L", serializeMessage)[0] > 200: 81 | # 表示坏球了 82 | return struct.unpack_from("!L", serializeMessage) 83 | 84 | fmt = "!LLL" 85 | # layer1 86 | _t = struct.unpack_from(fmt, serializeMessage) 87 | # MessageType 88 | get_message = [_t[0]] 89 | fmt += str(_t[1]) + "s" + str(_t[2]) + "s" 90 | # layer2 91 | _msg = struct.unpack(fmt, serializeMessage) 92 | # layer3 93 | res = struct.unpack(_msg[3].decode(), _msg[4]) 94 | for i in res: 95 | # get a list ---> [MessageType, data1, data2 ...] 96 | get_message.append(i) 97 | return get_message 98 | 99 | 100 | class MessageType: 101 | # === Client Action 1-100 102 | # username, password 103 | login = 1 104 | # username, passowrd, nickname 105 | register = 2 106 | friend_list = 3 107 | add_friend = 4 108 | confirm_friend_request = 5 109 | delete_friend = 6 110 | query_friend = 7 111 | 112 | send_message = 11 113 | chatroom_message = 12 114 | 115 | join_room = 21 116 | create_room = 22 117 | query_room_users = 23 118 | leave_room = 24 119 | bad = 44 120 | 121 | logout = 88 122 | 123 | # === Server Action 101-200 124 | login_successful = 100 125 | register_successful = 101 126 | contact_info = 103 127 | chat_history = 104 128 | query_friend_list = 105 129 | add_friend_request = 106 130 | add_friend_result = 107 131 | friend_on_off_line = 108 132 | 133 | large_file = 111 134 | 135 | create_room_res = 114 136 | query_room_users_result = 115 137 | room_user_on_off_line = 116 138 | 139 | on_new_message = 121 140 | chatroom_msg = 122 141 | incoming_friend_request = 123 142 | join_leave_chatroom = 124 143 | 144 | delete_friend_failed = 141 145 | 146 | broadcast = 198 147 | broadcast_to_client = 199 148 | 149 | # === Failure 201-300 150 | login_failed = 201 151 | username_taken = 202 152 | general_failure = 203 153 | general_msg = 204 154 | user_not_exist = 205 155 | -------------------------------------------------------------------------------- /server/common_handler.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Author:jeremyjone 3 | Date:2018-5-7 4 | 5 | This is common handler module 6 | 7 | Socket send and recive data Module: 8 | struct.pack three layers, 9 | first layer pack message and data. 10 | second layer pack some necessary message include 11 | MessageType, MessageLength, messageself/dataself. 12 | third layer is a encrypt layer. 13 | struct.unpack four layers, 14 | first layer is decrypt layer. 15 | second layer unpack_from by fmt("LLL"), get a tuple 16 | include MessageType, MessageLength. 17 | third layer unpack and get the received message. 18 | forth layer unpack can get messageself/dataself. 19 | 20 | Secure encryption Module: 21 | a cryptographic operation for a password. 22 | ''' 23 | import struct 24 | from Crypto.Cipher import AES 25 | from binascii import b2a_hex, a2b_hex 26 | 27 | 28 | class prpcrypt(): 29 | 30 | def __init__(self, key): 31 | self.key = key 32 | self.mode = AES.MODE_CBC 33 | 34 | def encrypt(self, text): 35 | ''' 36 | Encrypt emthod. 37 | 38 | The encrypt key must be 16(AES-128) / 24(AES-192) / 32(AES-256) bytes. 39 | If text not the multiplier of 16, must be complemented. 40 | After encrypt, change to Hexadecimal. 41 | ''' 42 | cryptor = AES.new(self.key, self.mode, self.key) 43 | # text = text.encode("utf-8") 44 | length = 16 45 | count = len(text) 46 | add = length - (count % length) 47 | text = text + (b'\0' * add) 48 | self.ciphertext = cryptor.encrypt(text) 49 | return b2a_hex(self.ciphertext) 50 | 51 | def decrypt(self, text): 52 | ''' 53 | Decrypt method. 54 | After decrypt, use strip() cut blanks. 55 | ''' 56 | cryptor = AES.new(self.key, self.mode, self.key) 57 | plain_text = cryptor.decrypt(a2b_hex(text)) 58 | return plain_text.rstrip(b'\0') 59 | 60 | 61 | def pack_message(MessageType, *args): 62 | fmt = '' 63 | for i in args: 64 | if type(i) == int: 65 | fmt += "L" 66 | elif type(i) == bytes: 67 | fmt += str(len(i)) + "s" 68 | elif type(i) == float: 69 | fmt += "f" 70 | # layer 1(data layer) 71 | serializeMessage = struct.pack(fmt, *args) 72 | 73 | # layer 2(message layer) 74 | fmt = fmt.encode() 75 | fmt_send = "!LLL" + str(len(fmt)) + "s" + str(len(serializeMessage)) + "s" 76 | serializeData = struct.pack(fmt_send, MessageType, len(fmt), 77 | len(serializeMessage), fmt, serializeMessage) 78 | 79 | # encrypt layer 80 | pack_to_send = prpcrypt("jeremyjonejeremy").encrypt(serializeData) 81 | return pack_to_send 82 | 83 | 84 | def unpack_message(data): 85 | # decrypt layer 86 | serializeMessage = prpcrypt("jeremyjonejeremy").decrypt(data) 87 | 88 | # layer 2(message layer) 89 | if struct.unpack_from("!L", serializeMessage)[0] > 200: 90 | # 表示坏球了 91 | return struct.unpack_from("!L", serializeMessage) 92 | fmt = "!LLL" 93 | 94 | # layer 1(data layer) 95 | _t = struct.unpack_from(fmt, serializeMessage) 96 | # MessageType 97 | get_message = [_t[0]] 98 | fmt += str(_t[1]) + "s" + str(_t[2]) + "s" 99 | # level 1 100 | _msg = struct.unpack(fmt, serializeMessage) 101 | # level 2 102 | res = struct.unpack(_msg[3].decode(), _msg[4]) 103 | for i in res: 104 | # get a list ---> [MessageType, data1, data2 ...] 105 | get_message.append(i) 106 | return get_message 107 | 108 | 109 | class MessageType: 110 | # === Client Action 1-100 111 | # username, password 112 | login = 1 113 | # username, passowrd, nickname 114 | register = 2 115 | friend_list = 3 116 | add_friend = 4 117 | confirm_friend_request = 5 118 | delete_friend = 6 119 | query_friend = 7 120 | 121 | send_message = 11 122 | chatroom_message = 12 123 | 124 | join_room = 21 125 | create_room = 22 126 | query_room_users = 23 127 | leave_room = 24 128 | bad = 44 129 | 130 | logout = 88 131 | 132 | # === Server Action 101-200 133 | login_successful = 100 134 | register_successful = 101 135 | contact_info = 103 136 | chat_history = 104 137 | query_friend_list = 105 138 | add_friend_request = 106 139 | add_friend_result = 107 140 | friend_on_off_line = 108 141 | 142 | large_file = 111 143 | 144 | create_room_res = 114 145 | query_room_users_result = 115 146 | room_user_on_off_line = 116 147 | 148 | on_new_message = 121 149 | chatroom_msg = 122 150 | incoming_friend_request = 123 151 | join_leave_chatroom = 124 152 | 153 | delete_friend_failed = 141 154 | 155 | broadcast = 198 156 | broadcast_to_client = 199 157 | 158 | # === Failure 201-300 159 | login_failed = 201 160 | username_taken = 202 161 | general_failure = 203 162 | general_msg = 204 163 | user_not_exist = 205 164 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 项目简介 2 | 简易聊天室 3 | - Author: by @jeremyjone 4 | - Date: 2018-5-10 5 | - 项目会保存在: https://github.com/jeremyjone 6 | 7 | 8 | # 功能 9 | - [x] 用AES加密所有的传输内容 10 | - [x] 用MD5 + 加盐 的方式存储密码,加盐字符由客户端和服务器共同生成 11 | - [x] 使用数据库存储用户信息、好友关系、房间信息、加入房间状态、所有聊天记录 12 | - [x] tkinter GUI 13 | - [x] 有新消息时自动好友列表提示 14 | - [x] 窗口放大缩小 15 | - [x] 联系人列表;未读的消息用彩色文本标注 16 | - [x] 加好友功能,对方收到通知,通过/拒绝,并将添加记录添加到数据库 17 | - [x] 右键好友名可以删除好友关系 18 | - [x] 防止重复打开窗口,如果已经打开则使窗口获得焦点 19 | - [x] 用户离线时收到的未读的消息,再次登录时用彩色文本标注 20 | - [x] 支持多行内容(Enter换行,Ctrl+Enter发送);支持聊天字体的设置 21 | - [x] 群聊功能、加群、创建群 22 | - [x] 群聊中显示群成员(双击打开聊天窗口/发送好友请求) 23 | 24 | # 安装说明 25 | Python版本: 3.5 26 | 27 | ``` 28 | pip install pycrypto # 用于加密 29 | ``` 30 | 31 | # 运行方法 32 | ``` 33 | python run_client.py 34 | python run_server.py 35 | ``` 36 | (一次只能运行一个server,但可以运行N个client) 37 | 38 | 第一次运行前,先运行 39 | ``` 40 | first_time_run_server_create_database.py 1 41 | ``` 42 | 可以快速创建数据库,(需要有参数1); 43 | 参数为2时创建几条数据,方便使用(前提创建好数据库); 44 | 参数为3时,删除数据库 45 | 46 | 47 | # 文件目录 48 | ``` 49 | ├─README.md 50 | ├─first_time_run_server_create_database.py 51 | ├─run_client.py 52 | ├─run_server.py 53 | │ 54 | ├─client 55 | │ __init__.py 56 | | 57 | │ chat_form.py 58 | │ contact_form.py 59 | │ login.py 60 | │ register.py 61 | │ memory.py 62 | | 63 | │ client_socket.py 64 | | common_socket.py 65 | | security.py 66 | | 67 | └─server 68 | __init__.py 69 | 70 | DB_Handler.py 71 | server_windows.py 72 | common_handler.py 73 | server_socket.py 74 | memory.py 75 | 76 | register.py 77 | login.py 78 | mamage_friend.py 79 | manage_group.py 80 | chat_msg.py 81 | ``` 82 | 83 | # 预览图 84 | ![1](image/001.jpg) 85 | ![2](image/002.jpg) 86 | ![3](image/003.jpg) 87 | ![4](image/004.jpg) 88 | ![5](image/005.jpg) 89 | 90 | # 自制信息传输协议说明 91 | 92 | 1. 无状态。 93 | 2. 使用时,函数可以传入任意多个需要传送的数据,注意需要传入二进制文件。 94 | 3. 每个Type对应一种固定的操作,对应固定的参数列表,Type类型需要在调用函数时提供。 95 | 96 | 这种设计的优点 97 | 98 | 1. 简单易懂,易于测试(因为无状态)。 99 | 2. 灵活方便,无需为不同的操作类型写不同的处理程序。 100 | 3. 可以传输非常复杂的数据结构(如:登入时的所有N条未读消息(每条都含有发件人/时间/消息内容/甚至图片二进制)都是放在一个包里的)。 101 | 4. 可以传输任意的二进制文件,对于功能扩展非常方便,比如图片、音视频等等。 102 | 5. 可以很好的与其他语言编写的端口进行数据交互(C、C++等) 103 | 104 | 这种设计的缺点 105 | 106 | 1. 所有数据没有进行压缩处理,略浪费流量。 107 | 108 | 在数据传输的基础上,增加安全性层级: 109 | 110 | 1. 自制的数据传输协议上,套加一层加密处理(密钥现在是固定的,可以扩展为数据交换两端共同计算得出的结果)。 111 | 112 | 113 | # 协议详细说明 114 | 115 | *注:具体实现可以看```commcon_handler.py```* 116 | 117 | 协议设计的宗旨是:无状态、分层设计、包头是数据Type,然后是数据内容,指定打包的第一层解包格式为"!L"。 118 | 119 | 网络包分为三层,第一层(外层)为数据加密层,第二层为数据结构层,第三层(内层)为数据体。构造分别如下: 120 | 121 | ## 层1: 122 | 123 | 1. Message Body *被AES加密的内容* 124 | 125 | ## 层2: 126 | 127 | 1. Message Type *发送的数据类型,根据数据类型分发数据* 128 | 3. Format of Data *具体数据的打包格式* 129 | 130 | ## 层3(基础类型如int、str、bool、float、binary等): 131 | 132 | 1. Packed Data (Using struct.pack in Python) *(直接转成bytes)* 133 | 134 | 一般来说,真正使用时,会直接给函数传递多个参数,这样可以一次和服务器交换多个参数。 135 | 136 | 比如```socket.send(MessageType.login, username, password)``` 137 | 138 | 这种设计非常强大,结合AES加密,可以无缝和服务器**安全**交换非常复杂的数据。比如,一个用户的所有离线未读消息(有N条,每条可能是不同的房间、不用的发送者,都包含不同的时间、字体大小,可能还有图片),可以通过一个包发送给客户端,效率非常高。 139 | 140 | # 用MySQL存储用户信息、消息记录等各种数据 141 | 数据库结构如下: 142 | 143 | ``` 144 | create table userinfo( 145 | -> id int primary key auto_increment, 146 | -> username varchar(50) unique not null, 147 | -> password varchar(254) not null, 148 | -> nickname varchar(50) not null, 149 | -> reg_time timestamp not null, 150 | -> isActive boolean not null)default charset=utf8; 151 | 152 | create table chatmsg( 153 | -> id int primary key auto_increment, 154 | -> user_id int not null, 155 | -> send_time timestamp not null, 156 | -> target_id int not null, 157 | -> isRead boolean not null, 158 | -> msg_type tinyint not null, 159 | -> msg varchar(4096) not null, 160 | -> isActive boolean not null)default charset=utf8; 161 | 162 | create table userfriend( 163 | -> id int primary key auto_increment, 164 | -> user_id int not null, 165 | -> friend_id int not null, 166 | -> add_time timestamp not null, 167 | -> isActive boolean not null)default charset=utf8; 168 | 169 | create table chatroom( 170 | -> id int primary key auto_increment, 171 | -> chatroom_name varchar(30) unique not null, 172 | -> create_time timestamp not null, 173 | -> isActive boolean not null)default charset=utf8; 174 | 175 | create table chatroom_user( 176 | -> id int primary key auto_increment, 177 | -> chatroom_id int not null, 178 | -> user_id int not null, 179 | -> create_time timestamp not null, 180 | -> isActive boolean not null)default charset=utf8; 181 | ``` 182 | 183 | 我做的所有数据没有delete选项,只有逻辑删除,默认isActive都为1,如果不需要了,改为0即可达到删除效果。 184 | 185 | ```chatmsg```表可以保存不同类型数据,用msg_type保存数字即可,默认聊天数据为1,系统消息为2,添加好友信息为3,群聊信息为4,这样可以方便不同类型消息的扩展;保存消息时先判断用户是否在线,如果在线,直接发送给用户并在保存数据时将isRead项保存为0,否则保存为1,当用户上线时读取该用户isRead项为1的所有消息。 186 | 187 | # 功能扩展 188 | 189 | 该程序分层设计,可扩展性良好,添加不同模块,定义不同的Type类型即可。 190 | 1. 添加表情 191 | 2. 传图 192 | 3. 传文件 193 | 4. 在聊天窗口显示类似QQ秀的可视化视图 194 | 5. 改进好友列表,使用Frame可以显示更多信息,比如未读消息条数,未读消息的概要,好友在线离线等 195 | 6. 改进信息交换机制,进一步支持更多更全的功能 196 | 197 | 未完待续... 198 | 199 | # 目前问题 200 | 201 | 1. 收取群聊信息,如果发送方和接收方为好友,那么接收方会在私聊接收到,问题已定位。 202 | 2. 客户端tkinter弹出框卡死,线程锁问题,但是目前还没想好将线程锁添加在什么地方。 203 | -------------------------------------------------------------------------------- /server/server_socket.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Author:jeremyjone 3 | Date:2018-5-5 4 | 5 | This module contains socket information, allowing the 6 | server to monitor the information sent by the client, 7 | and to handle and distribute the requests, and return 8 | the corresponding data to the client. 9 | ''' 10 | from socket import * 11 | from threading import * 12 | import os 13 | import struct 14 | 15 | from . import memory, login, chat_msg, manage_friend,\ 16 | manage_group, register, common_handler 17 | 18 | 19 | def server(IP, PORT): 20 | ''' 21 | Create socket by TCP. 22 | ''' 23 | sk = socket(AF_INET, SOCK_STREAM) 24 | sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) 25 | sk.bind((IP, int(PORT))) 26 | sk.listen(5) 27 | memory.server_socket = sk 28 | return sk 29 | 30 | 31 | def distribute_handler(s, ad): 32 | ''' 33 | Receive data and distribute them to different modules 34 | for processing respectively. 35 | ''' 36 | while True: 37 | try: 38 | data = s.recv(4096) 39 | msg = common_handler.unpack_message(data) 40 | # Recv large file 41 | if msg[0] == common_handler.MessageType.large_file: 42 | msg_buffer += msg[1] 43 | if msg[2] == 0: 44 | msg = msg_buffer 45 | msg_buffer = None 46 | else: 47 | continue 48 | 49 | if msg[0] == common_handler.MessageType.register: 50 | # Register 51 | print("接收到注册请求") 52 | register.register_handler(s, msg) 53 | 54 | elif msg[0] == common_handler.MessageType.login: 55 | # Login 56 | print("接收到登录请求") 57 | login.login_handler(s, ad, msg) 58 | 59 | elif msg[0] == common_handler.MessageType.add_friend: 60 | # Add friend 61 | print("接收到添加好友请求") 62 | manage_friend.add_friend_handler(s, msg) 63 | 64 | elif msg[0] == common_handler.MessageType.confirm_friend_request: 65 | # confirm add friend 66 | print("接收到确认添加好友请求") 67 | manage_friend.confirm_handler(s, msg) 68 | 69 | elif msg[0] == common_handler.MessageType.delete_friend: 70 | # delete friend 71 | print("接收到删除好友请求") 72 | manage_friend.del_friend_handler(s, msg) 73 | 74 | elif msg[0] == common_handler.MessageType.query_friend: 75 | # Get friend infomation 76 | print("接收到获取好友列表请求") 77 | manage_friend.get_friend_handler(s) 78 | 79 | elif msg[0] == common_handler.MessageType.send_message: 80 | # Chat message 81 | print("接收到发送消息请求") 82 | chat_msg.userchat_handler(msg) 83 | 84 | elif msg[0] == common_handler.MessageType.chatroom_message: 85 | # Chatroom message 86 | print("接收到聊天室信息请求") 87 | chat_msg.chatroom_handler(s, msg) 88 | 89 | elif msg[0] == common_handler.MessageType.broadcast: 90 | # Broadcast message 91 | print("接收到广播请求") 92 | chat_msg.broadcast_handler(s, msg) 93 | 94 | elif msg[0] == common_handler.MessageType.create_room: 95 | # Create chatroom 96 | print("接收到创建群聊请求") 97 | manage_group.chatroom_handler(s, msg) 98 | 99 | elif msg[0] == common_handler.MessageType.join_room: 100 | # User join/leave chatroom 101 | print("接收到加入/退出群聊请求") 102 | manage_group.user_join_leave_handler(s, msg, "join") 103 | 104 | elif msg[0] == common_handler.MessageType.leave_room: 105 | # User join/leave chatroom 106 | print("接收到加入/退出群聊请求") 107 | manage_group.user_join_leave_handler(s, msg, "leave") 108 | 109 | elif msg[0] == common_handler.MessageType.logout: 110 | # User logout 111 | print("接收到用户登出信号") 112 | login.logout_handler(s) 113 | 114 | elif msg[0] == common_handler.MessageType.query_room_users: 115 | print("收到用户请求刷新聊天室列表") 116 | manage_group.query_chatroom_user(s, msg) 117 | 118 | except struct.error: 119 | pass 120 | except ConnectionResetError as e: 121 | print(e) 122 | del memory.online_user[s] 123 | memory.window.add_user_list() 124 | except OSError as e: 125 | pass 126 | # except Exception as e: 127 | # print("服务器接收信息时遇到一个未知问题 >>", e) 128 | 129 | 130 | def server_handler(sk): 131 | ''' 132 | Loop monitor, receive data, simple handler and distribute 133 | data to different modules for further processing. 134 | ''' 135 | print("Server is running...") 136 | while True: 137 | try: 138 | c, ad = sk.accept() 139 | print(ad) 140 | except KeyboardInterrupt: 141 | os._exit(0) 142 | except Exception: 143 | continue 144 | 145 | t1 = Thread(target=distribute_handler, args=(c, ad,)) 146 | t1.start() 147 | 148 | 149 | def run(IP, PORT): 150 | s = server(IP, PORT) 151 | server_handler(s) 152 | 153 | 154 | if __name__ == "__main__": 155 | run() 156 | -------------------------------------------------------------------------------- /first_time_run_server_create_database.py: -------------------------------------------------------------------------------- 1 | import pymysql 2 | import sys 3 | from Crypto.Hash import MD5 4 | 5 | 6 | def loop_encrypt(pwd, n=10): 7 | # Salt encrypt and recursion 10 times. 8 | salt = 'jeremyjone' 9 | md5_obj = MD5.new() 10 | md5_obj.update((pwd + salt).encode()) 11 | # print(n, md5_obj.hexdigest()) 12 | if n == 1: 13 | return md5_obj.hexdigest() 14 | return loop_encrypt(md5_obj.hexdigest(), n - 1) 15 | 16 | 17 | class DB_Handler(object): 18 | def __init__(self): 19 | self.local = 'localhost' 20 | self.db_login_name = 'root' 21 | self.db_login_pswd = '123456' 22 | self.userinfo = "userinfo" 23 | self.chatmsg = "chatmsg" 24 | self.userfriend = "userfriend" 25 | self.chatroom = "chatroom" 26 | self.chatroomuser = "chatroom_user" 27 | 28 | def connect_to_DB(self, sql_statment, db=None): 29 | ''' 30 | Connect to database by base infomation and create database 31 | handler module, it can receive one SQL and execute. 32 | 33 | If operate successfully return OK, conversely return NG. 34 | ''' 35 | self.db = db 36 | _ = None 37 | sql = pymysql.connect(self.local, 38 | self.db_login_name, 39 | self.db_login_pswd, 40 | charset='utf8') 41 | 42 | # Create cursor 43 | cursor = sql.cursor() 44 | 45 | if self.db is not None: 46 | cursor.execute("use %s" % self.db) 47 | 48 | try: 49 | cursor.execute(sql_statment) 50 | sql.commit() 51 | _ = 'OK' 52 | except Exception as e: 53 | sql.rollback() 54 | print(e) 55 | _ = "NG" 56 | # close cursor 57 | cursor.close() 58 | # close database 59 | sql.close() 60 | return _ 61 | 62 | def do_create(self): 63 | cdsql = 'create database chatroom;' 64 | 65 | ctsql1 = '''create table userinfo( 66 | id int primary key auto_increment, 67 | username varchar(50) unique not null, 68 | password varchar(254) not null, 69 | nickname varchar(50) not null, 70 | reg_time timestamp not null, 71 | isActive boolean not null)default charset=utf8; 72 | ''' 73 | 74 | ctsql2 = '''create table chatmsg( 75 | id int primary key auto_increment, 76 | user_id int not null, 77 | send_time timestamp not null, 78 | target_id int not null, 79 | isRead boolean not null, 80 | msg_type tinyint not null, 81 | msg varchar(4096) not null, 82 | isActive boolean not null)default charset=utf8; 83 | ''' 84 | 85 | ctsql3 = '''create table userfriend( 86 | id int primary key auto_increment, 87 | user_id int not null, 88 | friend_id int not null, 89 | add_time timestamp not null, 90 | isActive boolean not null)default charset=utf8; 91 | ''' 92 | 93 | ctsql4 = '''create table chatroom( 94 | id int primary key auto_increment, 95 | chatroom_name varchar(30) unique not null, 96 | create_time timestamp not null, 97 | isActive boolean not null)default charset=utf8; 98 | ''' 99 | 100 | ctsql5 = '''create table chatroom_user( 101 | id int primary key auto_increment, 102 | chatroom_id int not null, 103 | user_id int not null, 104 | create_time timestamp not null, 105 | isActive boolean not null)default charset=utf8; 106 | ''' 107 | 108 | self.connect_to_DB(cdsql) 109 | self.connect_to_DB(ctsql1, db="chatroom") 110 | self.connect_to_DB(ctsql2, db="chatroom") 111 | self.connect_to_DB(ctsql3, db="chatroom") 112 | self.connect_to_DB(ctsql4, db="chatroom") 113 | self.connect_to_DB(ctsql5, db="chatroom") 114 | 115 | def do_delete(self): 116 | deldatabase = 'drop database chatroom;' 117 | self.connect_to_DB(deldatabase) 118 | 119 | def do_insertdata(self): 120 | username = ["admin", "jeremyjone", "jeremy", "tommy", "abc"] 121 | password = [loop_encrypt("123"), loop_encrypt("123"), loop_encrypt("123"), loop_encrypt("123"), loop_encrypt("123456")] 122 | nickname = ["管理员", "Jeremy", "小鹰", "TOMMY", "abc"] 123 | for i in range(5): 124 | userinfo = "insert into userinfo (username, password, nickname, isActive) values ('%s', '%s', '%s', %d);" % (username[i], password[i], nickname[i], 1) 125 | self.connect_to_DB(userinfo, db="chatroom") 126 | 127 | # chatroom = "insert into chatroom (chatroom_name, isActive) values (('No.1聊天室', 1), ('JEREMYJONE', 1));" 128 | # self.connect_to_DB(chatroom, db="chatroom") 129 | 130 | # userfriend = "insert into userfriend (user_id, friend_id, isActive) values ((1, 2, 1), (2, 1, 1), (1, 3, 1), (3, 1, 1), (1, 4, 1), (4, 1, 1), (2, 4, 1), (4, 2, 1), (3, 5, 1), (5, 3, 1), (2, 5, 1), (5, 2, 1));" 131 | # self.connect_to_DB(userfriend, db="chatroom") 132 | 133 | # chatroom_user = 'insert into chatroom_user (chatroom_id, user_id, isActive) values ((1, 1, 1), (1, 2, 1), (1, 3, 1), (1, 4, 1), (2, 1, 1), (2, 3, 1), (2, 4, 1), (2, 5, 1));' 134 | # self.connect_to_DB(chatroom_user, db="chatroom") 135 | 136 | 137 | if sys.argv[1] == "1": 138 | DB_Handler().do_create() 139 | elif sys.argv[1] == "2": 140 | DB_Handler().do_insertdata() 141 | elif sys.argv[1] == "3": 142 | DB_Handler().do_delete() 143 | -------------------------------------------------------------------------------- /client/contact_form.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @title: Contact window. 3 | @Author: jeremyjone 4 | date: 2018-5-6 5 | 6 | Keep general information for client. 7 | ''' 8 | from tkinter import * 9 | import tkinter as tk 10 | from tkinter import messagebox 11 | import time 12 | import struct 13 | from threading import Thread 14 | import os 15 | 16 | from . import memory, chat_form, client_socket, common_handler 17 | 18 | 19 | class contact_window(tk.Frame): 20 | def on_add_friend(self): 21 | def do_add_friend(): 22 | friend_name = input_name.get().encode() 23 | serializeMessage = common_handler.pack_message(common_handler.MessageType.add_friend, friend_name) 24 | client_socket.send_msg(serializeMessage) 25 | add_friend_form.destroy() 26 | messagebox.showinfo('添加好友', '好友请求已发送') 27 | 28 | add_friend_form = Toplevel() 29 | add_friend_form.title("添加好友") 30 | lb = Label(add_friend_form, text='要查找的好友名或ID') 31 | input_name = Entry(add_friend_form) 32 | btn = Button(add_friend_form, text='走你!', command=do_add_friend) 33 | lb.pack() 34 | input_name.pack() 35 | btn.pack() 36 | 37 | def on_add_room(self): 38 | def create_room(): 39 | chatroom_name = input_name.get().encode() 40 | name = memory.username.encode() 41 | serializeMessage = common_handler.pack_message(common_handler.MessageType.join_room, chatroom_name, name) 42 | client_socket.send_msg(serializeMessage) 43 | create_room_form.destroy() 44 | 45 | create_room_form = Toplevel() 46 | create_room_form.title("加入群") 47 | lb = Label(create_room_form, text='赶快找到你的组织吧') 48 | input_name = Entry(create_room_form) 49 | btn = Button(create_room_form, text='我确定找对了!', command=create_room) 50 | lb.pack() 51 | input_name.pack() 52 | btn.pack() 53 | 54 | def on_create_room(self): 55 | def create_room(): 56 | chatroom_name = input_name.get().encode() 57 | serializeMessage = common_handler.pack_message(common_handler.MessageType.create_room, chatroom_name) 58 | client_socket.send_msg(serializeMessage) 59 | create_room_form.destroy() 60 | 61 | create_room_form = Toplevel() 62 | create_room_form.title("创建群") 63 | lb = Label(create_room_form, text='快给您的后宫起个响亮的名字吧!') 64 | input_name = Entry(create_room_form) 65 | btn = Button(create_room_form, text='走你!就是TA了!', command=create_room) 66 | lb.pack() 67 | input_name.pack() 68 | btn.pack() 69 | 70 | def on_list_click(self, e): 71 | nickname = self.friend_list.get(self.friend_list.curselection()) 72 | chat_form.run(nickname) 73 | 74 | # class get_list_name: 75 | # def __init__(self, e): 76 | # self.e = e 77 | 78 | # def get_name(): 79 | # nickname = memory.tk_root.friend_list.\ 80 | # get(memory.tk_root.friend_list.curselection()) 81 | # print(nickname) 82 | # if "群" in nickname: 83 | # return "退出该群" 84 | # else: 85 | # return "与TA绝交" 86 | 87 | # def get_list_name(self): 88 | # nickname = self.friend_list.get(self.friend_list.curselection()) 89 | 90 | def popupmenu(self, e): 91 | self.delete_menu.post(e.x_root, e.y_root) 92 | 93 | def delete_friend(self): 94 | name = self.friend_list.get(self.friend_list.curselection()) 95 | _tmp = name.split() 96 | if _tmp[0] == "群": 97 | username = _tmp[2] 98 | username = username[1:] 99 | username = username[:-1] 100 | _flag = 2 101 | else: 102 | username = _tmp[1] 103 | username = username[1:] 104 | username = username[:-1] 105 | _flag = 1 106 | do_delete_friend(_flag, username) 107 | 108 | def __init__(self, master=None): 109 | memory.Contact_window.append(self) 110 | super().__init__(master) 111 | self.master = master 112 | memory.tk_root = self 113 | master.geometry('%dx%d' % (260, 600)) 114 | 115 | self.delete_menu = Menu(self.master, tearoff=0) 116 | self.delete_menu.add_command(label="与TA绝交", command=self.delete_friend) 117 | self.delete_menu.add_separator() 118 | self.base_frame = Frame(self.master) 119 | self.scroll = Scrollbar(self.base_frame) 120 | self.scroll.pack(side=RIGHT, fill=Y) 121 | self.friend_list = Listbox(self.base_frame, 122 | yscrollcommand=self.scroll.set) 123 | self.friend_list.bind("", self.on_list_click) 124 | # self.friend_list.bind("", self.get_list_name) 125 | self.friend_list.bind("", self.popupmenu) 126 | self.scroll.config(command=self.friend_list.yview) 127 | self.friend_list.pack(expand=True, fill=BOTH) 128 | 129 | self.button_frame = Frame(self.master) 130 | 131 | self.add_friend = Button(self.button_frame, text="添加好友", 132 | command=self.on_add_friend) 133 | self.add_friend.pack(side=LEFT, expand=True, fill=X) 134 | 135 | self.add_room = Button(self.button_frame, text="添加群", 136 | command=self.on_add_room) 137 | self.add_room.pack(side=LEFT, expand=True, fill=X) 138 | 139 | self.create_room = Button(self.button_frame, text="创建群", 140 | command=self.on_create_room) 141 | self.create_room.pack(side=LEFT, expand=True, fill=X) 142 | 143 | self.base_frame.pack(expand=True, fill=BOTH) 144 | self.button_frame.pack(expand=False, fill=X) 145 | 146 | self.master.title(memory.current_user[memory.username] + " - 联系人列表") 147 | 148 | def update_friend_list(self, flag_name=None, unflag_name=None): 149 | self.friend_list.delete("0", END) 150 | _flag = 0 151 | for t, f in memory.friend_list: 152 | self.friend_list.insert( 153 | END, memory.friend_list[(t, f)] + " (" + f + ")") 154 | 155 | if f == flag_name: 156 | self.friend_list.itemconfig(_flag, {"fg": "red", "bg": "grey"}) 157 | elif f == unflag_name: 158 | self.friend_list.itemconfig(_flag, {"fg": "black"}) 159 | _flag += 1 160 | 161 | def close_window(self): 162 | # Tell server logout. 163 | flag = b'logout' 164 | serializeMessage = common_handler.pack_message(common_handler.MessageType.logout, flag) 165 | client_socket.send_msg(serializeMessage) 166 | self.master.destroy() 167 | os._exit(0) 168 | 169 | 170 | def recive_some_info(msg): 171 | _flag = msg[0] 172 | if _flag == common_handler.MessageType.user_not_exist: 173 | messagebox.showerror("悲剧了!", "您希望添加的好友好像还没出生~!") 174 | 175 | elif _flag == common_handler.MessageType.add_friend_request: 176 | request_user = msg[1].decode() 177 | result = messagebox.askyesno("好友请求", request_user + "请求加您为好友,是否同意?") 178 | if result is False: 179 | _res = b'NG' 180 | else: 181 | _res = b'OK' 182 | serializeMessage = common_handler.pack_message( 183 | common_handler.MessageType.confirm_friend_request, _res, memory.username.encode(), request_user.encode()) 184 | client_socket.send_msg(serializeMessage) 185 | 186 | elif _flag == common_handler.MessageType.add_friend_result: 187 | if msg[1].decode() == "OK": 188 | messagebox.showinfo("恭喜您!", msg[2].decode() + "愿意跟您促膝长谈啦!") 189 | else: 190 | messagebox.showinfo("不走运", msg[2].decode() + "不愿意搭理你!") 191 | 192 | elif _flag == common_handler.MessageType.join_leave_chatroom: 193 | if msg[1] == b"OK": 194 | username = memory.username.encode() 195 | serializeMessage = common_handler.pack_message(common_handler.MessageType.query_friend, username) 196 | memory.sc.send(serializeMessage) 197 | else: 198 | messagebox.showerror("悲剧了!", "您希望添加的群组像空气~!") 199 | 200 | elif _flag == common_handler.MessageType.delete_friend_failed: 201 | messagebox.showinfo("对不住", msg[1].decode() + "删除失败!") 202 | 203 | 204 | def show(msg): 205 | friend_list_handler(msg) 206 | 207 | 208 | def friend_list_handler(msg): 209 | friend_list = msg[1].decode() 210 | chatroom_list = msg[2].decode() 211 | memory.friend_list.clear() 212 | if friend_list != "no friend": 213 | _friend_info_lst = friend_list.split(" + ") 214 | for _i in _friend_info_lst: 215 | _fl = _i.split(":") 216 | memory.friend_list[(1, _fl[0])] = _fl[1] 217 | if chatroom_list != "no chatroom": 218 | _chat_info_lst = chatroom_list.split(" + ") 219 | for _i in _chat_info_lst: 220 | _cl = _i.split(":") 221 | memory.friend_list[(2, _cl[1])] = "群 " + _cl[1] 222 | memory.tk_root.update_friend_list() 223 | 224 | 225 | def run(username): 226 | # Request friends list 227 | root = Tk() 228 | contact_window(root) 229 | 230 | t = Thread(target=client_socket.keep_recv) 231 | memory.recv_msg_thread = t 232 | t.start() 233 | 234 | time.sleep(0.1) 235 | username = username.encode() 236 | serializeMessage = common_handler.pack_message(common_handler.MessageType.query_friend, username) 237 | memory.sc.send(serializeMessage) 238 | root.protocol("WM_DELETE_WINDOW", memory.tk_root.close_window) 239 | root.mainloop() 240 | t.join() 241 | 242 | 243 | def chatroom_handler(msg): 244 | m = msg[1].decode() 245 | 246 | if m == "EXIST": 247 | messagebox.showerror("遗憾了~", "真遗憾,您希望的名字已被别的小主相中,赶快换一个吧!") 248 | elif m == "NG": 249 | messagebox.showerror("悲剧了!", "悲剧了,您到底干了啥,没成功耶!") 250 | elif m == "OK": 251 | chatroom_name = msg[2].decode() 252 | memory.friend_list[(2, chatroom_name)] = "群 " + chatroom_name 253 | memory.tk_root.update_friend_list() 254 | 255 | 256 | def do_delete_friend(flag, username): 257 | if flag == 1: 258 | target_user = username.encode() 259 | me = memory.username.encode() 260 | serializeMessage = common_handler.pack_message( 261 | common_handler.MessageType.delete_friend, target_user, me) 262 | client_socket.send_msg(serializeMessage) 263 | else: 264 | # Leave chatroom. 265 | pass 266 | -------------------------------------------------------------------------------- /client/chat_form.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @title: Chat Window 3 | @Author: jeremyjone 4 | date: 2018-5-6 5 | 6 | Chat widnow for user to input text and send, while receiving and 7 | displaying messages from other people. 8 | ''' 9 | import tkinter as tk 10 | from tkinter import * 11 | from tkinter.scrolledtext import ScrolledText 12 | from tkinter import colorchooser 13 | from tkinter import simpledialog 14 | import struct 15 | import datetime as dtime 16 | 17 | from . import client_socket, memory, common_handler 18 | 19 | 20 | class ChatForm(tk.Frame): 21 | font_color = "#000000" 22 | font_size = 12 23 | 24 | def on_list_click(self, e): 25 | name = self.chatroom_user_list.get( 26 | self.chatroom_user_list.curselection()) 27 | for tmp in memory.chatroom_user_list[self.username]: 28 | if tmp[1] == name: 29 | uname = tmp[0] 30 | 31 | for fn in memory.friend_list: 32 | if uname == fn[1]: 33 | # It's friend... 34 | uname = memory.friend_list[fn] + " (" + uname + ")" 35 | run(uname) 36 | return 37 | # Not friend... 38 | result = messagebox.askokcancel( 39 | "还不是好友?", "你和" + name + "还不是好友,是否立即添加?") 40 | if result: 41 | friend_name = uname.encode() 42 | serializeMessage = common_handler.pack_message(common_handler.MessageType.add_friend, friend_name) 43 | client_socket.send_msg(serializeMessage) 44 | messagebox.showinfo('添加好友', '好友请求已发送') 45 | 46 | def __init__(self, master=None, username=None, nickname="Unkown"): 47 | super().__init__(master) 48 | self.master = master 49 | self.username = username 50 | self.nickname = nickname 51 | self.master.resizable(width=True, height=True) 52 | self.master.geometry('660x500') 53 | self.master.minsize(420, 370) 54 | 55 | self.master.title("与 {} 聊天中...".format(self.nickname)) 56 | memory.Chat_window[self.username] = self 57 | print(memory.Chat_window) 58 | 59 | # Chatroom window 60 | 61 | for v in memory.friend_list: 62 | if v[1] == self.username: 63 | if v[0] == 2: 64 | self.left_frame = tk.Frame(self) 65 | 66 | self.scroll = Scrollbar(self.left_frame) 67 | self.scroll.pack(side=RIGHT, fill=Y) 68 | self.chatroom_user_list = Listbox( 69 | self.left_frame, yscrollcommand=self.scroll.set) 70 | self.chatroom_user_list.bind( 71 | "", self.on_list_click) 72 | self.scroll.config(command=self.chatroom_user_list.yview) 73 | self.chatroom_user_list.pack(expand=True, fill=BOTH) 74 | self.update_chatroom_user_list(v[1]) 75 | self.left_frame.pack(side=RIGHT, expand=True, fill=BOTH) 76 | 77 | # self.friend_name = tk.Label( 78 | # self.left_frame, text=nickname, bg='#EEE', width=15) 79 | # self.friend_name.pack(expand=True, fill=BOTH, ipadx=5, ipady=5) 80 | 81 | self.right_frame = tk.Frame(self, bg='white') 82 | self.right_frame.pack(side=LEFT, expand=True, fill=BOTH) 83 | self.input_frame = tk.Frame(self.right_frame) 84 | self.input_textbox = ScrolledText(self.right_frame, height=7) 85 | self.input_textbox.bind("", self.send_message) 86 | self.input_textbox.bind_all('', self.apply_font_change) 87 | 88 | self.send_btn = tk.Button(self.input_frame, text='发送消息(Ctrl+Enter)', 89 | command=self.send_message) 90 | self.send_btn.pack(side=RIGHT, expand=False) 91 | 92 | self.font_btn = tk.Button( 93 | self.input_frame, text='字体颜色', command=self.choose_color) 94 | self.font_btn.pack(side=LEFT, expand=False) 95 | 96 | self.font_btn = tk.Button( 97 | self.input_frame, text='字体大小', command=self.choose_font_size) 98 | self.font_btn.pack(side=LEFT, expand=False) 99 | 100 | # self.image_btn = tk.Button( 101 | # self.input_frame, text='发送图片', command=self.send_image) 102 | # self.image_btn.pack(side=LEFT, expand=False) 103 | 104 | self.chat_box = ScrolledText(self.right_frame, bg='white') 105 | self.input_frame.pack(side=BOTTOM, fill=X, expand=False) 106 | self.input_textbox.pack(side=BOTTOM, fill=X, 107 | expand=False, padx=(0, 0), pady=(0, 0)) 108 | self.chat_box.pack(side=BOTTOM, fill=BOTH, expand=True) 109 | self.chat_box.bind("", lambda e: "break") 110 | self.chat_box.tag_config( 111 | "default", lmargin1=10, lmargin2=10, rmargin=10) 112 | self.chat_box.tag_config("me", foreground="green", spacing1='5') 113 | self.chat_box.tag_config("them", foreground="blue", spacing1='5') 114 | self.chat_box.tag_config("message", foreground="black", spacing1='0') 115 | self.chat_box.tag_config("system", foreground="grey", spacing1='0', 116 | justify='center', font=(None, 8)) 117 | 118 | self.pack(expand=True, fill=BOTH, padx=5, pady=5, ipadx=5, ipady=5) 119 | 120 | def append_to_chat_box(self, time, user, message, tags): 121 | if user == memory.username: 122 | user = "我" 123 | time_info = "%s %s 说:\n" % (time, user) 124 | self.chat_box.insert(tk.END, time_info, [tags, 'message']) 125 | self.chat_box.insert(tk.END, message, [tags, 'default']) 126 | self.chat_box.insert(tk.END, "\n", [tags, 'message']) 127 | self.chat_box.update() 128 | self.chat_box.see(tk.END) 129 | 130 | def send_message(self, _=None): 131 | stime = dtime.datetime.now() 132 | time_info = "%s年%s月%s日 %s时%s分%s秒" % ( 133 | stime.year, stime.month, stime.day, 134 | stime.hour, stime.minute, stime.second) 135 | message = self.input_textbox.get("1.0", END) 136 | if not message or message.replace(" ", "").\ 137 | replace("\r", "").replace("\n", "") == '': 138 | return 139 | for k1 in memory.friend_list: 140 | if k1 == (1, self.username): 141 | self.append_to_chat_box(time_info, "我", message, 'me') 142 | self.input_textbox.delete("1.0", END) 143 | 144 | # format datetime 145 | send_message_handler(time_info, message, self.username) 146 | return 'break' 147 | 148 | def choose_color(self): 149 | _, self.font_color = colorchooser.askcolor( 150 | initialcolor=self.font_color) 151 | self.apply_font_change(None) 152 | 153 | def choose_font_size(self): 154 | result = simpledialog.askinteger("设置", "请输入字体大小", 155 | initialvalue=self.font_size) 156 | if result is None: 157 | return 158 | self.font_size = result 159 | self.apply_font_change(None) 160 | 161 | def apply_font_change(self, _): 162 | try: 163 | self.input_textbox.tag_config('new', foreground=self.font_color, 164 | font=(None, self.font_size)) 165 | self.input_textbox.tag_add('new', '1.0', END) 166 | except Exception: 167 | pass 168 | 169 | def close_window(self): 170 | del memory.Chat_window[self.username] 171 | self.master.destroy() 172 | 173 | def update_chatroom_user_list(self, chatroom_name): 174 | cn = chatroom_name.encode() 175 | serializeMessage = common_handler.pack_message(common_handler.MessageType.query_room_users, cn) 176 | client_socket.send_msg(serializeMessage) 177 | 178 | 179 | def chatroom_user_update(msg): 180 | chatroom_name = msg[1].decode() 181 | user_list = msg[2].decode() 182 | if user_list == "no more user": 183 | memory.chatroom_user_list = {} 184 | return 185 | else: 186 | _friend_info_lst = user_list.split(" + ") 187 | tmp = [] 188 | for _i in _friend_info_lst: 189 | _m = _i.split(":") 190 | tmp.append((_m[0], _m[1])) 191 | memory.chatroom_user_list[chatroom_name] = (tmp) 192 | print(memory.chatroom_user_list) 193 | for cuser in memory.chatroom_user_list[chatroom_name]: 194 | memory.Chat_window[chatroom_name].chatroom_user_list.\ 195 | insert(END, cuser[1]) 196 | 197 | 198 | def run(name): 199 | _tmp = name.split() 200 | if _tmp[0] == "群": 201 | username = _tmp[2] 202 | username = username[1:] 203 | username = username[:-1] 204 | nickname = _tmp[1] 205 | else: 206 | username = _tmp[1] 207 | username = username[1:] 208 | username = username[:-1] 209 | nickname = _tmp[0] 210 | 211 | try: 212 | if memory.Chat_window[username]: 213 | return 214 | except Exception: 215 | pass 216 | root = tk.Toplevel() 217 | ChatForm(root, username=username, nickname=nickname) 218 | for i in memory.recv_message: 219 | if i == username: 220 | memory.tk_root.update_friend_list(unflag_name=username) 221 | for _time, _user, _msg, _flag in memory.recv_message[username]: 222 | memory.Chat_window[username].append_to_chat_box( 223 | _time, _user, _msg, _flag) 224 | root.protocol( 225 | "WM_DELETE_WINDOW", memory.Chat_window[username].close_window) 226 | 227 | 228 | def send_message_handler(send_time, msg, username): 229 | msg = msg.encode() 230 | send_time = send_time.encode() 231 | username = username.encode() 232 | from_user = memory.username.encode() 233 | 234 | for v in memory.friend_list: 235 | if v[1] == username.decode(): 236 | if v[0] == 2: 237 | serializeMessage = common_handler.pack_message( 238 | common_handler.MessageType.chatroom_message, send_time, username, from_user, msg) 239 | client_socket.send_msg(serializeMessage) 240 | return 241 | serializeMessage = common_handler.pack_message( 242 | common_handler.MessageType.send_message, send_time, username, from_user, msg) 243 | client_socket.send_msg(serializeMessage) 244 | 245 | 246 | def chatmsg_handler(msg): 247 | from_user = msg[1].decode() 248 | send_time = msg[2].decode() 249 | message = msg[3].decode() 250 | _flag = "them" 251 | if msg[0] == common_handler.MessageType.broadcast: 252 | _flag = "system" 253 | 254 | for i in memory.Chat_window: 255 | # chat window exist 256 | if i == from_user: 257 | memory.Chat_window[i].append_to_chat_box( 258 | send_time, from_user, message, _flag) 259 | return 260 | # If not chat with target who msg from someone, save msg to buffer. 261 | for iii in memory.recv_message: 262 | if iii == from_user: 263 | memory.recv_message[from_user].append((send_time, from_user, message, _flag)) 264 | print(memory.recv_message) 265 | break 266 | memory.recv_message[from_user] = [(send_time, from_user, message, _flag)] 267 | memory.tk_root.update_friend_list(flag_name=from_user) 268 | 269 | 270 | def chatroom_msg_handler(msg): 271 | send_time = msg[1].decode() 272 | chatroom_name = msg[2].decode() 273 | from_user = msg[3].decode() 274 | message = msg[4].decode() 275 | _flag = "them" 276 | for i in memory.Chat_window: 277 | # chat window exist 278 | if i == chatroom_name: 279 | memory.Chat_window[i].append_to_chat_box( 280 | send_time, from_user, message, _flag) 281 | return 282 | # If not chat with target who msg from someone, save msg to buffer. 283 | for iii in memory.recv_message: 284 | if iii == chatroom_name: 285 | memory.recv_message[chatroom_name].append((send_time, from_user, message, _flag)) 286 | print(memory.recv_message) 287 | break 288 | memory.recv_message[from_user] = [(send_time, from_user, message, _flag)] 289 | memory.tk_root.update_friend_list(flag_name=from_user) 290 | 291 | 292 | 293 | if __name__ == "__main__": 294 | run("jz") 295 | -------------------------------------------------------------------------------- /server/DB_Handler.py: -------------------------------------------------------------------------------- 1 | ''' 2 | author:jeremyjone 3 | date:2018-5-4 4 | 5 | This is mysql-DB handler module for chatroom, Receive all 6 | kinds of database operation requests received by the server, 7 | process them and return the results. 8 | ''' 9 | import pymysql 10 | import re 11 | 12 | 13 | class DB_Handler(object): 14 | 15 | def __init__(self): 16 | self.local = 'localhost' 17 | self.db_login_name = 'root' 18 | self.db_login_pswd = '123456' 19 | self.db = 'chatroom' 20 | self.userinfo = "userinfo" 21 | self.chatmsg = "chatmsg" 22 | self.userfriend = "userfriend" 23 | self.chatroom = "chatroom" 24 | self.chatroomuser = "chatroom_user" 25 | self.charset = "utf8" 26 | 27 | def connect_to_DB(self, sql_statment): 28 | ''' 29 | Connect to database by base infomation and create database 30 | handler module, it can receive one SQL and execute. 31 | 32 | If operate successfully return OK, conversely return NG. 33 | ''' 34 | _ = None 35 | sql = pymysql.connect(self.local, 36 | self.db_login_name, 37 | self.db_login_pswd, 38 | self.db, 39 | charset=self.charset) 40 | # Create cursor 41 | cursor = sql.cursor() 42 | 43 | try: 44 | flag = re.search(r'^(select)\s', sql_statment).group(1) 45 | except Exception: 46 | flag = "" 47 | 48 | if flag == "select": 49 | cursor.execute(sql_statment) 50 | data = cursor.fetchall() 51 | _ = data 52 | else: 53 | # If not query 54 | try: 55 | cursor.execute(sql_statment) 56 | sql.commit() 57 | _ = 'OK' 58 | except Exception as e: 59 | sql.rollback() 60 | print(e) 61 | _ = "NG" 62 | # close cursor 63 | cursor.close() 64 | # close database 65 | sql.close() 66 | return _ 67 | 68 | def user_exist(self, name): 69 | ''' 70 | Judge whether the user exists or not. 71 | ''' 72 | if re.findall(r'^\d+$', name): 73 | statment = 'select username\ 74 | from %s where id=%s;' % (self.userinfo, name) 75 | else: 76 | statment = 'select username\ 77 | from %s where username="%s";' %\ 78 | (self.userinfo, name) 79 | res = self.connect_to_DB(statment) 80 | if res: 81 | return res[0] 82 | 83 | def register(self, name, pswd, nick): 84 | ''' 85 | User registration, receiving registration information, first 86 | check whether the username exists, and then fill in the 87 | registration information into the database. 88 | ''' 89 | # nick = nick.encode() 90 | res = self.user_exist(name) 91 | if res == 'EXIST': 92 | return 'NAMEEXIST' 93 | else: 94 | statment = 'insert into %s (username,\ 95 | password, nickname, isActive) values ("%s", "%s", "%s", 1);'\ 96 | % (self.userinfo, name, pswd, nick) 97 | res2 = self.connect_to_DB(statment) 98 | if res2 == 'OK': 99 | return 'OK' 100 | else: 101 | return 'NG' 102 | 103 | def login_check(self, name, pswd): 104 | ''' 105 | Check the user's login information, when received data, return OK 106 | ''' 107 | statment = 'select username, password from %s\ 108 | where username="%s" and isActive=1;' % (self.userinfo, name) 109 | res = self.connect_to_DB(statment) 110 | if res: 111 | # 判断返回值的密码 112 | if pswd == res[0][1]: 113 | return 'OK' 114 | else: 115 | return 'NG' 116 | 117 | def change_password(self, name, old_pswd, new_pswd): 118 | ''' 119 | Change the password, First query the username and password. 120 | If matching successful, try to change the password. As long 121 | as modify failed, the return value is NG. 122 | ''' 123 | statment1 = 'select username, password from %s\ 124 | where username="%s" and isActive=1;' % (self.userinfo, name) 125 | res = self.connect_to_DB(statment1) 126 | if res: 127 | # Judge the password 128 | if old_pswd == res[0][1]: 129 | # Modify the password with the current one 130 | statment2 = 'update %s set password="%s"\ 131 | where username="%s";' % (self.userinfo, new_pswd, name) 132 | res2 = self.connect_to_DB(statment2) 133 | if res2 == 'OK': 134 | return 'OK' 135 | # Return NG as long as no changes have been made 136 | return 'NG' 137 | 138 | def user_friend(self, name): 139 | ''' 140 | View friends and return to the list of friends. 141 | ''' 142 | statment = 'select %s.username, %s.nickname from %s inner join %s\ 143 | on %s.friend_id=%s.id where (%s.user_id=(select id from userinfo\ 144 | where username="%s") and %s.isActive=1);'\ 145 | % (self.userinfo, self.userinfo, self.userfriend, self.userinfo, 146 | self.userfriend, self.userinfo, self.userfriend, name, self.userfriend) 147 | res = self.connect_to_DB(statment) 148 | if res: 149 | friend_list = [] 150 | for i in res: 151 | friend_list.append("%s:%s" % (i[0], i[1])) 152 | return friend_list 153 | else: 154 | return "NF" 155 | 156 | def user_add_friend(self, name, friend_name): 157 | ''' 158 | Add friends to the database. 159 | ''' 160 | statment1 = 'insert into %s (user_id, friend_id, isActive)\ 161 | values ((select id from %s where username="%s"),\ 162 | (select id from %s where username="%s"), 1);'\ 163 | % (self.userfriend, self.userinfo, name, self.userinfo, friend_name) 164 | res1 = self.connect_to_DB(statment1) 165 | 166 | statment2 = 'insert into %s (user_id, friend_id, isActive)\ 167 | values ((select id from %s where username="%s"),\ 168 | (select id from %s where username="%s"), 1);'\ 169 | % (self.userfriend, self.userinfo, friend_name, self.userinfo, name) 170 | res2 = self.connect_to_DB(statment2) 171 | 172 | if res1 == res2 == 'OK': 173 | return 'OK' 174 | else: 175 | return 'NG' 176 | 177 | def user_del_friend(self, name, friend_name): 178 | ''' 179 | Delete one friend in database. 180 | In table userfriend, the value of isActive is changed to 0 181 | ''' 182 | statment1 = 'update %s set isActive=0\ 183 | where user_id=(select id from %s where username="%s") and\ 184 | friend_id=(select id from %s where username="%s");' %\ 185 | (self.userfriend, self.userinfo, name, self.userinfo, friend_name) 186 | statment2 = 'update %s set isActive=0\ 187 | where user_id=(select id from %s where username="%s") and\ 188 | friend_id=(select id from %s where username="%s");' %\ 189 | (self.userfriend, self.userinfo, friend_name, self.userinfo, name) 190 | res1 = self.connect_to_DB(statment1) 191 | res2 = self.connect_to_DB(statment2) 192 | if res1 == res2 == 'NG': 193 | return 'NG' 194 | else: 195 | return 'OK' 196 | 197 | def get_user_nickname(self, name): 198 | ''' 199 | Get user's nickname. 200 | ''' 201 | statment = 'select nickname from %s where username="%s";'\ 202 | % (self.userinfo, name) 203 | try: 204 | return self.connect_to_DB(statment)[0] 205 | except Exception: 206 | return 'Unknown user' 207 | 208 | def save_msg(self, name, target_user, isRead, msg_type, msg): 209 | ''' 210 | Save normal chat message, broadcast, chatroom message in DB. 211 | ''' 212 | statment = 'insert into %s (user_id, target_id,\ 213 | isRead, msg_type, msg, isActive) values (\ 214 | (select id from %s where username="%s"),\ 215 | (select id from %s where username="%s"), %d, %d, "%s", %d);'\ 216 | % (self.chatmsg, self.userinfo, name, self.userinfo, 217 | target_user, isRead, msg_type, msg, 1) 218 | res = self.connect_to_DB(statment) 219 | if res == 'OK': 220 | return 'OK' 221 | else: 222 | return 'NG' 223 | 224 | def get_unread_msg(self, name): 225 | ''' 226 | Get user's chat message, return msg and change isRead to 0. 227 | ''' 228 | statment = 'select user_id, send_time, msg_type, msg from %s\ 229 | where target_id=(select id from %s where username="%s" and\ 230 | isRead=1);' % (self.chatmsg, self.userinfo, name) 231 | res = self.connect_to_DB(statment) 232 | if res: 233 | _statment = 'update %s set isRead=0 where target_id=(select id from\ 234 | %s where username="%s" and isRead=1);' % (self.chatmsg, 235 | self.userinfo, name) 236 | self.connect_to_DB(_statment) 237 | return res 238 | 239 | def create_chatroom(self, chatroom_name): 240 | ''' 241 | Create a new chatroom, first check whether the chatroom exists, 242 | and then fill in the create information into the database. 243 | ''' 244 | # chatroom_name = chatroom_name.encode() 245 | statment1 = 'select chatroom_name\ 246 | from %s where chatroom_name="%s";' % (self.chatroom, chatroom_name) 247 | res = self.connect_to_DB(statment1) 248 | if res: 249 | return 'EXIST' 250 | else: 251 | statment2 = 'insert into %s (chatroom_name, isActive) values\ 252 | ("%s", %d);' % (self.chatroom, chatroom_name, 1) 253 | res1 = self.connect_to_DB(statment2) 254 | if res1 == "OK": 255 | return "OK" 256 | return "NG" 257 | 258 | def chatroom_user(self, chatroom_name, name, user_handler): 259 | ''' 260 | Manage user in chatroom, when user join a chatroom, insert one record, 261 | and when user leave a chatroom, its isActive value is changed to 0. 262 | ''' 263 | # Join operation 264 | # chatroom_name = chatroom_name.encode() 265 | if user_handler == 'join': 266 | statment = 'insert into %s (chatroom_id, user_id, isActive) values\ 267 | ((select id from %s where chatroom_name="%s"),\ 268 | (select id from %s where username="%s"),1);' %\ 269 | (self.chatroomuser, self.chatroom, chatroom_name, 270 | self.userinfo, name) 271 | # Out operation 272 | elif user_handler == "leave": 273 | statment = 'update %s set isActive=0 where\ 274 | chatroom_id=(select id from %s where chatroom_name="%s") and\ 275 | user_id=(select id from %s where username="%s");' %\ 276 | (self.chatroomuser, self.chatroom, chatroom_name, 277 | self.userinfo, name) 278 | res = self.connect_to_DB(statment) 279 | if res == "OK": 280 | return "OK" 281 | return "NG" 282 | 283 | def get_username_by_id(self, uid): 284 | ''' 285 | Get username. 286 | ''' 287 | statment = 'select username from %s where id="%d" and isActive=1;'\ 288 | % (self.userinfo, uid) 289 | try: 290 | return self.connect_to_DB(statment)[0] 291 | except Exception: 292 | return 'qurey NG' 293 | 294 | def get_chatroom_user(self, chatroom_name): 295 | ''' 296 | Get all of users in this chatroom. 297 | ''' 298 | # chatroom_name = chatroom_name.encode() 299 | statment = 'select userinfo.username, userinfo.nickname from\ 300 | userinfo where userinfo.id=any(select chatroom_user.user_id from\ 301 | chatroom_user where chatroom_user.chatroom_id=(select chatroom.id\ 302 | from chatroom where chatroom.chatroom_name="%s"));' % chatroom_name 303 | 304 | res = self.connect_to_DB(statment) 305 | if res: 306 | friend_list = [] 307 | for i in res: 308 | friend_list.append("%s:%s" % (i[0], i[1])) 309 | return friend_list 310 | else: 311 | return "NF" 312 | 313 | def query_chatroom(self, name): 314 | ''' 315 | View room and return to the list of room. 316 | ''' 317 | # statment_get_id = 'select id from %s where username="%s"' %\ 318 | # (self.userinfo, name) 319 | # cid = self.connect_to_DB(statment_get_id)[0] 320 | # statment_isexist = 'select id from %s where user_id=\ 321 | # (select id from %s where usernam="%s")' % ( 322 | # self.chatroom, self.userinfo, name) 323 | # res = self.connect_to_DB(statment_isexist) 324 | 325 | statment = 'select %s.chatroom_name from %s where %s.id=any(\ 326 | select %s.chatroom_id from %s where %s.user_id=(select id\ 327 | from %s where %s.username="%s"));' % ( 328 | self.chatroom, self.chatroom, self.chatroom, 329 | self.chatroomuser, self.chatroomuser, self.chatroomuser, 330 | self.userinfo, self.userinfo, name) 331 | res = self.connect_to_DB(statment) 332 | 333 | if res: 334 | chatroom_list = [] 335 | for i in res: 336 | print(i) 337 | chatroom_list.append("%s:%s" % ("群", i[0])) 338 | return chatroom_list 339 | else: 340 | return "NF" 341 | --------------------------------------------------------------------------------