吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2704|回复: 2
收起左侧

[Python 原创] 【Python】利用socket做的一个类似早期QQ的聊天程序

[复制链接]
suifeng_king 发表于 2019-9-17 19:34
完整程序还未完成,有什么问题或者建议欢迎大家提出来:loveliness:

登录窗口:

登录界面

登录界面


注册窗口:

注册界面

注册界面


聊天界面(直接点击X关闭程序接收线程不会退出):

双击左侧用户列表的用户名开始一对一发送消息。
chat.JPG

聊天界面

聊天界面


程序运行:

  1. 首先运行服务端servers.py;
  2. 然后运行login_GUI.py启动登录界面。

数据库结构:
由于当前功能还不完整,只有一个用户表和消息表
用户表(database_usersprofile):

  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `password` varchar(65) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `nickname` varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `date_joined` datetime(6) NOT NULL,
  `last_login` datetime(6) NULL DEFAULT NULL,
  `last_ip` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE

消息表(messages):

  `id` int(11) NOT NULL AUTO_INCREMENT,
  `send_to` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `from_user` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `status` tinyint(1) NOT NULL,
  `send_time` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `content` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `send_type` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`id`) USING BTREE

客户端client.py的代码:

# -*- coding: utf-8 -*-
# @file    : client.py
# @Software: PyCharm

import socket
import threading
import json
import time

class SFChatClient(object):
    def __init__(self):
        # 创建TCP套接字
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 链接服务器
        server_ip = '192.168.72.1'
        server_port = 8888
        server_addr = (server_ip, server_port)
        self.socket.connect(server_addr)

        self.username = ''
        self.is_login = False
        self.online_users_list = list()

    def __del__(self):
        # 关闭套接字
        self.socket.close()
        print('已结束!!!')

    def login(self, username, password):
        """用户登录"""
        auth_recv = threading.Thread(target=self.recv_msg)
        auth_recv.start()
        print('正在登录, 请稍等……')
        self.username = username
        self.send_msg(content={'username': username, 'password': password}, send_type='login')
        time.sleep(1.5)

    def send_msg(self, content, send_to='', send_type='msg'):
        """
        :param content: <class 'dict'> The message that you what to send.
        :param send_to: <class 'str'> Who receives information.
        :param send_type:
        :return:
        """
        data = content
        data['send_type'] = send_type
        data['send_to'] = send_to
        data['from_user'] = self.username
        data['send_time'] = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(time.time() + 28800))
        # 发送数据
        self.socket.send(json.dumps(data).encode('utf-8'))  # 以json字符串经过utf-8编码发送

    def recv_msg(self):
        """接收各种消息并处理"""
        recv_data = self.socket.recv(1024)
        recv_dict = json.loads(recv_data.decode('utf-8'))
        if recv_dict.get('send_type', '') == 'login':
            if recv_dict.get('result', None) == 'ok':
                self.is_login = True
                print('登陆成功!!!')
            else:
                print(recv_dict.get('result'))
                self.username = ''
        elif recv_dict.get('send_type', '') == 'register':
            print(recv_dict.get('result'))
        elif recv_dict.get('send_type', '') == 'msg':
            content = recv_dict.get('content', '')
            send_time = recv_dict.get('send_time', '')
            print(send_time + '\n收到来自'+recv_dict.get('from_user', '')+'的消息:' + content)
        elif recv_dict.get('send_type', '') == 'is_online':
            pass
        elif recv_dict.get('send_type', '') == 'online_users':
            self.online_users_list = recv_dict.get('result', None)
        return recv_dict

    def recv_msg_always(self):
        while True:
            self.recv_msg()

    def get_users_list(self):
        self.send_msg(content={}, send_type='online_users')

    def register(self, username, password):
        auth_recv = threading.Thread(target=self.recv_msg)
        auth_recv.start()
        self.username = username
        time.sleep(0.5)
        self.send_msg(content={'username': username, 'password': password}, send_type='register')
        print('正在验证信息, 请稍等……')

    def search_users(self, username=''):
        pass

    def add_friends(self, username=''):
        pass

    def delete_friend(self, username=''):
        pass

    def create_group(self, group_id=''):
        pass

    def join_group(self, group_id=''):
        pass

    def quit_group(self, group_id=''):
        pass

客户端图形界面

  1. login_GUI.py
