好友
阅读权限 10
听众
最后登录 1970-1-1
本帖最后由 袅袅系秋风 于 2019-9-7 20:16 编辑
程序目前尚未完全完成,勿喷
如果有错的或者有更简单的方法请留下言,共同学习哈
数据库使用的是mysql、用户为root、密码123456
数据库结构
create database database1 ;
use database1;
create table user_info(
nickname varchar(20),
id varchar(20) not null,
pwd varchar(20)
);
insert into user_info(nickname,id,pwd)value('用户1','123456789','123456789');
insert into user_info(nickname,id,pwd)value('用户2','987654321','987654321');
程序执行顺序
1、首先运行s.py
2、其次在终端运行“界面.py”登陆账户1(123456789为用户1)
3、其次在另一个终端运行“界面.py”登陆账户2(987654321为用户2)
4、下图是登陆后界面(和谁聊天点谁,目前只能用户1和用户2聊天,如果没有查到用户则将消息保存到消息队列,等到有该用户连接服务器再发送)
5、下方是聊天界面截图
服务端代码
文件名: s.py
[Python] 纯文本查看 复制代码
# -*- coding: UTF-8 -*-
import socket
from queue import Queue
from time import sleep
import threading
class service:
def __init__(self):
self.sendMsgQueue = Queue()
self.recvMsgQueue = Queue()
self.connect_info=[]
self.accept_connect=[]
self.s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.host='127.0.0.1'
self.port=2333
def accept_request(self):
self.s.bind((self.host, self.port))
self.s.listen(50)
while True:
print('正在等待连接。。。')
conn = self.s.accept()
nickname=conn[0].recv(1024).decode()#连接成功先将用户昵称接收
#print('nick:',nickname)
self.accept_connect.append(conn)
self.accept_connect.append(nickname)
print(self.accept_connect)
#self.connect_info.append('connectinfo'+'/'+str(conn[1][0])+'/'+str(conn[1][1])+'/'+nickname)
#print(self.connect_info)
#格式:
# connectinfo/192.168.29.1/4318/袅袅兮秋风
for info in self.connect_info:#发送当前连接服务气的所有ip 端口 昵称
conn[0].send(info.encode())
#print(conn[0])
#print(conn[1])
print('连接成功')
def sendMsg(self):
while True:
if self.sendMsgQueue.empty():
#print('发送消息队列为空!!!')
sleep(1)
else:
#print('大小:', self.sendMsgQueue.qsize())
#print('555555555555555',len(self.accept_connect))
flag=False
length=int(len(self.accept_connect)/2)
#print('length',length)
msg=self.sendMsgQueue.get()
for i in range(0,int(len(self.accept_connect)/2)):
#print(self.accept_connect)
#print('等待发送。。。',i)
name1=self.accept_connect[int(i*2+1)]
name2=msg.split('/')[2]
if name1==name2:
#print('找到收件人',i)
self.accept_connect[int(i*2)][0].send(msg.encode())
#print('转发成功?',i)
flag=True
break
if flag==False:
print('未找到该用户!!!')
self.sendMsgQueue.put(msg)
sleep(1)
def add_recvmsg_to_queue(self,conn):
print('++++',conn)
thread = [0]*100
flag=[0]*100
for j in range(0,100):
flag[j]=True
while True:
i = 0
#print('cccconn:',conn)
for k in range(0,len(self.accept_connect)):
if i%2!=0:
i=i+1
continue
#print('conn:',c['conn'])
if flag[int(i/2)]==True:
#print('鹅鹅鹅鹅鹅鹅饿')
#print(self.accept_connect)
thread[int(i/2)]=threading.Thread(target=self.recvthread,args=(self.accept_connect[i],))
thread[int(i/2)].start()
flag[int(i/2)] =False
if not thread[int(i/2)].isAlive():
flag[int(i/2)]=True
'''recvmsg = c[0].recv(1024).decode()
print('调试:',i)
if recvmsg.split('/')[0]=='message':#只接受message类型消息到接收队列
self.recvMsgQueue.put(recvmsg)
self.sendMsgQueue.put(recvmsg)'''
i=i+1
#print('recv队列大小:',self.recvMsgQueue.qsize())
def recvthread(self,c):
recvmsg = c[0].recv(1024).decode()
if recvmsg.split('/')[0] == 'message': # 只接受message类型消息到接收队列
self.recvMsgQueue.put(recvmsg)
self.sendMsgQueue.put(recvmsg)
elif recvmsg.split('/')[0] == 'closeconnect':#断开连接,从self.connect中删除此连接
for i in range(0,len(self.accept_connect)):
if self.accept_connect[i][0]==c[0]:
a=self.accept_connect.pop(i)
b=self.accept_connect.pop(i)
print(a,b)
print(i,self.accept_connect)
print('删除成功')
break
def recvMsg(self):
print('--------')
while True:
if self.recvMsgQueue.empty():
sleep(1)
else:
#print('接受队列大小:',self.recvMsgQueue.qsize())
msg=self.recvMsgQueue.get().split('/')
#print(msg)
print(msg[0]+'\n'+msg[1]+'\t'+msg[2]+'\n'+msg[3]+':\t'+msg[4])
def run(self):
#接受连接线程
acceptrequestthread=threading.Thread(target=self.accept_request)
# 接收消息线程
recvmsgthread = threading.Thread(target=self.recvMsg)
# 添加消息到发送消息队列
addmsgthread = threading.Thread(target=self.add_recvmsg_to_queue, args=(self.accept_connect,))
# 发送消息线程
sendmsgthread = threading.Thread(target=self.sendMsg)
acceptrequestthread.start()
recvmsgthread.start()
sendmsgthread.start()
addmsgthread.start()
if __name__ == '__main__':
client1=service()
client1.run()
客户端代码
文件名 :c.py
[Python] 纯文本查看 复制代码
import socket
import user
import time
from queue import Queue
import threading
import sys
from tkinter import END
class client:
def __init__(self,myuser,friendname):
self.myuser=myuser
self.friendname = friendname
self.sendMsgQueue=Queue()
self.recvMsgQueue=Queue()
self.s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.host='127.0.0.1'
self.port=2333
def connect_service(self):
self.s.connect((self.host,self.port))
self.s.send(self.myuser.getnickname().encode())#连接成功后将连接者昵称发送至服务器
#print(self.s.recv(1024).decode())
def add_sendmsg_to_queue(self,msg,senduser,recvuser):
message=msg
if not message:
print('消息不能为空')
return -1
t = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
msg = 'message'+'/'+ senduser.getnickname() +'/'+recvuser+ '/' + t + '/' + message
print(msg)
self.sendMsgQueue.put(msg)
print(self.sendMsgQueue.qsize())
#消息格式:
# message/192.168.1.119/testname/2019-08-12 15:25:37/都看过就欧弟郭德纲
def sendMsg(self):
while True:
if self.sendMsgQueue.empty():
time.sleep(1)
else:
#print('大小:',self.sendMsgQueue.qsize())
self.s.send(self.sendMsgQueue.get().encode())
def add_recvmsg_to_queue(self):
print('++++')
while True:
#print('conn:',c['conn'])
recvmsg = self.s.recv(1024).decode()
if recvmsg.split('/')[0]=='message':#只接受message类型消息到接收队列
self.recvMsgQueue.put(recvmsg)
#print('recv队列大小:',self.recvMsgQueue.qsize())
def recvMsg(self):
#while True:
if self.recvMsgQueue.empty():
print('接受队列为空hhhh')
return -1
else:
# print('接受队列大小:',self.recvMsgQueue.qsize())
msg = self.recvMsgQueue.get().split('/')
# print(msg)
#print(msg[0] + '\n' + msg[1] + '\t' + msg[2] + '\n' + msg[3] + ':\t' + msg[4])
#message=msg[0] + '\n' + msg[1] + '\t' + msg[2] + '\n' + msg[3] + ':\t' + msg[4]
return msg
def close_connect(self):#断开socket连接
msg='closeconnect/'
self.s.send(msg.encode())
self.s.close()
print('断开连接,程序已退出!!!')
#sys.exit()
def connectandaddmsgtoqueue(self):
self.connect_service()
# 添加消息到接收消息队列
addrecvmsgthread = threading.Thread(target=self.add_recvmsg_to_queue)
addrecvmsgthread.start()
# 发送消息线程
sendmsgthread = threading.Thread(target=self.sendMsg)
sendmsgthread.start()
def run(self):
pass
#nickname = '用户1'
#self.usertest = user.user(nickname)
#self.connect_service()
#发送消息线程
#sendmsgthread=threading.Thread(target=self.sendMsg)
#sendmsgthread.start()
# 接收消息线程
#recvmsgthread = threading.Thread(target=self.recvMsg)
#recvmsgthread.start()
#添加消息到发送消息队列
#addmsgthread = threading.Thread(target=self.add_sendmsg_to_queue,args=(self.chattingtouser,))
#addmsgthread.start()
# 添加消息到接收消息队列
#addrecvmsgthread = threading.Thread(target=self.add_recvmsg_to_queue)
#addrecvmsgthread.start()
#time.sleep(10)
#self.close_connect()
程序界面(客户端只需要执行这个)
文件名:界面.py
[Python] 纯文本查看 复制代码
from tkinter import Tk,Canvas,Button,Entry,Checkbutton,Label,PhotoImage,NW,END,scrolledtext,BooleanVar,StringVar,messagebox
from PIL import Image,ImageTk
from threading import Thread
from time import strftime,sleep,time,localtime
import pymysql
import c
import user
#登陆类
class LoginPanel:
def __init__(self):
#图形界面区域
self.root = Tk()
self.root.title('登陆0.0.1')
self.root.geometry('450x350+400+200')
#头像区域
self.head = Canvas(self.root)
#self.head.create_oval(165, 15, 265, 115)
self.head.place(x=5, y=5, heigh=120, width=440)
#输入区域
self.input = Canvas(self.root, bg='#ffffff')
self.input.place(x=5, y=130, heigh=220, width=440)
#登陆按钮
self.loginbutton=Button(self.input,text='登陆',bg='#4fcffd',command=self.loginbuttonclickevent)
self.loginbutton.place(x=100,y=160,heigh=40, width=240)
#账号输入框
self.accountinput=Entry(self.input,font=("仿宋", 16, "bold"))
self.accountinput.place(x=130,y=60,heigh=30,width=210,)
#密码输入框
self.passwordinput = Entry(self.input,font=("仿宋", 16, "bold"),show='*')
self.passwordinput.place(x=130, y=95, heigh=30, width=210)
#自动登录checkbutton
self.autologinvar=BooleanVar()
self.autologincheck=Checkbutton(self.input,text='自动登录',variable=self.autologinvar,command=self.autologincheckbuttonstatusevent)
self.autologincheck.place(x=100, y=130, heigh=15, width=80)
#记住密码
self.remberpasswordvar=BooleanVar()
self.remberpasswordcheck = Checkbutton(self.input, text='记住密码',variable=self.remberpasswordvar,command=self.remberpasswordcheckbuttonstatusevent)
self.remberpasswordcheck.place(x=190, y=130, heigh=15, width=80)
#找回密码
self.findpasswordbutton=Button(self.input,text='找回密码',bg='white')
self.findpasswordbutton.place(x=290, y=130, heigh=15, width=50)
#注册账号
self.registerbutton=Button(self.input,text='注册账号',bg='white',command=self.registerbuttonclickevent)
self.registerbutton.place(x=10, y=190, heigh=15, width=50)
#准备工作
#1、获取复选框状态(自动登录,记住密码)
#2、如果自动登录已勾选,获取最近一次登录账号及对应密码输入框并直接登录,摧毁当前窗口,打开主界面窗口,
#3、如果仅勾选记住密码,则仅获取最近一次登录账号及对应密码输入框,等待用户操作
#对应事件监听区域
#登录按钮点击事件
def loginbuttonclickevent(self):
account = self.accountinput.get().strip().replace(' ', '')
self.accountinput.delete(0, END)
self.accountinput.insert(END, account)
print(account)
password = self.passwordinput.get().strip().replace(' ', '')
self.passwordinput.delete(0, END)
self.passwordinput.insert(END, password)
print(password)
if len(account) < 8 or len(password) < 8 or not account.isdigit():
messagebox.showinfo('登录失败', '查无此号')
return -1
for c in password:
if ord(c) > 255:
messagebox.showinfo('登录失败', '密码错误\n( ⊙ o ⊙ )')
return -2
print('等待连接数据库。。。')
db = pymysql.connect('localhost', 'root', '123456', 'database1')
cursor = db.cursor()
print('连接成功')
sql = "select pwd,nickname from user_info where id=%s"%account
# print('sql',sql)
# cursor.execute("select * from user_info")
try:
cursor.execute(sql)
results=cursor.fetchall()[0]
print('results',results)
if results[0]==password:
messagebox.showinfo('登录成功','登录成功')
db.close()
self.root.destroy()
mainpanel=MainPanel(results[1])
mainpanel.run()
return 0
messagebox.showinfo('登录失败','账号密码不匹配')
except:
print('登录抛出异常')
db.rollback()
db.close()
return -3
#登录操作
#登录成功,自动保存账号,并根据记住密码复选框状态决定是否保存密码
#自动登录勾选事件
def autologincheckbuttonstatusevent(self):
print('自动登录状态:',self.autologinvar.get())
#如果为自动登录状态,则记住密码为勾选状态
if self.autologinvar.get()==True:
self.remberpasswordvar.set(True)
#记住密码勾选事件
def remberpasswordcheckbuttonstatusevent(self):
print('记住密码状态:',self.remberpasswordvar.get())
#注册账号按钮点击事件(未完成)
def registerbuttonclickevent(self):
register = RegisterPanel()
register.run()
#找回密码按钮点击事件(未完成)
def findpasswordbuttonclickevent(self):
pass
def run(self):
self.root.mainloop()
pass
class MainPanel:
def __init__(self,loginnickname):
self.myuser=user.user(loginnickname)
self.root=Tk()
self.root.title('聊天主界面0.0.1')
self.root.geometry('350x600+400+50')
#个人信息区域
self.head=Canvas(self.root,bg='orange')
self.head.create_oval(30,30,80,80)
self.head.place(x=5,y=5,heigh=120,width=340)
#好友区域
self.frend = Canvas(self.root, bg='pink')
self.frend.place(x=5, y=130, heigh=420, width=340)
#设置区域
self.setting = Canvas(self.root, bg='yellow')
self.setting.place(x=5, y=555, heigh=40, width=340)
#好友栏
self.frendstext=scrolledtext.ScrolledText(self.frend)
self.frendstext.place(x=5,y=5,heigh=410,width=330)
#自己的昵称名标签
self.myselfnicknamelable=Label(self.head,text=self.myuser.getnickname())
self.myselfnicknamelable.place(x=100,y=30)
friendnum=10
for i in range(friendnum):
Thread(target=self.friendbuttonthread,args=('用户'+str(i+1),)).start()
#绑定对应好友事件
def friendbuttonthread(self,friendname):
friendbutton=Button(self.frendstext,text=friendname,bg='#ecf3d8',heigh=2,width=44,command=lambda:self.friendsbuttonclickevent(friendname))
self.frendstext.window_create(END, window=friendbutton)
#监听事件
#好友按钮点击事件
def friendsbuttonclickevent(self,friendname):
print(friendname)
self.client=c.client(self.myuser,friendname)
#Thread(target=ChatPanel(self.myuser,friendname,self.client).run).start()
print('66666666666666')
Thread(target=self.client.connectandaddmsgtoqueue).start()
print('77777777777777')
ChatPanel(self.myuser, friendname, self.client).run()
print('88888888888888')
#连接数据库,获取好友列表
#每个用户建立一个数据库
#好友信息table 昵称 账号 其他(待补充)
def getmyfriendsinfo(self):
pass
def run(self):
self.root.mainloop()
class ChatPanel:
def __init__(self,myuser,frienduser,client):
self.client=client
self.root = Tk()
self.frienduser=frienduser#暂时未用户名, 不是user对像!!!!!!!
self.myuser = myuser
self.root.title('登录用户: '+self.myuser.getnickname())
self.root.geometry('700x600+400+50')
#标题
self.headtitle = Canvas(self.root, bg='yellow')
self.headtitle.place(x=5, y=5, heigh=40, width=690)
#好友名字标签
titlenamelable=Label(self.headtitle,text=self.frienduser)
titlenamelable.place(x=200,y=5, heigh=30, width=300)
#聊天信息
self.chattext = Canvas(self.root, bg='orange')
self.chattext.place(x=5, y=45, heigh=400, width=490)
#输入区域
self.input = Canvas(self.root, bg='pink')
self.input.place(x=5, y=445, heigh=150, width=490)
#好友信息区域
self.info = Canvas(self.root, bg='#d9ffb3')
self.info.place(x=495, y=45, heigh=550, width=200)
#发送按钮
closebutton=Button(self.input,text='发送',command=self.sendbuttonclickevent)
closebutton.place(x=400, y=110, heigh=30, width=80)
#关闭按钮
closebutton=Button(self.input,text='关闭',command=self.closebuttonclickevent)
closebutton.place(x=310, y=110, heigh=30, width=80)
#聊天信息框
self.chattextvar=StringVar
self.chatscrolltext=scrolledtext.ScrolledText(self.chattext,font=("仿宋", 16, "normal"))
self.chatscrolltext.place(x=5,y=5,heigh=390,width=480)
# 信息输入框
self.inputchattext = scrolledtext.ScrolledText(self.input, font=("仿宋", 16, "normal"))
self.inputchattext.place(x=5, y=5, heigh=100, width=480)
#绑定事件区域
def sendbuttonclickevent(self):
#获取输入信息
t = strftime("%Y-%m-%d %H:%M:%S", localtime(time()))
msg=self.inputchattext.get('0.0',END)
self.inputchattext.delete('0.0',END)
print(msg)
#将消息加入发送消息队列
self.client.add_sendmsg_to_queue(msg,self.myuser,self.frienduser)
#将输入信息追加到聊天记录中
self.chatscrolltext.insert(END,t+'\n'+self.myuser.getnickname()+'\t:'+msg)
print('发送按钮被点击了')
def closebuttonclickevent(self):
print('关闭按钮被点击了')
self.closewindow()
def closewindow(self):
self.client.close_connect()
self.root.destroy()
def recvthread(self):
while True:
msg=self.client.recvMsg()
if msg==-1:
sleep(1)
continue
print('mmmsg',msg)
self.chatscrolltext.insert(END, msg[3] + '\n' + msg[1] + '\t:' + msg[4])
print('插入成功')
def run(self):
# 接收消息线程
#Thread(target=self.client.recvMsg,args=(self.chatscrolltext,)).start()
#self.root.mainloop()
Thread(target=self.recvthread).start()
print('ssssssssssssssssssssss')
self.root.mainloop()
#Thread(target=self.root.mainloop).start()
class RegisterPanel:
def __init__(self):
self.root = Tk()
self.root.title('注册0.0.1')
self.root.geometry('450x350+400+200')
# 输入区域
self.input = Canvas(self.root, bg='#ffffff')
self.input.place(x=5, y=130, heigh=220, width=440)
# 注册按钮
self.loginbutton = Button(self.input, text='立即注册', bg='#4fcffd', command=self.registerbuttonclickevent)
self.loginbutton.place(x=100, y=160, heigh=40, width=240)
# 昵称输入框
self.nicknameinput = Entry(self.input, font=("仿宋", 16, "bold"))
self.nicknameinput.place(x=130, y=20, heigh=30, width=210, )
# 账号输入框
self.accountinput = Entry(self.input, font=("仿宋", 16, "bold"))
self.accountinput.place(x=130, y=60, heigh=30, width=210, )
# 密码输入框
self.passwordinput = Entry(self.input, font=("仿宋", 16, "bold"), show='*')
self.passwordinput.place(x=130, y=95, heigh=30, width=210)
def registerbuttonclickevent(self):
nickname=self.nicknameinput.get().strip().replace(' ','')
self.nicknameinput.delete(0, END)
self.nicknameinput.insert(END, nickname)
print(nickname)
account=self.accountinput.get().strip().replace(' ','')
self.accountinput.delete(0,END)
self.accountinput.insert(END,account)
print(account)
password=self.passwordinput.get().strip().replace(' ','')
self.passwordinput.delete(0,END)
self.passwordinput.insert(END,password)
print(password)
if len(account)<8 or len(password)<8 :
messagebox.showinfo('注册失败','账号或密码过短\no(︶︿︶)o')
return -1
if not account.isdigit():
messagebox.showinfo('注册失败','账号必须全为数字\n(╯﹏╰)')
return -2
for c in password:
if ord(c)>255:
messagebox.showinfo('注册失败','密码不能包含非法字符\n( ⊙ o ⊙ )')
return -3
print('等待连接数据库。。。')
db=pymysql.connect('localhost','root','123456','database1')
cursor=db.cursor()
print('连接成功')
sql="insert into user_info(id,nickname,pwd)value('%s','%s','%s')"%(account,nickname,password)
#print('sql',sql)
#cursor.execute("select * from user_info")
try:
cursor.execute(sql)
db.commit()
except:
db.rollback()
db.close()
messagebox.showinfo('注册成功','恭喜您 注册成功\n~\(≧▽≦)/~')
return 0
def run(self):
self.root.mainloop()
if __name__ == '__main__':
loginpanel = LoginPanel()
loginpanel.run()
#mainpanel=MainPanel()
#mainpanel.run()
#chatpanel=ChatPanel()
#chatpanel.run()
#register=RegisterPanel()
#register.run()
存储用户信息的类
文件名:user.py
[Python] 纯文本查看 复制代码
import socket
class user:
def __init__(self,nickname='null'):
self.ip=self.get_host_ip()
self.nickname=nickname#自己的昵称
self.friends=[]#存储好友信息
#self.friends=[0]*100
def add_friend(self):
print(self.ip,self.nickname)
def get_host_ip(self):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 80))
self.ip = s.getsockname()[0]
finally:
s.close()
return self.ip
def getip(self):
return self.ip
def getnickname(self):
return self.nickname
想省事的可以直接从这里下载哦
代码.rar
(9.22 KB, 下载次数: 30)
免费评分
查看全部评分