多人在线聊天室,可私聊可发表情包哟~

这学期我们网络编程这门课主要是Window Socket,并且还是用我们没学过C++实现的,不过值得庆幸的是老师上课讲的很仔细,并且他讲课主要是讲思想,实现思路,最后老师没有限制我们课程设计使用的语言。权衡了一下,我使用Python实现了多人在线聊天的聊天室。

这个聊天室的主要功能是:

1、多人聊天——群聊

2、私聊

3、发表情包

有种仿QQ和微信聊天的感觉,但是界面不太美观,功能没那么强大,但是用作网络编程的课程设计大作业还是OK的......

这个小项目首先是在b站看的教学视频,这个教程就只实现多人聊天的功能后面我要在这个基础上实现了私聊发表情的功能,源码我会放到文章的最后,有需要的可以下载......

这篇文章主要是讲和教程不一样的地方,详细可以看一下的教学视频,或是自己下载源码学习.....

教学视频连接:

【python项目实战】创作自己的多人聊天室_哔哩哔哩_bilibili

一、服务端

服务端主要是在处理客户端请求的死循环中,添加了一个判断,判断是否要进入私聊的聊天模式

    def request_handle(self, client_sock):
        """处理客户端请求"""
        while True:
            """1、接收客户端请求"""
            recv_data = client_sock.recv_data()
            if not recv_data:  # 如果收到空字符串 就表示结束聊天
                self.remove_offline_user(client_sock)
                client_sock.close()
                break
            print("客户端发送的请求信息:%s" % recv_data)
            """2、解析客户端请求"""
            parser_data = self.parser_request_text(recv_data)
            print("解析过的数据为:%s" % parser_data)

            """3、判断请求类型,调用请求对应的处理函数"""
            handle_function = self.request_handle_function.get(parser_data["request_id"])
            if handle_function:
                handle_function(client_sock, parser_data)

    #处理聊天请求
    def request_chat_handle(self, client_sock, request_data):
        print("正在处理聊天请求~")
        username = request_data['username']
        messages = request_data['messages']
        chat = request_data['chat']
        nickname = self.clients[username]['nickname']

        msg = ResponseProtocol.response_chat(nickname, messages, str(self.users), chat)

        if chat == '------------------群聊------------------':
            for u_name, info in self.clients.items():
                if username == u_name:
                    continue
                info['sock'].send_data(msg)
        else:
            for u_name, info in self.clients.items():
                if username == u_name:
                    continue
                elif chat == info['nickname']:
                    info['sock'].send_data(msg)

二、客户端 

客户端的代码有较大的变化

首先是图形界面:

聊天界面的主要变化

    def append_message(self, sender, messages, chat):
        """追加聊天内容到chat_text_area"""
        send_time = strftime("%Y-%m-%d %H:%M:%S", localtime(time()))
        send_info = "%s: %s\n" % (sender, send_time)
        # 判断是不是表情  如果字典里有则贴图
        if messages in self.dic:
            if chat == '------------------群聊------------------':
                self.children['chat_text_area'].insert(END, send_info, 'green')  # END将信息加在最后一行
                self.children['chat_text_area'].image_create(END, image=self.dic[messages])
                self.children['chat_text_area'].insert(END, "\n")
                self.children['chat_text_area'].see(END)
            else:
                self.children['chat_text_area'].insert(END, send_info, 'blue')  # END将信息加在最后一行
                self.children['chat_text_area'].image_create(END, image=self.dic[messages])
                self.children['chat_text_area'].insert(END, "\n")
                self.children['chat_text_area'].see(END)
        else:
            if chat == '------------------群聊------------------':
                self.children['chat_text_area'].insert(END, send_info, 'green')  # END将信息加在最后一行
                self.children['chat_text_area'].insert(END, "  " + messages + "\n")
                self.children['chat_text_area'].see(END)
            else:  # 显示私聊
                self.children['chat_text_area'].insert(END, send_info, 'blue')  # END将信息加在最后一行
                self.children['chat_text_area'].insert(END, "  " + messages + "\n")
                self.children['chat_text_area'].see(END)  # 显示在最后