# -*- coding: utf-8 -*-
# @File    : login_GUI.py.py
# @Software: PyCharm
import threading
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QMessageBox
from login_window import Ui_LoginWindow
from users_lists_GUI import UsersListsWindow
from register_GUI import RegisterWindow
from client import SFChatClient

class LoginWindow(QMainWindow, Ui_LoginWindow):
    def __init__(self):
        super(LoginWindow, self).__init__()
        self.setupUi(self)

        self.users_lists_window = UsersListsWindow()
        self.register_window = RegisterWindow()

        self.pushButton_login.clicked.connect(self.pushButton_login_click)
        self.pushButton_register.clicked.connect(self.pushButton_register_click)

    def pushButton_login_click(self):
        username = str(self.lineEdit_username.text())
        password = str(self.lineEdit_password.text())
        try:
            chat_obj = SFChatClient()
            chat_obj.login(username=username, password=password)
        except:
            QMessageBox.warning(self, '网络出错', '链接服务器失败,请检查网络或联系管理员!!!')
            return
        if chat_obj.is_login:
            self.users_lists_window.get_chat_obj(chat_obj)
            self.users_lists_window.show()
            self.hide()
            threading.Thread(target=self.recv_msg_always, args=(chat_obj,)).start()
            self.users_lists_window.get_online_users()
            self.users_lists_window.label_username.setText("<html><head/><body><p align=\"center\"><span style=\" font-size:11pt; font-weight:600;\">"+chat_obj.username+"</span></p></body></html>")
        else:
            QMessageBox.warning(self, '登陆失败!!!', '用户名或密码错误!!!')

    def pushButton_register_click(self):
        try:
            chat_obj = SFChatClient()
        except:
            QMessageBox.warning(self, '网络出错', '链接服务器失败,请检查网络或联系管理员!!!')
            return
        self.register_window.get_chat_obj(chat_obj)
        self.register_window.show()

    def recv_msg_always(self, chat_obj):
        while True:
            recv_dict = chat_obj.recv_msg()
            if recv_dict.get('send_type', '') == 'msg':
                content = recv_dict.get('content', '')
                send_time = recv_dict.get('send_time', '')
                from_user = recv_dict.get('from_user', '')
                print(send_time + '\n收到来自' + from_user + '的消息:' + content)
                self.users_lists_window.listWidget_recv_msg.addItem(send_time + '//' + from_user + ' : ' + content)
            elif recv_dict.get('send_type', '') == 'logout':
                break

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = LoginWindow()
    window.show()
    sys.exit(app.exec_())
  1. login_window.py
# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_LoginWindow(object):
    def setupUi(self, LoginWindow):
        LoginWindow.setObjectName("LoginWindow")
        LoginWindow.resize(446, 291)
        LoginWindow.setMinimumSize(QtCore.QSize(446, 291))
        LoginWindow.setMaximumSize(QtCore.QSize(446, 291))
        LoginWindow.setStyleSheet("")
        self.centralwidget = QtWidgets.QWidget(LoginWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label_name = QtWidgets.QLabel(self.centralwidget)
        self.label_name.setGeometry(QtCore.QRect(40, 20, 361, 31))
        self.label_name.setObjectName("label_name")
        self.pushButton_register = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_register.setGeometry(QtCore.QRect(100, 200, 101, 31))
        self.pushButton_register.setObjectName("pushButton_register")
        self.pushButton_login = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_login.setGeometry(QtCore.QRect(250, 200, 101, 31))
        self.pushButton_login.setObjectName("pushButton_login")
        self.lineEdit_username = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit_username.setGeometry(QtCore.QRect(160, 80, 221, 31))
        self.lineEdit_username.setObjectName("lineEdit_username")
        self.lineEdit_password = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit_password.setGeometry(QtCore.QRect(160, 130, 221, 31))
        self.lineEdit_password.setObjectName("lineEdit_password")
        self.label_password = QtWidgets.QLabel(self.centralwidget)
        self.label_password.setGeometry(QtCore.QRect(70, 80, 71, 31))
        self.label_password.setObjectName("label_password")
        self.label_username = QtWidgets.QLabel(self.centralwidget)
        self.label_username.setGeometry(QtCore.QRect(70, 140, 71, 21))
        self.label_username.setObjectName("label_username")
        LoginWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(LoginWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 446, 26))
        self.menubar.setObjectName("menubar")
        LoginWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(LoginWindow)
        self.statusbar.setObjectName("statusbar")
        LoginWindow.setStatusBar(self.statusbar)

        self.retranslateUi(LoginWindow)
        QtCore.QMetaObject.connectSlotsByName(LoginWindow)

    def retranslateUi(self, LoginWindow):
        _translate = QtCore.QCoreApplication.translate
        LoginWindow.setWindowTitle(_translate("LoginWindow", "登录"))
        LoginWindow.setToolTip(_translate("LoginWindow", "<html><head/><body><p><br/></p></body></html>"))
        self.label_name.setText(_translate("LoginWindow", "<html><head/><body><p align=\"center\"><span style=\" font-size:18pt; font-weight:600;\">名字还没想好……</span></p></body></html>"))
        self.pushButton_register.setText(_translate("LoginWindow", "注册"))
        self.pushButton_login.setText(_translate("LoginWindow", "登录"))
        self.label_password.setText(_translate("LoginWindow", "<html><head/><body><p><span style=\" font-size:11pt; font-weight:600;\">用户名:</span></p></body></html>"))
        self.label_username.setText(_translate("LoginWindow", "<html><head/><body><p><span style=\" font-size:11pt; font-weight:600;\">密 码:</span></p></body></html>"))
  1. register_GUI.py
