python编写的全双工聊天室

python编写的全双工聊天室

支持的功能为,用户能够进行注册与登录,用户的数据通过mysql数据库进行存取。程序通过模块json来进行客户端与服务器端的数据的封装,tcp协议传输。
成功登录后的用户,将会进入 “在线用户列表”界面,会显示对应聊天对像未读的信息数量,这里有一个缺陷便是如果有新的用户上线,则必须通过输入"update"进行更新界面,才能看到新的上线用户,以及如果用户下线后,也未作处理,即“在线用户列表”依然会显示该用户。
(补充,对密码的md5加密应该放在客户端上面,我这里是直接放在数据库上面)

服务端流程

在这里插入图片描述

客户端流程

在这里插入图片描述

服务器端 server.py

import socket
import threading
import queue
import json
import mysql_sqlalchemy
class TcpServer():
    def __init__(self):
        self.host=''
        self.port=8002
        self.sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.sock.bind((self.host,self.port))
        self.maxNumber=10 #最大连接人数
        self.buffsize=1024

        self.login_verification_code=205#登录验证码
        self.registered_code=206 #注册码
        self.registered_ok_code = 207 #注册结果码

        self.user_messages_code = 301  #表示 消息数据格式码
        #self.user_status_code = 302 #表示 在线成员格式码
        self.user_update_status_code = 303 #表示 更新在线成员格式码
        self.loginOk_client_code = 351  #登录成功返回给客户端的码
        self.login_verification_fails_code = 400 #验证失败返回给客户端的码

        #self.users_in_online=[]  #保存每个用户的 (username,s_client)
        self.users_in_online_dic={} #保存在线用户{username,(s_client,name)} 便于取
        self.q_messages=queue.Queue() #存放服务器接收的消息,[d_username,content]
        self.usesql=mysql_sqlalchemy.Main() #返回一个可操作sql的对象
        self.test_id=10

        listen_connect_thread=threading.Thread(target=self.listen_user) #创建一个监听连接的线程
        listen_connect_thread.start()
        #listen_connect_thread.join() #设置为守护线程

        listen_send_thread=threading.Thread(target=self.send_data) #创建一个随时监听数据,一有数据就发送的线程
        listen_send_thread.start()
        #listen_send_thread.join() #设置为守护线程

    "监听函数"
    def listen_user(self):
        self.sock.listen(self.maxNumber)
        while True:
            print("wating to be connected...")

            s_client,addr=self.sock.accept()
            thr=threading.Thread(target=self.listen_login_or_registered,args=(s_client,))
            thr.start()
            #thr.join()
    def listen_login_or_registered(self,s_client):
        while True:
            print("等待命令...")
            messages = s_client.recv(self.buffsize).decode("utf8")
            dic_messages = json.loads(messages)
            commande_code=dic_messages["command_code"]
            content = dic_messages["content"]
            if commande_code == self.login_verification_code:  #登录验证函数码
                checkbool,name=self.login_aut(s_client,content)
                if checkbool: #如果验证成功
                    break
            elif commande_code==self.registered_code:  #注册码
                self.registered_user(s_client,content)

        self.user_main(s_client, content["username"], name)  #如果验证成功,则break跳出while循环,执行该函数

    def registered_user(self,s_client,content):
        username=content["username"]
        password=content["password"]
        name=content["name"]
        add_bool=self.usesql.add_user(username,password,name)  #True 成功,False失败
        data={
            "command_code":self.registered_ok_code,  #注册结果反馈码
            "content":add_bool
        }
        s_client.send(json.dumps(data).encode("utf8"))


    "登录验证函数"
    def login_aut(self,s_client,dic_content):
        print("登录验证")

        username = dic_content["username"]
        password = dic_content["password"]
        print(username,password)
        check_bool, name = self.usesql.check_user(username=username, password=password)  # 验证成功则返回TRUE

        if check_bool:  # 登录成功则返回用户信息
            print("test_id:",self.test_id)
            self.test_id+=1
            # self.users_in_online.append((username, s_client))  # 将自己加入在线列表中
            self.users_in_online_dic[username] = (s_client, name)  # 将自己加入在线字典中
            my_name=self.users_in_online_dic[username][1]
            print("out my :"+my_name)
            for i,j in self.users_in_online_dic.items():
                print(i,j)
            return (True,name)
        else:  # 验证失败,则返回400 命令码
            data = {
                "command_code": self.login_verification_fails_code,
            # (表示登录验证)self.login_verification_fails_code=400
                "content": ''
            }
            s_client.send(json.dumps(data).encode("utf8"))
            return (False,"")


    "进入用户界面响应"
    def user_main(self,s_client,username,name):
        users_in_online=[]  #为每个用户新生成一个在线列表,列表中的成员除了自己,这样比较低效率
        for the_username,tup_next in self.users_in_online_dic.items():
            if the_username==username:
                continue
            the_name=tup_next[1] #(s_client,name) 获得name
            users_in_online.append((the_username,the_name))
            print("users_in_online :")
            print(users_in_online)

        data={
            "command_code": self.loginOk_client_code,  #(表示登录验证)self.loginOk_client_code=351
            "content":{
                "user_information":(username,name),
                "users":users_in_online
            }
        }
        s_client.send(json.dumps(data).encode("utf8"))  #将初始信息发送给客户端
        self.update_users(s_client,username,name)  #一有新的用户连接入,就更新其他在线用户的在线列表
        self.recv_data(s_client,username,name) #进入数据接收


        #self.users_in_online.append((username,s_client))  #将自己加入在线列表中



    def recv_data(self,s_client,username,name):
        print("begin recv the messages from username:",username )

        while True:
            data=s_client.recv(self.buffsize).decode("utf8")
            data=json.loads(data)
            command_code=data["command_code"]
            if command_code==301:
                content=data["content"]
                d_username = content["d_username"]
                dic_content=[d_username,content]
                self.q_messages.put(dic_content)
            else:
                continue
    def update_users(self,my_client,username,name):
        print("update_users")
        data={
            "command_code":self.user_update_status_code,
              "content":(username,name)
            }
        data=json.dumps(data).encode("utf8")
        for i,j in self.users_in_online_dic.items():
            print(i,j)
        #users_in_online_dic=self.users_in_online_dic
        #users_in_online_dic.pop(username)  #傻逼啊,这是浅拷贝,这TM对象是同一个啊,会删了自身的啊。。。

        for tup_next in self.users_in_online_dic.values():   #给每个在线用户发送更新列表

            s_client=tup_next[0]
            if s_client==my_client:
                continue
            s_client.send(data)

    def send_data(self):

        print("send messages...")
        while True:
            if self.q_messages.qsize() > 0:  # 有数据时
                d_username, content = self.q_messages.get()
                data={"command_code":self.user_messages_code,
                      "content":content
                      }
                s_client=self.users_in_online_dic[d_username][0]
                s_client.send(json.dumps(data).encode("utf8"))
                print("send messages to username:" ,d_username)