登录界面:
 


from tkinter import Tk
from tkinter import Label
from tkinter import Entry
from tkinter import Button
from tkinter import END



class WindowLogin(Tk):
    def __init__(self):
        super(WindowLogin, self).__init__()

        # 初始化窗口
        self.window_init()

        # 添加控件
        self.add_widgets()

    def window_init(self):
        self.title('登录')
        self.resizable(False, False)

        window_width = 450
        window_height = 300

        screen_width = self.winfo_screenwidth()
        screen_height = self.winfo_screenheight()

        position_x = (screen_width - window_width) / 2
        position_y = (screen_height - window_height) / 2

        # 设置窗体大小和位置
        self.geometry("%dx%d+%d+%d" % (window_width, window_height, position_x, position_y))

    def add_widgets(self):
        login_title = Label(self, fg="black", font=('宋体', 20))
        login_title["text"] = "在 线 聊 天 室 登 录"
        login_title.place(x=80, y=30)

        username_label = Label(self)
        username_label["text"] = "用户名:"
        username_label.place(x=90, y=130)

        username_entry = Entry(self, name='username_entry')
        username_entry["width"] = 30
        username_entry.place(x=150, y=130)

        password_label = Label(self)
        password_label["text"] = "密   码:"
        password_label.place(x=90, y=170)

        password_entry = Entry(self, name='password_entry')
        password_entry["width"] = 30
        password_entry["show"] = "*"
        password_entry.place(x=150, y=170)

        reset_button = Button(self, name='reset_button', bg="#00BFFF", fg="white", font=('宋体', 12))
        reset_button["text"] = " 重 置 "
        reset_button.place(x=130, y=210)

        login_button = Button(self, name='login_button', bg="#00BFFF", fg="white", font=('宋体', 12))
        login_button["text"] = " 登 录 "
        login_button.place(x=240, y=210)

    def on_login_click(self, command):
        login_button = self.children["login_button"]
        login_button["command"] = command

    def on_reset_click(self, command):
        reset_button = self.children["reset_button"]
        reset_button["command"] = command

    def get_username(self):
        return self.children["username_entry"].get()

    def get_password(self):
        return self.children["password_entry"].get()

    def clear_username(self):
        return self.children["username_entry"].delete(0, END)

    def clear_password(self):
        return self.children["password_entry"].delete(0, END)

    def on_window_closed(self, command):
        self.protocol('WM_DELETE_WINDOW', command)


if __name__ == '__main__':
    window = WindowLogin()
    window.mainloop()

客户端主要功能的变化:

from tkinter import END, Button, FLAT

from window_login import WindowLogin
from request_protocol import RequestProtocol
from client_socket import ClientSocket
from threading import Thread
from config import *
from tkinter.messagebox import showinfo
from window_chat import WindowChat
import sys