# -*- coding: utf-8 -*-
# @Time    : 2019/9/16 16:00
# @File    : register_GUI.py
# @Software: PyCharm
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox

from register_window import Ui_RegisterWindow

class RegisterWindow(QWidget, Ui_RegisterWindow):
    def __init__(self):
        super(RegisterWindow, self).__init__()
        self.setupUi(self)

        self.chat_obj = None

        self.pushButton_register.clicked.connect(self.register_submit)

    def register_submit(self):
        username = str(self.lineEdit_username.text())
        if username == '':
            QMessageBox.warning(self, '不能为空', '用户名不能为空!!!')
            return
        password = str(self.lineEdit_password.text())
        re_password = str(self.lineEdit_re_password.text())
        if password == '' or password != re_password:
            QMessageBox.warning(self, '密码不匹配', '两次输入的密码不一致或为空!!!')
            return
        self.chat_obj.send_msg(content={'username': username, 'password': password}, send_type='register')
        response = self.chat_obj.recv_msg()
        if response.get('code', 1) == 0:
            QMessageBox.information(self, '注册成功', response.get('result', '注册成功!!!'))
            self.hide()
        else:
            QMessageBox.warning(self, '注册失败', response.get('result', '该用户名已存在!!!'))

    def get_chat_obj(self, chat_obj):
        self.chat_obj = chat_obj
  1. register_window.py
# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_RegisterWindow(object):
    def setupUi(self, RegisterWindow):
        RegisterWindow.setObjectName("RegisterWindow")
        RegisterWindow.resize(381, 321)
        RegisterWindow.setMinimumSize(QtCore.QSize(381, 321))
        RegisterWindow.setMaximumSize(QtCore.QSize(381, 321))
        self.label_title = QtWidgets.QLabel(RegisterWindow)
        self.label_title.setGeometry(QtCore.QRect(30, 30, 321, 31))
        self.label_title.setObjectName("label_title")
        self.label_username = QtWidgets.QLabel(RegisterWindow)
        self.label_username.setGeometry(QtCore.QRect(30, 100, 111, 31))
        self.label_username.setObjectName("label_username")
        self.label_password = QtWidgets.QLabel(RegisterWindow)
        self.label_password.setGeometry(QtCore.QRect(30, 150, 111, 31))
        self.label_password.setObjectName("label_password")
        self.label_re_password = QtWidgets.QLabel(RegisterWindow)
        self.label_re_password.setGeometry(QtCore.QRect(30, 200, 111, 31))
        self.label_re_password.setObjectName("label_re_password")
        self.lineEdit_username = QtWidgets.QLineEdit(RegisterWindow)
        self.lineEdit_username.setGeometry(QtCore.QRect(170, 100, 181, 31))
        self.lineEdit_username.setObjectName("lineEdit_username")
        self.lineEdit_password = QtWidgets.QLineEdit(RegisterWindow)
        self.lineEdit_password.setGeometry(QtCore.QRect(170, 150, 181, 31))
        self.lineEdit_password.setObjectName("lineEdit_password")
        self.lineEdit_re_password = QtWidgets.QLineEdit(RegisterWindow)
        self.lineEdit_re_password.setGeometry(QtCore.QRect(170, 200, 181, 31))
        self.lineEdit_re_password.setObjectName("lineEdit_re_password")
        self.pushButton_register = QtWidgets.QPushButton(RegisterWindow)
        self.pushButton_register.setGeometry(QtCore.QRect(30, 260, 321, 31))
        font = QtGui.QFont()
        font.setPointSize(14)
        self.pushButton_register.setFont(font)
        self.pushButton_register.setObjectName("pushButton_register")

        self.retranslateUi(RegisterWindow)
        QtCore.QMetaObject.connectSlotsByName(RegisterWindow)

    def retranslateUi(self, RegisterWindow):
        _translate = QtCore.QCoreApplication.translate
        RegisterWindow.setWindowTitle(_translate("RegisterWindow", "用户注册"))
        self.label_title.setText(_translate("RegisterWindow", "<html><head/><body><p align=\"center\"><span style=\" font-size:18pt; font-weight:600;\">用户注册</span></p></body></html>"))
        self.label_username.setText(_translate("RegisterWindow", "<html><head/><body><p align=\"center\"><span style=\" font-size:14pt; font-weight:600;\">用户名:</span></p></body></html>"))
        self.label_password.setText(_translate("RegisterWindow", "<html><head/><body><p align=\"center\"><span style=\" font-size:14pt; font-weight:600;\">密  码:</span></p></body></html>"))
        self.label_re_password.setText(_translate("RegisterWindow", "<html><head/><body><p align=\"center\"><span style=\" font-size:14pt; font-weight:600;\">重复密码:</span></p></body></html>"))
        self.pushButton_register.setText(_translate("RegisterWindow", "提交"))
  1. users_lists_GUI.py
# -*- coding: utf-8 -*-
# @Time    : 2019/9/12 10:12
# @File    : users_lists_GUI.py
# @Software: PyCharm
import time
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QListWidgetItem
from PyQt5.QtGui import QPixmap

# from MyGUI.users_lists_window import Ui_UsersLists
from users_lists_window import Ui_UsersLists

class UsersListsWindow(QWidget, Ui_UsersLists):
    def __init__(self):
        super(UsersListsWindow, self).__init__()
        self.setupUi(self)

        self.chat_obj = None
        self.send_to = ''

        self.label_show.setPixmap(QPixmap('tushansusu.jpg'))
        self.listWidget_online_users.doubleClicked.connect(lambda: self.start_chat(self.listWidget_online_users))
        # self.listWidget_friends.doubleClicked.connect(lambda: self.start_chat(self.listWidget_friends))
        self.pushButton_fresh_online_users.clicked.connect(self.get_online_users)
        self.pushButton_send_msg.clicked.connect(self.send_msg)
        self.pushButton_exit.clicked.connect(self.log_out_exit)

    def start_chat(self, listwidget):
        print(listwidget.currentItem().text())
        self.send_to = listwidget.currentItem().text()
        self.label_user_to.setText('<html><head/><body><p align=\"center\"><span style=\" font-size:11pt; font-weight:600;\">正在给'+listwidget.currentItem().text()+'发送消息</span></p></body></html>')

    def send_msg(self):
        text = self.textEdit_msg_edit.toPlainText()
        if text == '':
            QMessageBox.warning(self, '内容为空', '内容为空!!!')
            return
        if self.send_to == '':
            QMessageBox.warning(self, '未选择接收用户', '请双击左侧列表用户名选择接收用户!!!')
            return
        self.chat_obj.send_msg(content={'content': text}, send_to=self.send_to)
        self.textEdit_msg_edit.clear()

    def get_chat_obj(self, chat_obj):
        self.chat_obj = chat_obj
        self.setWindowTitle(self.chat_obj.username)

    def get_online_users(self):
        self.listWidget_online_users.clear()
        self.chat_obj.get_users_list()
        time.sleep(1.5)
        for user in self.chat_obj.online_users_list:
            self.listWidget_online_users.addItem(user)

    def log_out_exit(self):
        self.chat_obj.send_msg(content={}, send_type='logout')
        self.close()
  1. users_lists_window.py
# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_UsersLists(object):
    def setupUi(self, UsersLists):
        UsersLists.setObjectName("UsersLists")
        UsersLists.resize(951, 646)
        UsersLists.setMinimumSize(QtCore.QSize(951, 646))
        UsersLists.setMaximumSize(QtCore.QSize(951, 646))
        self.listWidget_online_users = QtWidgets.QListWidget(UsersLists)
        self.listWidget_online_users.setGeometry(QtCore.QRect(20, 120, 131, 471))
        self.listWidget_online_users.setObjectName("listWidget_online_users")
        self.label_online_users = QtWidgets.QLabel(UsersLists)
        self.label_online_users.setGeometry(QtCore.QRect(20, 90, 131, 21))
        self.label_online_users.setObjectName("label_online_users")
        self.label_friends = QtWidgets.QLabel(UsersLists)
        self.label_friends.setGeometry(QtCore.QRect(180, 90, 101, 21))
        self.label_friends.setObjectName("label_friends")
        self.listWidget_friends = QtWidgets.QListWidget(UsersLists)
        self.listWidget_friends.setGeometry(QtCore.QRect(180, 120, 131, 471))
        self.listWidget_friends.setObjectName("listWidget_friends")
        item = QtWidgets.QListWidgetItem()
        self.listWidget_friends.addItem(item)
        self.label_username = QtWidgets.QLabel(UsersLists)
        self.label_username.setGeometry(QtCore.QRect(70, 30, 171, 41))
        self.label_username.setObjectName("label_username")
        self.pushButton_fresh_online_users = QtWidgets.QPushButton(UsersLists)
        self.pushButton_fresh_online_users.setGeometry(QtCore.QRect(20, 600, 131, 31))
        self.pushButton_fresh_online_users.setObjectName("pushButton_fresh_online_users")
        self.pushButton_exit = QtWidgets.QPushButton(UsersLists)
        self.pushButton_exit.setGeometry(QtCore.QRect(180, 600, 131, 31))
        self.pushButton_exit.setObjectName("pushButton_exit")
        self.pushButton_send_msg = QtWidgets.QPushButton(UsersLists)
        self.pushButton_send_msg.setGeometry(QtCore.QRect(610, 600, 93, 28))
        self.pushButton_send_msg.setObjectName("pushButton_send_msg")
        self.listWidget_recv_msg = QtWidgets.QListWidget(UsersLists)
        self.listWidget_recv_msg.setGeometry(QtCore.QRect(340, 120, 371, 311))
        self.listWidget_recv_msg.setObjectName("listWidget_recv_msg")
        self.textEdit_msg_edit = QtWidgets.QTextEdit(UsersLists)
        self.textEdit_msg_edit.setGeometry(QtCore.QRect(340, 450, 371, 141))
        self.textEdit_msg_edit.setObjectName("textEdit_msg_edit")
        self.label_show = QtWidgets.QLabel(UsersLists)
        self.label_show.setGeometry(QtCore.QRect(750, 120, 171, 471))
        self.label_show.setObjectName("label_show")
        self.label_user_to = QtWidgets.QLabel(UsersLists)
        self.label_user_to.setGeometry(QtCore.QRect(350, 80, 341, 41))
        self.label_user_to.setObjectName("label_user_to")

        self.retranslateUi(UsersLists)
        QtCore.QMetaObject.connectSlotsByName(UsersLists)

    def retranslateUi(self, UsersLists):
        _translate = QtCore.QCoreApplication.translate
        UsersLists.setWindowTitle(_translate("UsersLists", "Form"))
        self.label_online_users.setText(_translate("UsersLists", "<html><head/><body><p><span style=\" font-size:11pt; font-weight:600;\">在线用户列表:</span></p></body></html>"))
        self.label_friends.setText(_translate("UsersLists", "<html><head/><body><p><span style=\" font-size:11pt; font-weight:600;\">我的好友:</span></p></body></html>"))
        __sortingEnabled = self.listWidget_friends.isSortingEnabled()
        self.listWidget_friends.setSortingEnabled(False)
        item = self.listWidget_friends.item(0)
        item.setText(_translate("UsersLists", "功能暂未开发"))
        self.listWidget_friends.setSortingEnabled(__sortingEnabled)
        self.label_username.setText(_translate("UsersLists", "<html><head/><body><p align=\"center\"><span style=\" font-size:11pt; font-weight:600;\">我的用户名</span></p></body></html>"))
        self.pushButton_fresh_online_users.setText(_translate("UsersLists", "刷新在线用户"))
        self.pushButton_exit.setText(_translate("UsersLists", "退出程序"))
        self.pushButton_send_msg.setText(_translate("UsersLists", "发送"))
        self.label_show.setText(_translate("UsersLists", "TextLabel"))
        self.label_user_to.setText(_translate("UsersLists", "<html><head/><body><p align=\"center\"><span style=\" font-size:11pt; font-weight:600;\">正在与某某某聊天</span></p></body></html>"))