if __name__=="__main__":
    tcpserver=TcpServer()

数据库 mysql_sqlalchemy.py

from sqlalchemy import Column,Integer,String,create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from hashlib import md5
Base=declarative_base()

class User(Base):
    __tablename__="users"
    id=Column(Integer,primary_key=True,autoincrement=True)
    username=Column(String(20),unique=True)
    password=Column(String(64))
    name=Column(String(64))

class Usesql():
    def __init__(self):
        engine = create_engine("mysql+pymysql://root:123456@localhost/chat_sql?charset=utf8", \
                               encoding='utf8')
        Base.metadata.create_all(engine)
        Session_class=sessionmaker(engine)
        self.session=Session_class()

    def add_user(self,username,password,name):
        m=md5()
        m.update(password.encode("utf8"))

        user=self.session.query(User).filter(User.username==username).first()

        if not user:   #如果用户不存在
            user = User(username=username, password=m.hexdigest(), name=name)
            self.session.add(user)
            self.session.commit()
            print("提交成功")
            return True
        else:
            print("用户已存在")
            return False

    def delete_user(self,username):
        user=self.session.query().filter(User.username==username).first()
        self.session.delete(user)
        print("删除成功")
    def check_user(self,username,password):
        m = md5()
        m.update(password.encode("utf8"))
        user = self.session.query(User).filter(User.username == username).first()
        if not user:  #如果用户不存在,则返回失败
            return (False,'')
        if user.password==m.hexdigest():  #如果密码相同,则验证成功
            return (True,user.name)
        else:
            return (False,'')

#导入文件时,调用Main开始使用,并返回管理对象
def Main():
    usesql=Usesql()
    return usesql

if __name__=="__main__":
    usesql=Main()

客户端 client.py

import socket
import threading
import json
import queue

