基于python实现类似qq群聊功能

这篇文章主要记录了自己学习python时学习到的使用python来实现类似qq群聊的功能,整个项目分为服务器端和客户端两个部分,具体的实现如下:

一、具体代码

1、服务器端(Server.py)

import os
import sys
import time
import uuid
from socket import *

import pymysql

DATE_FORMAT_YYYY_MM_DD_HH_MM_SS = '%Y-%m-%d %H:%M:%S'


# 子进程处理客户端请求
def do_login(s, user, name, addr):
    # 判断用户是否存在以及用户是否为管理员
    cursor.execute("select * from user where name = '" + name + "'")
    data = cursor.fetchone()
    if (data != None and data[3] == 'on') or name == '管理员':
        s.sendto('该用户已存在,请重新输入'.encode(), tuple(list((addr[0], int(addr[1])))))
        return
    # 用户成功登陆,返回成功信息
    s.sendto('OK'.encode(), tuple(list((addr[0], int(addr[1])))))
    cursor.execute("select user.name, t.date, t.msg from (select user_id, msg, date from message where date > (select join_date from user where name = '"+name+"')) as t join user on user.id = t.user_id order by t.date")
    data_list = cursor.fetchall()
    msg = ''
    index = 0
    for data_temp in data_list:
        if index == 0:
            msg += '%s 于 %s 发言:%s' % (data_temp[0], data_temp[1], data_temp[2])
        else:
            msg += '\n%s 于 %s 发言:%s' % (data_temp[0], data_temp[1], data_temp[2])
        index += 1
    s.sendto(msg.encode(), tuple(list((addr[0], int(addr[1])))))
    msg = '\n欢迎 %s 进入聊天室' % name
    # 通知所有人
    for i in user:
        s.sendto(msg.encode(), tuple(list((i[1], int(i[2])))))
    # 将用户插入字典
    if data != None and data[3] == 'off':
        cursor.execute("update user set status = 'on', ip = '"+addr[0]+"', port = '"+addr[1]+"' where name = '" + name + "'")
    else:
        cursor.execute(
            "insert into user values('" + str(uuid.uuid1()) + "', '" + name + "', '" + get_date_str_from_format(
                DATE_FORMAT_YYYY_MM_DD_HH_MM_SS) + "', 'on', '" + addr[0] + "', " + addr[1] + ")")
    db.commit()
    return


# 处理聊天请求
def do_chat(s, user, cmd):
    # cmd = ['C','zhang','I','love','China']
    name = cmd[1]
    ori_msg = ' '.join(cmd[2:])
    msg = '\n%s 于 %s 发言:%s' % (name, get_date_str_from_format(DATE_FORMAT_YYYY_MM_DD_HH_MM_SS), ori_msg)
    # 将信息发送给出了自己的所有人
    for i in user:
        if i[0] != cmd[1]:
            s.sendto(msg.encode(), tuple(list((i[1], int(i[2])))))
    cursor.execute("select id from user where name = '" + name + "'")
    data = cursor.fetchone()
    user_id = data[0]
    cursor.execute("insert into message values('" + str(
        uuid.uuid1()) + "', '" + user_id + "', '" + ori_msg + "', '" + get_date_str_from_format(
        DATE_FORMAT_YYYY_MM_DD_HH_MM_SS + "')"))
    db.commit()
    return


# 处理用户离开聊天室请求
def do_quit(s, user, name):
    msg = '\n' + name + ' 离开了聊天室'
    for i in user:
        if i[0] != name:
            s.sendto(msg.encode(), tuple(list((i[1], int(i[2])))))
    cursor.execute("update user set status = 'off' where name = '"+name+"'")
    db.commit()
    return