服务端servers.py的代码(数据库需要按照自己的配置):

# -*- coding: utf-8 -*-
# @File    : servers.py
# @Software: PyCharm

import socket
import threading
import time
import json
import MySQLdb
import hashlib

class SFChatServers(object):
    def __init__(self):
        # 创建TCP套接字
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 绑定本地信息
        self.socket.bind(('192.168.72.1', 8888))
        # 将套接字由主动变为被动
        self.socket.listen(128)

        self.online_users = {}      # 用字典信息存储当前在线的用户
        self.users_addr = {}        # 保存用户IP

        self.db = MySQLdb.connect(
            host='localhost',
            port=3306,
            user='root',
            password='root',
            db='sf_chat',
            charset='utf8'
        )

    def __del__(self):
        """服务端程序退出时关闭所有已连接的用户并关闭监听socket"""
        # 关闭套接字
        for username in self.online_users:
            self.online_users[username].close()
        self.socket.close()
        self.db.close()
        print('已结束!!!')

    def listen(self):
        """监听连接,每当用户登录到聊天工具时开始开启一个该用户的线程"""
        while True:
            new_socket, client_addr = self.socket.accept()
            print(client_addr)
            t1 = threading.Thread(target=self.recv_data, args=(new_socket, client_addr[0]))
            t1.start()
            print('线程启动')

    def recv_data(self, temp_socket, client_addr):
        """接收用户的各种信息并分别处理"""
        while True:
            # 接受数据,并解码,转换为字典形式暂时存储
            recv_data = temp_socket.recv(1024)
            recv_data = recv_data.decode('utf-8')
            recv_data = json.loads(recv_data)
            print(recv_data)        # 调试打印调试打印调试打印调试打印调试打印调试打印调试打印调试打印调试打印调试打印
            if recv_data['send_type'] == 'login':
                # 登录处理, 验证成功继续等待, 直到客户端退出, 断开连接
                if not self.login_auth(temp_socket, recv_data, client_addr=client_addr):
                    break
            elif recv_data['send_type'] == 'register':  # v1.1版本新添加了用户注册功能
                self.register(temp_socket, recv_data)
            elif recv_data['send_type'] == 'msg':
                self.send_msg(temp_socket, recv_data, send_to=recv_data['send_to'])     # 普通消息处理,转发至目标用户
            elif recv_data['send_type'] == 'online_users':
                self.send_users_list(temp_socket)       # 请求在线人数处理,
            elif recv_data['send_type'] == 'logout':
                logout_user = recv_data.get('from_user', '')
                del self.online_users[logout_user]
                self.send_msg(temp_socket, recv_data)
                print(logout_user + '已退出')
                break
            elif recv_data['send_type'] == 'recv_return':
                pass

    def login_auth(self, temp_socket, recv_data, client_addr):
        """用户登录身份验证的具体实现"""
        username = recv_data['username']
        password = recv_data['password']
        send_time = recv_data['send_time']
        sh = hashlib.sha256()
        sh.update((username + password + 'cyj').encode('utf8'))
        password_hash = sh.hexdigest()
        cur = self.db.cursor()
        cur.execute('select * from database_usersprofile where username=%s and password=%s', [username, password_hash])
        user = cur.fetchone()
        if user is not None:
            self.send_msg(temp_socket, {'result': 'ok', 'send_type': 'login'}, recv_data['send_to'])
            self.online_users[recv_data['username']] = temp_socket
            cur.execute("""update database_usersprofile set last_login=%s,last_ip=%s where username=%s""", [send_time, client_addr, username])
            self.db.commit()
            cur.close()
            return True
        else:
            self.send_msg(temp_socket, {'result': '用户不存在或密码错误!!!', 'send_type': 'login'}, recv_data['send_to'])
            cur.close()
            return False

    def send_msg(self, temp_socket, content, send_to=''):
        """
        向客户端发送消息, 或者存入数据库
        :param temp_socket:
        :param content:
        :param send_to:
        :return:
        """
        # 消息反馈, 对接收到的客户端大多数行为进行反馈, 比如登录注册
        if content.get('send_type', '') == 'msg':
            temp_socket.send(json.dumps({'send_type': 'recv_return', 'result': 'ok'}).encode('utf-8'))   #
        else:
            temp_socket.send(json.dumps(content).encode('utf-8'))       #

        if content.get('send_type', '') == 'msg':
            # 此处从数据库查找目的用户,在线尝试发送,离线保存数据库
            user_socket = self.online_users.get(send_to, None)
            from_user = content.get('from_user', '')
            send_time = content.get('send_time', '')
            msg = content.get('content', '')
            cur = self.db.cursor()
            if user_socket is None:
                # 此处用户离线存入数据库
                cur.execute("""insert into messages(send_to,from_user,status,send_time,content,send_type)
                            values(%s,%s,%s,%s,%s,%s)""",
                            [send_to, from_user, 0, send_time, msg, 'msg'])
            else:
                try:
                    user_socket.send(json.dumps(content).encode('utf-8'))
                    # 发送过去也应该存入数据库, 只是状态是已读
                    cur.execute("""insert into messages(send_to,from_user,status,send_time,content,send_type)
                                values(%s,%s,%s,%s,%s,%s)""",
                                [send_to, from_user, 1, send_time, msg, 'msg'])
                except:
                    cur.execute("""insert into messages(send_to,from_user,status,send_time,content,send_type)
                                values(%s,%s,%s,%s,%s,%s)""",
                                [send_to, from_user, 0, send_time, msg, 'msg'])
            self.db.commit()
            cur.close()

    def save_to_database(self):
        """把数据存入数据库"""
        # 数据应该包括发送人, 发送时间, 内容, 消息状态, 接收人
        # 可以考虑使用MySQL的外键
        pass

    def send_users_list(self, temp_socket):
        """获取在线用户并发送"""
        users_list = list()
        logout_list = list()
        for user in self.online_users:
            try:
                self.online_users[user].send(json.dumps({'send_type': 'is_online'}).encode('utf-8'))
                users_list.append(user)
            except:
                logout_list.append(user)
                print(user, '已退出')
        for i in logout_list:
            if self.online_users.get(i, None) is not None:
                del self.online_users[i]
        temp_socket.send(json.dumps({'send_type': 'online_users', 'result': users_list}).encode('utf-8'))

    def register(self, temp_socket, recv_data):
        username = recv_data['username']
        password = recv_data['password']
        send_time = recv_data['send_time']
        cur = self.db.cursor()
        cur.execute('select * from database_usersprofile where username=%s', [username])
        user = cur.fetchone()
        if user is not None:
            temp_socket.send(json.dumps({'send_type': 'register', 'result': '用户名已存在!!!', 'code': 1}).encode('utf8'))
        else:
            sh = hashlib.sha256()
            sh.update((username + password + 'cyj').encode('utf8'))
            password = sh.hexdigest()
            cur.execute('''insert into database_usersprofile(username, password, nickname, date_joined) 
            value (%s,%s,%s,%s)''', [username, password, username, send_time])
            self.db.commit()
            temp_socket.send(json.dumps({'send_type': 'register', 'result': '注册成功!!!', 'code': 0}).encode('utf8'))
        cur.close()

if __name__ == '__main__':
    server = SFChatServers()
    server.listen()


以下是全部代码:

chat.zip (10.01 KB, 下载次数: 37)

免费评分

参与人数 3吾爱币 +5 热心值 +3 收起 理由
月色凝声 + 1 + 1 谢谢@Thanks!
yike911 + 1 + 1 谢谢@Thanks!
wushaominkk + 3 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

头像被屏蔽
yike911 发表于 2019-9-19 00:38
提示: 作者被禁止或删除 内容自动屏蔽
dongbala 发表于 2019-9-19 16:14
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-16 15:41

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表