class TcpClient():

    def __init__(self):
        self.config={
            "host":"127.0.0.1",
            "port":8002
        }
        self.buffsize=1024
        self.client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

        self.login_verification_code = 205 #登录验证数据格式
        self.registered_code = 206 #注册码
        self.registered_ok_code = 207  # 注册结果状态码
        self.user_messages_code = 301 #发送聊天消息码
        self.user_update_status_code = 303 #更新 增加在线成员列表码
        self.loginOk_client_code = 351  # 登录成功,返回的初始消息码
        self.login_verification_fails_code=400  #验证失败,则接收400数据包

        self.username=""
        self.name=""
        self.send_queue=queue.Queue()  #将要发送的消息放入发送消息队列中
        # content={
        #     "s_username": self.username,
        #     "d_username": d_username,
        #     "messages": messages
        # }
        self.dic_chat_queue={} #username:queue.Queue() 存放各聊天对象的未读消息
        self.dic_chat_users={} #username:name
        self.new_user=queue.Queue()  #存放新上线的用户 (username,name)
    def start_connect(self):
        self.client.connect((self.config["host"],self.config["port"]))
        # print(self.client)
        #self.login(username,password) # 从GUI中获取登录数据
    def registered_user(self,username,password,name):
        data={
            "command_code":self.registered_code,  #注册码206
            "content":{
                "username":username,
                "password":password,
                "name":name
            }
        }
        self.client.send(json.dumps(data).encode("utf8"))
        data=self.client.recv(self.buffsize).decode("utf-8")
        data=json.loads(data)

        if data["command_code"]==self.registered_ok_code:
            registered_bool=data["content"]
            return registered_bool
        else:
            print("registered_user code error...")
            return False

    #验证登录函数
    def login(self,username,password):
        data={
            "command_code":self.login_verification_code,  #(表示登录验证)self.login_verification_code=205
            "content":{
                "username":username,
                "password":password
            }
        }
        self.client.send(json.dumps(data).encode("utf8"))
        data=self.client.recv(self.buffsize).decode("utf8")
        data=json.loads(data)
        command_code=data["command_code"]
        if command_code==self.loginOk_client_code:
            self.user_main(data["content"])
            return True
        elif command_code==self.login_verification_fails_code:
            string_prompt="帐号或密码错误"
            print(string_prompt,username,password)
            return False
        else:
            print("login error...")
            return False
    "用户主界面,登录成功后进入,并初始化个人信息与在线用户列表"
    def user_main(self,content):
        self.username,self.name=content["user_information"] #"user_information":(username,name)
        users_in_online_list=content["users"] #users_in_online[(username,name),(username,name)]
        # print(content["user_information"])
        # print(users_in_online_list)
        for username,name in users_in_online_list:
            self.dic_chat_users[username]=name
            self.dic_chat_queue[username]=queue.Queue()

        send_thread=threading.Thread(target=self.send_data) #发送消息线程
        send_thread.start()

        recv_thread=threading.Thread(target=self.recv_data) #接收消息线程
        recv_thread.start()

    def recv_data(self):
        while True:
            #print("recv messages...")
            data = self.client.recv(self.buffsize).decode("utf-8")
            data = json.loads(data)
            command_code = data["command_code"]
            content = data["content"]
            if command_code == self.user_messages_code:  # 如果是聊天消息
                s_username = content["s_username"]  # 消息来源,即是哪个用户发来的消息
                messages = content["messages"]
                q = self.dic_chat_queue[s_username]
                q.put(messages)  # 将消息放入对应用户的消息队列中
            elif command_code == self.user_update_status_code:  # 如果是 增加在线成员列表

                username = content[0]  # content=(username,name)
                name = content[1]
                # print("hava new user :", username, name)
                self.dic_chat_queue[username] = queue.Queue()
                self.dic_chat_users[username] = name
                self.new_user.put((username, name))
            else:
                print("recv commande_code error...")
    #将待发数据加入 发送数据队列中
    def add_data(self,d_username,messages):
        content={
            "s_username": self.username,
            "d_username": d_username,
            "messages": messages
        }

        self.send_queue.put(content)


    def send_data(self):
        #print("send messages...")
        while True:
            if self.send_queue.qsize()>0:
                content=self.send_queue.get()
                data={
                    "command_code":self.user_messages_code,
                    "content":content
                }
                self.client.send(json.dumps(data).encode("utf8"))

    def show_data(self):
        pass
def Main():
    tcpclient=TcpClient()
    tcpclient.start_connect()
    return tcpclient

if __name__ == '__main__':
    Main()

用户的菜单界面 login_cmd.py 客户端以此为主运行文件

