说明
服务器端
-
用户注册信息存储在数据库user_informance中,创建的字段和表在源代码注释中
-
用户登录后,会将用户及其ip_addr和端口号记录在数据库address_informance中,数据类型详见注释
-
首次运行前记得在目录下使用sqlite3创建上面提到的俩个数据库
客户端
- 每次运行客户端端口是随机的,所以登录后,退出一定要输入exit,让服务器端删除登录地址信息,避免不必要的麻烦
- 公聊只需要在对话框中输入就好了
- 私聊某用户的方法是:在对话框中输入
private+空格+对象用户名+空格+内容
- 退出输入: exit
特别注意
- 只在windows下运行,没来及在linux中,linux自测可行性
- 若需要将客户端和服务器端放在不同主机(ip)上请修改客服端的发送ip地址参数,以及检查list_name,list_port这几个根据他值索引问题,同时得连接远程数据库,sqlite3此时不能满足条件
- 本人菜鸡,没有使用class类(
感觉功能不够怎么强大,关键是我不会用啊哈哈哈)有问题欢迎留言
1.客户端源代码:
import socket
import multiprocessing
import random
def main():
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)#调用套接字,数据类型为UDP
my_port=random.randint(1024,10000)
print('your port:',my_port)
my_address=('127.0.0.1',my_port)
sock.bind(my_address)
server_address=('127.0.0.1',8888)
chat(server_address,sock)
def chat(server_addr,sock):#用户聊天功能函数,包括注册,登录,发送消息,消息接收功能
try:
num=input('登录请输入数字1,注册请输入数字2:\n')
if num=='1':
login(server_addr,sock)
elif num=='2':
register(server_addr,sock)
else:
print('选择出错')
p = multiprocessing.Process(target=recvmsg, args=(sock,))#类似于linux下的os.fork函数创建子进程
p.daemon = True
p.start()
sendmsg(server_addr,sock)
except KeyboardInterrupt:
sock.close()
def sendmsg(client,sock):#发送消息函数,若需要私聊则需要使用以下格式:private+空格+user_name+空格+内容
while True:
data=input('您:\n')
sock.sendto(data.encode(),client)
def recvmsg(sock):
while True:
data,addr=sock.recvfrom(1024)
data_text=data.decode('utf-8')
print(data_text)
def register(server_addr,sock):#用户注册输入,并将信息提交给服务器,注册成功需要重启程序进行登录
user_name = input('请输入您的账户名\n') # 输入聊天信息
user_pwd =input('请输您的密码\n')
user_email=input('请输入您的邮箱\n')
send_data='register'+' '+user_name+' '+user_pwd+' '+user_email
sock.sendto(send_data.encode(), server_addr) # 网络中以字节传输
def login(server_addr,sock):#输入用户信息,并将信息发送给服务端
user_name = input('请输入您的账户名\n') # 输入账户信息
user_pwd = input('请输您的密码\n')
send_data = 'login' + ' ' + user_name + ' ' + user_pwd
sock.sendto(send_data.encode(), server_addr)
if __name__=='__main__':
main()
2.服务器端
import socket
import hashlib
import sqlite3
#database_name=user_informance.db table_name=USER :username text primary key not null,password text not null,mail text
#database_name=address_informance.db table_name=ADDR ADDR(name text primary key not null,address text not null)CREATE TABLE ADDR(name text primary key not null,address text not null,port integer not null
def register(username1,passwd,mail,sock,msg_addr):#接收用户端提交的数据进行注册
conn=sqlite3.connect('user_informance.db')#数据放在旗下的表USER里面
c=conn.cursor()
check_name=c.execute('select username from USER ')
flag=0#flag=0可正常注册
for row in check_name:
k = row[0]
if username1==k:
flag=1
break
if flag==1 :
err_data='用户名已存在请关掉程序重新注册!!!'
sock.sendto(err_data.encode(),msg_addr)
else:
pwd = hashlib.md5(passwd.encode())#密码以md5加密方式存储
passwd = pwd.hexdigest()
c.execute('INSERT INTO USER (username,password,mail) VALUES (?,?,?)',(username1,passwd,mail))#usernam text/passwd text/mail text
data_text='用户注册成功,请登录进行会话'
sock.sendto(data_text.encode(),msg_addr)
conn.commit()
c.close()
def login_check(username,passwd,sock,client_addr,list_addr,list_name,list_port):#服务器端将用户登录的用户名,地址,端口存进address_informance.db中,三个list分别是查询数据库得到的用户名,地址,端口
conn = sqlite3.connect('user_informance.db') # 数据放在旗下的表USER里面
c = conn.cursor()
pwd = hashlib.md5(passwd.encode())
passwd = pwd.hexdigest()
pd=c.execute('SELECT password FROM USER WHERE USER.username= (?)',(username,))
pd_ckeck=pd.fetchone()
if passwd==pd_ckeck[0]:
login_text='登陆成功,您可以开始对话了'
sock.sendto(login_text.encode(),client_addr)
coonn = sqlite3.connect('address_informance.db')
coo=coonn.cursor()
sql_text='''INSERT OR IGNORE INTO ADDR (name,address,port) values(?,?,?)'''
print(username,'login',client_addr)
coo.execute(sql_text,(username,client_addr[0],client_addr[1]))#将用户名、地址、端口存入表ADDR中
coonn.commit()
coonn.close()
else:
err_text='用户名或者密码错误请重新输入'
sock.sendto(err_text.encode(),client_addr)
conn.commit()
c.close()
def doQuit(sock,name,list_addr,list_name,list_port):#用户退出,服务器将存在数据库中的登录的地址和端口信息删除
message = '\n%s 退出了聊天室' %name
for u in list_name:
if u!=name:
private_add = list_addr[list_name.index(u)]
private_port= list_port[list_name.index(u)]
sock.sendto(message.encode(),(private_add,private_port))
else:
sock.sendto('exit'.encode(),(list_addr[list_name.index(u)],list_port[list_name.index(u)]))
ccoonn = sqlite3.connect('address_informance.db')
ccoo = ccoonn.cursor()
sql_text = '''DELETE FROM ADDR WHERE name=?'''
ccoo.execute(sql_text, (name,))#删除登录地址信息
ccoonn.commit()
ccoonn.close()
def main():
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
address = (('127.0.0.1', 8888))
sock.bind(address)
# coon=sqlite3.connect('address_informance.db')
# co=coon.cursor()
# userlist = {'admin': '(127.0.0.1,8888)',}
getserver(sock)
def getserver(sock):#服务器中转功能,包括注册,登录、接收用户会话并转发给私人或者是全部在线用户、用户退出等功能
while True:
coon = sqlite3.connect('address_informance.db')
msg,client_addr=sock.recvfrom(1024)
msglist = msg.decode().split(' ') # 拆分数据,以空格为分隔
co=coon.cursor()
sql1='''select * from ADDR'''
list_name=list()
list_addr=list()
list_port=list()
k=co.execute(sql1)
coon.commit()
for row in k:
list_name.append(row[0])
list_addr.append(row[1])
list_port.append(row[2])
co.close()
#三个list表,查找出所有已记录的在线用户的信息包括用户名,地址,端口
if (client_addr[1] in list_port)==True:#收到消息的发送端是已登录的用户
msg_user = list_name[list_port.index(client_addr[1])]
print(msg_user + ':' + msg.decode('utf-8'))
if msglist[0]=='login' :
login_check(msglist[1],msglist[2],sock,client_addr,list_addr,list_name,list_port)
elif msglist[0]=='register' :
print('registering')
register(msglist[1],msglist[2],msglist[3],sock,client_addr)
elif msglist[0]=='private' :
private_add=list_addr[list_name.index(msglist[1])]
private_port=list_port[list_name.index(msglist[1])]
private_user=list_name[list_port.index(client_addr[1])]
content=private_user+':'+''.join(msglist[2:])
sock.sendto(content.encode(),(private_add,private_port))
elif msglist[0]=='exit':
private_user = list_name[list_port.index(client_addr[1])]
userlist=doQuit(sock,private_user,list_addr,list_name,list_port)
else: #若是公聊,将用户的对话发送给所有在线用户
private_user = list_name[list_port.index(client_addr[1])]
content_all='%s:'%private_user+msg.decode()
for usr in list_name:
if usr != private_user:
private_add = list_addr[list_name.index(usr)]
private_port= list_port[list_name.index(usr)]
sock.sendto(content_all.encode(),(private_add,private_port))
if __name__ == '__main__':
main()
可借鉴,hdu伙伴切勿整抄,不然若是同一老师则直夸类行
有帮助的话,记得点点👍哦,大佬轻喷