# 子进程处理客户端请求
def do_child(s):
    while True:
        msg, addr_temp = s.recvfrom(1024)
        msg = msg.decode()
        cmd = msg.split(' ')

        # 字典用来存储用户信息:{name:(ip,port)}
        cursor.execute("select name, ip, port from user where status = 'on'")
        user = cursor.fetchall()

        addr = list(addr_temp)
        addr[1] = str(addr[1])

        if cmd[0] == 'L':
            do_login(s, user, cmd[1], addr)
        elif cmd[0] == 'C':
            do_chat(s, user, cmd)
        elif cmd[0] == 'Q':
            do_quit(s, user, cmd[1])
        else:
            s.sendto('请求错误'.encode(), addr_temp)


# 发送管理员消息
def do_parent(s, addr):
    while True:
        msg = input('管理员消息:')
        msg = 'C 管理员 ' + msg
        s.sendto(msg.encode(), tuple(list((addr[0], int(addr[1])))))
    s.close()
    sys.exit(0)


# 获取指定格式的日期字符串
def get_date_str_from_format(format):
    return time.strftime(format, time.localtime(time.time()))


if __name__ == '__main__':

    HOST = sys.argv[1]
    PORT = (int)(sys.argv[2])
    ADDR = (HOST, PORT)

    # 创建数据库连接
    db = pymysql.connect('ip_address', 'username', 'password', 'db_name', charset='utf8')
    # 创建游标对象
    cursor = db.cursor()

    # 使用数据报套接字
    s = socket(AF_INET, SOCK_DGRAM)
    s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    s.bind(ADDR)

    ADDR = list(ADDR)
    ADDR[1] = str(ADDR[1])

    # 将管理员加入群聊
    cursor.execute("select * from user where name = '管理员'")
    data = cursor.fetchone()
    if data == None:
        cursor.execute("insert into user values('" + str(uuid.uuid1()) + "', '管理员', '" + get_date_str_from_format(
        DATE_FORMAT_YYYY_MM_DD_HH_MM_SS) + "', 'on', '" + ADDR[0] + "', " + ADDR[1] + ")")
    db.commit()

# 创建子进程
pid1 = os.fork()
if pid1 < 0:
    print('创建一级子进程失败')
elif pid1 == 0:
    # 创建二级子进程
    pid2 = os.fork()
    if pid2 < 0:
        print('创建二级子进程失败')
    elif pid2 == 0:
        do_child(s)
    else:
        # 一级子进程退出,使二级子进程成为孤儿
        os._exit(0)
else:
    # 等待一级子进程退出
    os.wait()
    do_parent(s, ADDR)

# 释放资源
cursor.close()
db.close()

2、客户端(Client.py)

import os
import signal
import sys
from socket import *


# 子进程发送信息
def do_child(s, name, addr):
    while True:
        text = input(name + ' 发言(输入quit退出):')
        # 用户退出
        if text == 'quit':
            msg = 'Q ' + name
            s.sendto(msg.encode(), addr)
            os.kill(os.getppid(), signal.SIGKILL)
            sys.exit('退出聊天室')
        # 正常聊天
        else:
            msg = 'C %s %s'%(name, text)
            s.sendto(msg.encode(), addr)

# 父进程接收消息
def do_parent(s, name):
    while True:
        msg, addr = s.recvfrom(1024)
        print(msg.decode() + '\n' + name + ' 发言(输入quit退出):', end="")


if __name__ == '__main__':

    HOST = sys.argv[1]
    PORT = (int)(sys.argv[2])
    ADDR = (HOST, PORT)

    # 使用数据报套接字
    s = socket(AF_INET, SOCK_DGRAM)

    while True:
        name = input('请输入姓名:')
        msg = 'L ' + name
        s.sendto(msg.encode(), ADDR)
        data, addr = s.recvfrom(1024)
        if data.decode() == 'OK':
            data, addr = s.recvfrom(1024)
            if data.decode() != '':
                print(data.decode())
            break
        else:
            print(data.decode())

    pid = os.fork()
    if pid < 0:
        print('创建子进程失败')
    elif pid == 0:
        do_child(s, name, ADDR)
    else:
        do_parent(s, name)

