这篇文章主要记录了自己学习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;