class Client:
    def __init__(self):

        # 初始化用户名 用于保存在线用户
        self.username = None
        self.users = set()  # 在线用户列表
        self.pri_user = ''  # 私聊对象
        self.chat = '------------------群聊------------------'  # 聊天对象, 默认为群聊

        # 初始化登录界面
        self.window = WindowLogin()

        self.window.on_window_closed(self.exit)

        """重置按钮 和 登录按钮 的事件处理"""
        self.window.on_reset_click(self.clear_inputs)
        self.window.on_login_click(self.send_login_data)

        # 初始化聊天界面
        self.window_chat = WindowChat()
        self.window_chat.withdraw()  # 隐藏聊天窗口 等登陆成功后再显示
        self.window_chat.on_window_closed(self.exit)
        self.window_chat.on_eBut_click(self.express)

        # 在显示用户列表框上设置绑定事件
        self.window_chat.children['chat_count'].bind('<ButtonRelease-1>', self.privat_user)

        """发送信息按钮的 事件处理函数"""
        self.window_chat.on_send_button_click(self.send_chat_data)
        self.window_chat.on_reset_button_click(self.reset_chat_data)

        # 创建套接字
        self.conn = ClientSocket()

        self.response_handle_function = {}
        self.register(RESPONSE_LOGIN_RESULT, self.response_login_handle)
        self.register(RESPONSE_CHAT, self.response_chat_handle)

    def register(self, response_id, handle_function):
        self.response_handle_function[response_id] = handle_function

    def startup(self):
        self.conn.connect()  # 自动连接服务器
        Thread(target=self.response_handle).start()  # 开启多线程实现数据接收
        self.window.mainloop()

    def clear_inputs(self):
        self.window.clear_username()
        self.window.clear_password()

    def send_login_data(self):
        # 获取用户名 和 密码
        username = self.window.get_username()
        password = self.window.get_password()

        # 封装请求信息
        request_text = RequestProtocol.request_login_result(username, password)

        # 蒋请求信息发送给服务器
        print("客户端的请求登录的信息为:" + request_text)
        self.conn.send_data(request_text)
        # print(self.conn.recv_data())

    def send_chat_data(self):
        msg = self.window_chat.get_inputs()
        self.window_chat.clear_inputs()
        print("%s" % msg)

        # 封装数据 发送到服务端
        request_text = RequestProtocol.request_chat_result(self.username, msg, self.chat)
        self.conn.send_data(request_text)

        # 自己界面显示的信息
        self.window_chat.append_message("我", msg, self.chat)

    def mark(self, exp):  # 参数是发的表情图标记, 发送后将按钮销毁
        msg = exp
        request_text = RequestProtocol.request_chat_result(self.username, msg, self.chat)
        self.window_chat.append_message("我", msg, self.chat)
        self.conn.send_data(request_text)

        self.window_chat.b1.destroy()
        self.window_chat.b2.destroy()
        self.window_chat.b3.destroy()
        self.window_chat.b4.destroy()
        self.window_chat.ee = 0

    # 四个对应的函数
    def bb1(self):
        self.mark('aa**')

    def bb2(self):
        self.mark('bb**')

    def bb3(self):
        self.mark('cc**')

    def bb4(self):
        self.mark('dd**')

    def express(self):
        if self.window_chat.ee == 0:
            self.window_chat.ee = 1
            self.window_chat.b1 = Button(self.window_chat, image=self.window_chat.p1, command=self.bb1, relief=FLAT,
                                         bd=0)
            self.window_chat.b2 = Button(self.window_chat, image=self.window_chat.p2, command=self.bb2, relief=FLAT,
                                         bd=0)
            self.window_chat.b3 = Button(self.window_chat, image=self.window_chat.p3, command=self.bb3, relief=FLAT,
                                         bd=0)
            self.window_chat.b4 = Button(self.window_chat, image=self.window_chat.p4, command=self.bb4, relief=FLAT,
                                         bd=0)
            self.window_chat.b1.place(x=10, y=333)
            self.window_chat.b2.place(x=80, y=333)
            self.window_chat.b3.place(x=150, y=333)
            self.window_chat.b4.place(x=220, y=333)
        else:
            self.window_chat.ee = 0
            self.window_chat.b1.destroy()
            self.window_chat.b2.destroy()
            self.window_chat.b3.destroy()
            self.window_chat.b4.destroy()

    def reset_chat_data(self):
        self.window_chat.clear_inputs()

    def response_handle(self):
        while True:
            msg = self.conn.recv_data()
            if not msg:
                self.conn.close()
                break
            print("服务端返回的消息:%s" % msg)
            # 解析数据
            response_data = self.parser_response_data(msg)

            handle_function = self.response_handle_function[response_data['response_id']]
            if handle_function:
                handle_function(response_data)
            # 接收到的是在线用户列表
            self.window_chat.children['chat_count'].delete(0, END)  # 清空列表框
            number = ('     在线人数: ' + str(len(self.users)) + ' 人')
            self.window_chat.children['chat_count'].insert(END, number)
            self.window_chat.children['chat_count'].itemconfig(END, fg='green', bg="#f0f0ff")
            self.window_chat.children['chat_count'].insert(END, '------------------群聊------------------')
            self.window_chat.children['chat_count'].itemconfig(END, fg='green')
            for i in range(len(self.users)):
                self.window_chat.children['chat_count'].insert(END, (list(self.users)[i]))
                self.window_chat.children['chat_count'].itemconfig(END, fg='green')

    def parser_response_data(self, msg):
        # 两种数据类型 登录1001|登陆结果|昵称|username  聊天 1002|昵称|messages
        response_data_list = msg.split(DELIMITER)
        response_data = {}
        response_data['response_id'] = response_data_list[0]

        if response_data['response_id'] == RESPONSE_LOGIN_RESULT:
            response_data['result'] = response_data_list[1]
            response_data['nickname'] = response_data_list[2]
            response_data['username'] = response_data_list[3]
            self.users = eval(response_data_list[4])
            print(self.users)
        elif response_data['response_id'] == RESPONSE_CHAT:
            response_data['nickname'] = response_data_list[1]
            response_data['messages'] = response_data_list[2]
            self.users = eval(response_data_list[3])
            self.chat = response_data_list[4]
        return response_data

    def response_login_handle(self, response_data):
        print("客户端正在处理登录:%s" % response_data)
        result = response_data['result']

        if result == '0':
            showinfo("登录失败", "登录失败,用户名或密码错误!")
            print("登陆失败!")
            return

        # 登陆成功 获取昵称和用户名
        showinfo("登陆成功", "登陆成功!")
        nickname = response_data['nickname']
        self.username = response_data['username']

        self.window_chat.set_title(nickname)
        self.window_chat.update()
        self.window_chat.deiconify()  # 登陆成功 显示聊天界面
        self.window.withdraw()  # 登录成功 隐藏登录界面

    def response_chat_handle(self, response_data):
        print("客户端正在处理聊天信息:%s" % response_data)

        sender = response_data['nickname']
        messages = response_data['messages']
        self.window_chat.append_message(sender, messages, self.chat)

    # 设置私聊时的窗体标题
    def privat_user(self, response_data):
        # 获取点击的索引然后得到内容(用户名)
        indexs = self.window_chat.children['chat_count'].curselection()
        index = indexs[0]
        self.chat = self.window_chat.children['chat_count'].get(index)
        # 修改客户端名称
        if self.chat == '------------------群聊------------------':
            self.window_chat.set_title(self.username)
            return
        ti = '我' + '  -->  ' + self.chat
        self.window_chat.set_title(ti)
        return self.chat

    def exit(self):
        self.conn.close()
        sys.exit(0)  # 关闭程序