二、使用方法:

1、服务端:

服务端需要通过脚本传入两个参数,一个是服务器的ip地址,另一个是服务器端的端口号

python3 Server.py 127.0.0.1 8888

2、客户端:

客户端和服务端一样,同样需要通过脚本传入两个参数,一个是服务器的ip地址,另一个是服务器端的端口号

python3 Client.py 127.0.0.1 8888

3、数据库脚本:

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 80015
 Source Host           : localhost:3306
 Source Schema         : chatroom

 Target Server Type    : MySQL
 Target Server Version : 80015
 File Encoding         : 65001

 Date: 02/03/2019 18:01:11
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for message
-- ----------------------------
DROP TABLE IF EXISTS `message`;
CREATE TABLE `message` (
  `id` varchar(255) COLLATE utf8_bin NOT NULL,
  `user_id` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `msg` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `date` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

-- ----------------------------
-- Records of message
-- ----------------------------
BEGIN;
INSERT INTO `message` VALUES ('06989a8a-3cce-11e9-8b89-d0a637ea48d3', '561b4638-3cca-11e9-9ae6-d0a637ea48d3', 'hi', '2019-03-02 17:31:58');
INSERT INTO `message` VALUES ('06f25f28-3cd0-11e9-8d6a-d0a637ea48d3', '94a3ccf2-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:46:17');
INSERT INTO `message` VALUES ('0b78ca64-3cd0-11e9-a12f-d0a637ea48d3', '561b4638-3cca-11e9-9ae6-d0a637ea48d3', 'hi', '2019-03-02 17:46:25');
INSERT INTO `message` VALUES ('0c0258d2-3ccc-11e9-99b8-d0a637ea48d3', '9070330a-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:17:48');
INSERT INTO `message` VALUES ('0cd50198-3cd0-11e9-a12f-d0a637ea48d3', 'b6989400-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:46:27');
INSERT INTO `message` VALUES ('0d52c082-3ccc-11e9-99b8-d0a637ea48d3', '9070330a-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:17:50');
INSERT INTO `message` VALUES ('0f1c73be-3cd0-11e9-8d6a-d0a637ea48d3', '9070330a-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:46:31');
INSERT INTO `message` VALUES ('108d953e-3cd0-11e9-a12f-d0a637ea48d3', '9070330a-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:46:34');
INSERT INTO `message` VALUES ('230fb1e4-3cce-11e9-8b89-d0a637ea48d3', '29665212-3ccb-11e9-b7e5-d0a637ea48d3', 'hi', '2019-03-02 17:32:46');
INSERT INTO `message` VALUES ('255f3faa-3cce-11e9-8b89-d0a637ea48d3', '29665212-3ccb-11e9-b7e5-d0a637ea48d3', 'hi', '2019-03-02 17:32:49');
INSERT INTO `message` VALUES ('443ff486-3ccf-11e9-807a-d0a637ea48d3', '92afe2b4-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:40:51');
INSERT INTO `message` VALUES ('46c77ecc-3ccf-11e9-807a-d0a637ea48d3', '94a3ccf2-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:40:55');
INSERT INTO `message` VALUES ('4994ced4-3ccf-11e9-807a-d0a637ea48d3', '9070330a-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:41:00');
INSERT INTO `message` VALUES ('6bdee1c8-3cca-11e9-9ae6-d0a637ea48d3', '561b4638-3cca-11e9-9ae6-d0a637ea48d3', 'hi', '2019-03-02 17:06:10');
INSERT INTO `message` VALUES ('8ec4dcf4-3ccc-11e9-99d0-d0a637ea48d3', '94a3ccf2-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:21:27');
INSERT INTO `message` VALUES ('9ecdc07a-3c18-11e9-9106-d0a637ea48d3', '92afe2b4-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-01 19:53:25');
INSERT INTO `message` VALUES ('a13f6cfa-3c18-11e9-9106-d0a637ea48d3', '92afe2b4-3c18-11e9-9106-d0a637ea48d3', '你好', '2019-03-01 19:53:29');
INSERT INTO `message` VALUES ('a86a838e-3c18-11e9-9106-d0a637ea48d3', '94a3ccf2-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-01 19:53:41');
INSERT INTO `message` VALUES ('b6817c2e-3ccd-11e9-8b89-d0a637ea48d3', '94a3ccf2-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:29:43');
INSERT INTO `message` VALUES ('c1867d0a-3c18-11e9-9106-d0a637ea48d3', 'b6989400-3c18-11e9-9106-d0a637ea48d3', '大家好,我是d', '2019-03-01 19:54:23');
INSERT INTO `message` VALUES ('d4facb34-3c18-11e9-9106-d0a637ea48d3', '9070330a-3c18-11e9-9106-d0a637ea48d3', '大家好,我是管理员', '2019-03-01 19:54:56');
INSERT INTO `message` VALUES ('e18db12c-3ccc-11e9-a4d5-d0a637ea48d3', '92afe2b4-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:23:46');
INSERT INTO `message` VALUES ('e3915bea-3ccc-11e9-a4d5-d0a637ea48d3', '94a3ccf2-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:23:50');
INSERT INTO `message` VALUES ('e71748e8-3ccb-11e9-99b8-d0a637ea48d3', 'dfe2a4fa-3ccb-11e9-99b8-d0a637ea48d3', 'hi', '2019-03-02 17:16:46');
INSERT INTO `message` VALUES ('e8ef9242-3ccb-11e9-99b8-d0a637ea48d3', 'dfe2a4fa-3ccb-11e9-99b8-d0a637ea48d3', 'hi', '2019-03-02 17:16:49');
INSERT INTO `message` VALUES ('f3cfad14-3ccb-11e9-99b8-d0a637ea48d3', '92afe2b4-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:17:07');
INSERT INTO `message` VALUES ('fe507e86-3ccf-11e9-8d6a-d0a637ea48d3', '92afe2b4-3c18-11e9-9106-d0a637ea48d3', 'hi', '2019-03-02 17:46:03');
COMMIT;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
  `join_date` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
  `status` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `ip` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `port` int(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

-- ----------------------------
-- Records of user
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES ('29665212-3ccb-11e9-b7e5-d0a637ea48d3', 'e', '2019-03-02 17:11:28', 'off', '127.0.0.1', 62199);
INSERT INTO `user` VALUES ('561b4638-3cca-11e9-9ae6-d0a637ea48d3', 'c', '2019-03-02 17:05:33', 'off', '127.0.0.1', 65085);
INSERT INTO `user` VALUES ('6aee25d4-3ccb-11e9-a6e1-d0a637ea48d3', 'f', '2019-03-02 17:13:18', 'off', '127.0.0.1', 49177);
INSERT INTO `user` VALUES ('9070330a-3c18-11e9-9106-d0a637ea48d3', '管理员', '2019-03-01 19:53:01', 'off', '127.0.0.1', 8888);
INSERT INTO `user` VALUES ('92afe2b4-3c18-11e9-9106-d0a637ea48d3', 'a', '2019-03-01 19:53:04', 'off', '127.0.0.1', 65277);
INSERT INTO `user` VALUES ('94a3ccf2-3c18-11e9-9106-d0a637ea48d3', 'b', '2019-03-01 19:53:08', 'off', '127.0.0.1', 60116);
INSERT INTO `user` VALUES ('b6989400-3c18-11e9-9106-d0a637ea48d3', 'd', '2019-03-01 19:54:05', 'off', '127.0.0.1', 52335);
INSERT INTO `user` VALUES ('dfe2a4fa-3ccb-11e9-99b8-d0a637ea48d3', 'g', '2019-03-02 17:16:34', 'off', '127.0.0.1', 61773);
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

 

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

GraysonWP

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

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

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

打赏作者

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

抵扣说明:

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

余额充值