import client
import threading
import queue
class Login():
    def __init__(self,tcpclient):
        self.username=""
        self.name=""
        self.tcpclient=tcpclient
        self.in_users_dic={}  # id:(username,name)
        self.in_users_list=[] #  (1,(username,name))
        self.message_number={}  #(username,number)
    def start_login(self):
        username=input("帐号:")
        password=input("密码:")
        login_bool=self.tcpclient.login(username,password)

        if login_bool:  #如果登录成功,进入主界面
            self.in_users_dic_init()
            self.main_in_user()
    #初始化在线人员
    def in_users_dic_init(self):
        self.name=self.tcpclient.name
        self.username=self.tcpclient.username
        self.id=1
        for username,name in self.tcpclient.dic_chat_users.items():
            id=str(self.id)
            self.in_users_dic[id]=(username,name)
            self.in_users_list.append((id,(username,name)))
            self.id+=1
    "检查各对象未读消息数"
    def check_message_number(self):

        for username,q_message in self.tcpclient.dic_chat_queue.items():
            self.message_number[username]=q_message.qsize()
    "监听是否有新用户上线"
    def read_new_user(self):
        while self.tcpclient.new_user.qsize()>0:
            username,name=self.tcpclient.new_user.get()
            #self.in_users_dic[username]=name
            self.in_users_dic[str(self.id)]=(username,name)
            self.in_users_list.append((str(self.id),(username,name)))
            self.id+=1

    def main_in_user(self): #进入在线列表
        while True:
            print("序号      帐号     名称    未读消息数")
            self.check_message_number()
            for id, information in self.in_users_list:
                username=information[0]
                name=information[1]
                number=str(self.message_number[username])
                print(str(id) + "   " + username + "   " + name+"   "+number)
            print("输入 exit 则退出")
            print("输入 update 刷新界面")
            string_cmd = input("输入序号:")
            if string_cmd == "exit":
                break
            elif string_cmd=="update":
                self.read_new_user()
            else:
                information=self.in_users_dic[string_cmd]

                if information:  #如果存在
                    self.chat_to_user(information[0],information[1])
                else:  #不然为None
                    print("并没有这个。。。")
    def chat_to_user(self,d_username,d_name):
        d_queue=self.tcpclient.dic_chat_queue[d_username]
        self.read_thread=threading.Thread(target=self.chat_read_messages,args=(d_queue,d_name))
        self.read_thread.start()
        self.chat_send_messages(d_username)



    def chat_read_messages(self,d_queue,d_name):  #读消息
        self.stop_thread=False
        while not self.stop_thread:
            if d_queue.qsize()>0:
                message=d_queue.get()
                print(d_name+":"+message)


    def chat_send_messages(self,d_username):  #发消息
        print("没有消息直接回国键,退出聊天")
        while True:
            message=input("")
            if not message:   #如果消息为空,则退出
                break
            self.tcpclient.add_data(d_username,message)
        self.stop_thread=True #退出读消息线程

def check_login(self,username,password):
        check_bool = self.tcpclient.login(username, password)

        if check_bool:  # True则登录验证成功,进行登录
            print("登录成功")
            return True

        else:
            print("帐号或密码错误")
            return False


class SignIn():
    def __init__(self,tcpclient):
        self.tcpclient=tcpclient

    def siginin(self):
        print("SignIn")
        name=input("名称:")
        username=input("帐号:")
        password=input("密码:")
        password2=input("密码:")
        self.check_signin_func(name,username,password,password2)

    def check_signin_func(self,name,username,password,password2):
        if password != password2:
            print("你可能不信,两次密码不一样")

        else:

            registered_bool = self.tcpclient.registered_user(username, password, name)
            if registered_bool:
                print("baby 注册成功")

            else:
                print("帐号已存在")





class Menu():
    def __init__(self):
        self.tcpclient=client.Main() # return tcpclient
        self.login = Login(self.tcpclient)
        self.signin = SignIn(self.tcpclient)

    def main_menu(self):
        while True:
            print("login")
            print("signin")
            print("exit")
            str_choice = input("input:")
            if str_choice == "login":
                self.login.start_login()
            elif str_choice=="signin":
                self.signin.siginin()
            elif str_choice=="exit":
                exit(0)
            else:
                print("输错了啊!!!")


if __name__ == '__main__':
    menu=Menu()
    menu.main_menu()

下面附运行效果截图

注册界面:

注册界面效果

登录界面:

登录界面效果

消息发送:

消息发送效果

在线列表更新:

在线列表更新效果

进入聊天:

在这里插入图片描述

在这里插入图片描述

第一次写博客,也是第一个写这样的程序,有很多不足的地方,也没有再去思考解决,还是人比较懒。。。

请大佬们多多指教。

附文件下载链接

python全双工聊天源码下载

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值