源码地址: 网络编程课程设计.rar-Python文档类资源-CSDN下载

小明用基于socket通道做了一个实时聊天,实现了多客户客户端、服务端的实时通信,但是老师要求每个人都有自己的独特功能........于是,小明想,创新是不可能创新的,那现在QQ的工鞥有什么能够移植的呢,,,小明效果了一圈,发现大多功能都是基于文件传输,去找了一下socket的文件传输,果断撂挑子。还有什呢,要不就自己客户端约定好,特殊的字符串表示特殊操作,比如下面小明要举个例子:                                                                                                                                   有两个客户端A、B,客户端都是一模一样的,里面资源实现存放好10张图片,并且约定:发送消息为“@”的时候,这个字符串后边会跟着一个数字,咱们两个同时把数字代表编号的图片输出到聊天框,如                                                                A发送给B:@     那现在,两个客户端就会把3号图片输出到聊天框,造成发送的是图片的假象 说干就干,先实现自己窗口判断输出图片,在获取于是先在获取聊天输入区文本时做判断(我正添加表按钮,到时候就不用记住暗号了),在输出相应编号图片,欸不对啊,这个聊天信息展示区是个文本框啊,那就给改成面板,使用新建面板类,使用流式布局规定布局宽度(不然他会横着输出消息)然后是面板添加滚动条,然后再聊天区打出来就好了 ----(一个积分,望土豪点一点,我也想恰饭) 你们看到这个代码的时候,我主页应该就有相关博文了,大家可以去参考
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BlackStar-Coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值