1-基于socket实现的登录注册
需求
讲解视频参考:https://www.bilibili.com/video/BV1pa411r7TE?p=7&vd_source=f56f96c7f7894594fdc04129b7d97ff6
-- 基于socket和面向对象,实现一个注册功能,密码尽量以密文形式传输和存储
- 文件构成
D:\demo
- server.py
- client.py
- user.txt # 你也可以选择其他类型的文件
- 需求:
- 客户端和服务端都要使用面向对象实现,但不考虑多用户同时登录的问题(无需考虑io多路复用,和多线程实现多用户)
- 客户端运行程序后,有登录和注册、退出三个功能
- 当用户选择注册,让用户输入用户名和密码,并在客户端对密码进行md5加密,然后将数据传给 sever 端,server 校验用户名是否存在
- 存在则返回用户已存在,让客户端重新注册
- 如果不存在,则保存注册信息,并返回注册成功
- 如果用户选择登录,将用户输入的信息发送到server端进行校验
- 成功提示登录成功
- 否则提示登录失败
- 可选升级:如果用户已经登录成功,再次选择登录功能后,直接提示"自动登录成功",而无需再次输入用户名和密码
- 可选升级:如果用户选择退出,结束客户端程序并断开连接,然后server端断开与当前客户端的连接
- 客户端正常断开,向server端发一个断开的消息
- 客户端异常断开,server端也要进行断开处理
基础版
client.py
:
import hashlib
import socket
import json
class Client(object):
login_status = False
def __init__(self, ip, port):
self.ip = ip
self.port = port
self.sock = socket.socket()
self.sock.connect(('127.0.0.1', 8887), )
def md5(self, pwd):
""" 专门对密码进行加密,并返回加密后的密码 """
return hashlib.md5(pwd.encode()).hexdigest()
def register(self):
""" 专门负责注册逻辑的方法 """
while True:
user = input('user: ').strip()
pwd = input('pwd: ').strip()
if not user or not pwd:
continue
md_pwd = self.md5(pwd)
data = {"user": user, "pwd": md_pwd, "action_type": "register"}
self.sock.send(json.dumps(data).encode())
msg_data = self.sock.recv(1024).decode()
msg_data = json.loads(msg_data)
if msg_data['code'] == 201: # 用户已存在
print(msg_data['code_content'])
else: # 注册成功
print(msg_data['code_content'])
break
def login(self):
""" 专门负责登录逻辑的方法 """
if self.login_status:
print('已为你自动登录....')
return
while True:
user = input('user: ').strip()
pwd = input('pwd: ').strip()
if not user or not pwd:
continue
md_pwd = self.md5(pwd)
data = {"user": user, "pwd": md_pwd, "action_type": "login"}
self.sock.send(json.dumps(data).encode())
msg_data = self.sock.recv(1024).decode()
msg_data = json.loads(msg_data)
if msg_data['code'] == 301: # 用户名或者密码错误
print(msg_data['code_content'])
self.login_status = False
else: # 登录成功
print(msg_data['code_content'])
self.login_status = True
break
def q(self):
""" 退出程序 """
exit('你选择退出')
def logout(self):
""" 退出登录状态 """
if self.login_status:
self.login_status = False
print(f'当前用户已退出.....')
else:
print('用户没有登录')
def handler(self):
"""
专门用于跟客户端转发消息的方法
:return:
"""
tmp_dict = {
"1": ['登录', self.login],
"2": ['注册', self.register],
"3": ['退出', self.q],
"4": ['退出登录状态', self.logout]
}
while True:
for k, v in tmp_dict.items():
print(k, v[0])
choice = input('根据序号进行选择').strip()
if not choice: # 客户户端不能发送为空的消息
continue
if tmp_dict.get(choice):
tmp_dict.get(choice)[-1]()
else:
print('不支持的功能')
if __name__ == '__main__':
obj = Client('127.0.0.1', 8887)
obj.handler()
server.py
:
import os
import socket
import json
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
class Server(object):
file_path = os.path.join(BASE_DIR, 'user.txt')
msg_code = {
200: "register successful",
# 200: "注册成功",
201: "user exits",
300: "login successful",
301: "user or password error"
}
def __init__(self, ip, port):
self.ip = ip
self.port = port
self.sock = socket.socket()
self.sock.bind((self.ip, self.port), )
self.sock.listen(5)
# 如果用户文件不存在,就创建
if not os.path.isfile(self.file_path):
open(self.file_path, 'wb').close()
def register(self, data):
""" 专门负责注册逻辑的方法 """
print('register', data)
f = open(self.file_path, 'r', encoding='utf8')
for line in f:
# line: zhangkai|202cb962ac59075b964b07152d234b70
user, pwd = line.strip().split('|')
if user == data['user']: # 该用户已注册
data['code'] = 201
data['code_content'] = self.msg_code[201]
break
else: # 表示用户没有注册过,可以正常写文件了
f.close()
f = open(self.file_path, 'a', encoding='utf8')
f.write(f'{data["user"]}|{data["pwd"]}\n')
f.close()
data['code'] = 200
data['code_content'] = self.msg_code[200]
self.conn.send(json.dumps(data).encode())
def login(self, data):
""" 专门负责登录逻辑的方法 """
print('login', data)
f = open(self.file_path, 'r', encoding='utf8')
for line in f:
# line: zhangkai|202cb962ac59075b964b07152d234b70
user, pwd = line.strip().split('|')
if user == data['user'] and pwd == data['pwd']: # 用户名和密码校验成功
data['code'] = 300
data['code_content'] = self.msg_code[300]
break
else: # 用户名或者密码校验失败,需要重新登录
data['code'] = 301
data['code_content'] = self.msg_code[301]
f.close()
self.conn.send(json.dumps(data).encode())
def handler(self):
"""
专门用于跟客户端转发消息的方法
:return:
"""
while True: # 连接循环
print('等待新客户端的链接....')
self.conn, addr = self.sock.accept()
while True: # 通信循环
print(f'等待客户端发送消息....')
try:
msg = self.conn.recv(1024).decode()
if msg:
js_data = json.loads(msg)
print(js_data)
if hasattr(self, js_data['action_type']):
method = getattr(self, js_data['action_type'])
method(js_data)
else:
self.conn.send('不支持的方法'.encode())
else:
print('客户端退出')
break
except ConnectionResetError as e:
print('当前客户端异常断开', e)
break
self.conn.close()
self.sock.close()
if __name__ == '__main__':
obj = Server('127.0.0.1', 8887)
obj.handler()
报头封装版
client.py
:
import hashlib
import socket
import json
import struct
class Client(object):
login_status = False
def __init__(self, ip, port):
self.ip = ip
self.port = port
self.sock = socket.socket()
self.sock.connect(('127.0.0.1', 8887), )
def md5(self, pwd):
""" 专门对密码进行加密,并返回加密后的密码 """
return hashlib.md5(pwd.encode()).hexdigest()
def send_msg(self, data):
""" 将data字典序列化并且传递给对方 """
head_data = json.dumps(data).encode()
head_size = struct.pack('i', len(head_data))
self.sock.send(head_size)
self.sock.send(head_data)
def recv_msg(self):
""" 将对方传过来的报头大小和真实报头解析成字典并返回,它和send_msg搭配使用 """
head_size = self.sock.recv(4)
st_head_size = struct.unpack('i', head_size)[0]
head_data = self.sock.recv(st_head_size)
return json.loads(head_data.decode())
def register(self):
""" 专门负责注册逻辑的方法 """
while True:
user = input('user: ').strip()
pwd = input('pwd: ').strip()
if not user or not pwd:
continue
md_pwd = self.md5(pwd)
data = {"user": user, "pwd": md_pwd, "action_type": "register"}
self.send_msg(data)
msg_data = self.recv_msg()
if msg_data['code'] == 201: # 用户已存在
print(msg_data['code_content'])
else: # 注册成功
print(msg_data['code_content'])
break
def login(self):
""" 专门负责登录逻辑的方法 """
if self.login_status:
print('已为你自动登录....')
return
while True:
user = input('user: ').strip()
pwd = input('pwd: ').strip()
if not user or not pwd:
continue
md_pwd = self.md5(pwd)
data = {"user": user, "pwd": md_pwd, "action_type": "login"}
self.send_msg(data)
msg_data = self.recv_msg()
if msg_data['code'] == 301: # 用户名或者密码错误
print(msg_data['code_content'])
self.login_status = False
else: # 登录成功
print(msg_data['code_content'])
self.login_status = True
break
def q(self):
""" 退出程序 """
exit('你选择退出')
def logout(self):
""" 退出登录状态 """
if self.login_status:
self.login_status = False
print(f'当前用户已退出.....')
else:
print('用户没有登录')
def handler(self):
"""
专门用于跟客户端转发消息的方法
:return:
"""
tmp_dict = {
"1": ['登录', self.login],
"2": ['注册', self.register],
"3": ['退出', self.q],
"4": ['退出登录状态', self.logout]
}
while True:
for k, v in tmp_dict.items():
print(k, v[0])
choice = input('根据序号进行选择').strip()
if not choice: # 客户户端不能发送为空的消息
continue
if tmp_dict.get(choice):
tmp_dict.get(choice)[-1]()
else:
print('不支持的功能')
# self.sock.send(choice.encode())
# msg = self.sock.recv(1024).decode()
# print(msg)
if __name__ == '__main__':
obj = Client('127.0.0.1', 8887)
obj.handler()
server.py
:
import os
import socket
import json
import struct
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
class Server(object):
file_path = os.path.join(BASE_DIR, 'user.txt')
msg_code = {
200: "register successful",
# 200: "注册成功",
201: "user exits",
300: "login successful",
301: "user or password error"
}
def __init__(self, ip, port):
self.ip = ip
self.port = port
self.sock = socket.socket()
self.sock.bind((self.ip, self.port), )
self.sock.listen(5)
# 如果用户文件不存在,就创建
if not os.path.isfile(self.file_path):
open(self.file_path, 'wb').close()
def send_msg(self, data):
""" 将data字典序列化并且传递给对方 """
head_data = json.dumps(data).encode()
head_size = struct.pack('i', len(head_data))
self.conn.send(head_size)
self.conn.send(head_data)
def recv_msg(self):
""" 将对方传过来的报头大小和真实报头解析成字典并返回,它和send_msg搭配使用 """
head_size = self.conn.recv(4)
st_head_size = struct.unpack('i', head_size)[0]
head_data = self.conn.recv(st_head_size)
return json.loads(head_data.decode())
def register(self, data):
""" 专门负责注册逻辑的方法 """
print('register', data)
f = open(self.file_path, 'r', encoding='utf8')
for line in f:
# line: zhangkai|202cb962ac59075b964b07152d234b70
user, pwd = line.strip().split('|')
if user == data['user']: # 该用户已注册
data['code'] = 201
data['code_content'] = self.msg_code[201]
break
else: # 表示用户没有注册过,可以正常写文件了
f.close()
f = open(self.file_path, 'a', encoding='utf8')
f.write(f'{data["user"]}|{data["pwd"]}\n')
f.close()
data['code'] = 200
data['code_content'] = self.msg_code[200]
self.send_msg(data)
def login(self, data):
""" 专门负责登录逻辑的方法 """
print('login', data)
f = open(self.file_path, 'r', encoding='utf8')
for line in f:
# line: zhangkai|202cb962ac59075b964b07152d234b70
user, pwd = line.strip().split('|')
if user == data['user'] and pwd == data['pwd']: # 用户名和密码校验成功
data['code'] = 300
data['code_content'] = self.msg_code[300]
break
else: # 用户名或者密码校验失败,需要重新登录
data['code'] = 301
data['code_content'] = self.msg_code[301]
f.close()
self.send_msg(data)
def handler(self):
"""
专门用于跟客户端转发消息的方法
:return:
"""
while True: # 连接循环
print('等待新客户端的链接....')
self.conn, addr = self.sock.accept()
while True: # 通信循环
print(f'等待客户端发送消息....')
try:
msg_data = self.recv_msg()
if msg_data:
if hasattr(self, msg_data['action_type']):
method = getattr(self, msg_data['action_type'])
method(msg_data)
else:
msg_data['error_msg'] = "不支持的方法"
self.send_msg(msg_data)
else:
print('客户端退出')
break
except Exception as e:
print('当前客户端异常断开', e)
break
self.conn.close()
self.sock.close()
if __name__ == '__main__':
obj = Server('127.0.0.1', 8887)
obj.handler()
json类型文件实现版本
client.py
:
import socket
import json
import hashlib
import struct
class Client(object):
user_dict = {}
def __init__(self):
self.host = '127.0.0.1'
self.port = 8888
self.connect_socket()
def connect_socket(self):
"""绑定IP、端口"""
self.request = socket.socket()
self.request.connect((self.host, self.port), )
def send_msg(self, data):
"""发送数据"""
head_data = json.dumps(data).encode()
head_size = struct.pack('i', len(head_data))
self.request.send(head_size)
self.request.send(head_data)
def recv_msg(self):
"""接收数据"""
head_size = self.request.recv(4)
st_data = struct.unpack('i', head_size)[0]
head_data = self.request.recv(st_data).decode()
return json.loads(head_data)
def get_md5(self, meg):
return hashlib.md5(meg.encode()).hexdigest()
def login(self, ):
if self.user_dict.get("login_status", False):
print('自动登录成功.....')
else:
while True:
user, pwd = input('user: ').strip(), input('pwd: ').strip()
if user and pwd:
data = {"user": user, "pwd": self.get_md5(pwd), 'action_type': 'login'}
# print(data)
self.send_msg(data)
recv_data = self.recv_msg()
if recv_data.get("status") == 202:
print(recv_data.get("content"))
self.user_dict = recv_data
break
else:
print(recv_data.get("content"))
def register(self, ):
while True:
user, pwd = input('user: ').strip(), input('pwd: ').strip()
if user and pwd:
data = {"user": user, "pwd": self.get_md5(pwd), 'action_type': 'register'}
# print(data)
self.send_msg(data)
recv_data = self.recv_msg()
if recv_data.get("status") == 201:
print(recv_data.get("content"))
self.user_dict = recv_data
break
else:
print(recv_data.get("content"))
def q(self, ):
""" client exit """
self.send_msg({"action_type": 'q'})
exit('client exit.......')
def handler(self, ):
tmp_dict = {
"1": ['注册', self.register],
"2": ['登录', self.login],
"3": ['退出', self.q],
}
while True:
for k, v in tmp_dict.items():
print(k, v[0])
choice = input('根据序号选择操作: ').strip()
if choice in tmp_dict:
tmp_dict[choice][-1]()
else:
print('输入不合法!!!')
if __name__ == '__main__':
Client().handler()
server.py
:
import os
import socket
import json
import struct
from socket import SOL_SOCKET, SO_REUSEADDR
sock = socket.socket()
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sock.bind(("127.0.0.1", 8888))
sock.listen(5)
class Server(object):
msg = {
200: "register error, user exists!",
201: "register successful",
202: "login successful",
203: "login error, user or password error",
}
user_info_path = './userinfo.json'
def __init__(self):
self.request, addr = sock.accept()
def read_file(self):
if not os.path.isfile(self.user_info_path):
open(self.user_info_path, 'wb').close()
with open(self.user_info_path, 'r', encoding='utf-8') as f:
data = f.read()
if data:
return json.loads(data)
else:
return {}
def write_file(self, data):
with open(self.user_info_path, 'w', encoding='utf-8') as f:
return json.dump(data, f)
def send_msg(self, data):
"""发送数据"""
head_data = json.dumps(data).encode()
head_size = struct.pack('i', len(head_data))
self.request.send(head_size)
self.request.send(head_data)
# sock.send(json.dumps(data).encode())
def recv_msg(self):
"""接收数据"""
head_size = self.request.recv(4)
st_data = struct.unpack('i', head_size)[0]
head_data = self.request.recv(st_data).decode()
return json.loads(head_data)
def q(self, data):
""" sever端退出 """
exit('server exit......')
def login(self, data):
user_info = self.read_file()
if data['user'] == user_info.get(data['user'])['user'] and data['pwd'] == user_info.get(data['user'])['pwd']:
data['status'] = 202
data['content'] = self.msg[202]
data['login_status'] = True
else:
data['status'] = 203
data['content'] = self.msg[203]
data['login_status'] = False
self.send_msg(data)
def register(self, data):
user_info = self.read_file()
if data['user'] in user_info:
data['status'] = 200
data['content'] = self.msg[200]
else:
user_info[data['user']] = {"user": data['user'], "pwd": data['pwd']}
self.write_file(user_info)
data['status'] = 201
data['content'] = self.msg[201]
self.send_msg(data)
def handler(self):
while True:
data = self.recv_msg()
if hasattr(self, data['action_type']):
getattr(self, data['action_type'])(data)
if __name__ == '__main__':
Server().handler()
that's all