文章目录
内容描述
内容及功能
构建客户端。客户端具备的功能有:可以输入客户端的账号和密码,可以选择你所需要的各种功能,可以输入文字和服务器进行交流,可以通过IP及端口号和服务器进行连接,可以自主和服务器断开连接。
构建服务器。服务器具备的功能有:可以连接数据库,可以通过调用数据库验证客户端输入的账号和密码是否正确,密码和账号都正确才可以继续使用,可以通过数据库删除账号,注册账号,修改密码,可以输入文字和客户端进行交流,可以通过IP及端口号和客户端进行连接,服务器一直处于启动中。
目的
通过使用TCP或UDP套接字构建客户端和服务端,搭建数据库,实现在客户端输入账号和密码,在服务器端通过调用数据库进行验证,确认是否正确以便于使用接下来的功能,通过数据库进行对注册账号的保存,删除,和密码修改,还设计了一个服务器和客户端之间互相交流的的程序,总体类似于市面上社交软件的登录和交流功能,主要的目的在于了解在网络中的数据传输和保存,提高对网络安全的认识。
套接字
套接字:在网络中,由IP地址可以唯一确定一台主机,但是准确来说,网络通讯中的双方并不是主机,而是运行在主机上的进程,这样就需要进一步确定是主机中的哪个进程要进行网络通讯。因此,除了IP地址之外,还需要端口号来唯一确定主机中的通讯进程。IP地址和端口号就构成了一个网络中的唯一标识符,即套接字。
套接字用途:Socket被用于客户端/服务端应用框架中。服务端是一个针对客户端的请求执行某些特定操作的进程。大多数应用层协议如FTP、SMTP和POP3使用Socket来建立客户端与服务端之间的连接,从而进行数据的交换。
程序设计概要
程序流程图
服务器端构建(代码后附)
创建socket实例对象。
使用bind()方法绑定socket地址。
listen()开始监听。
accept()接收客户端连接,会阻塞。
与客户端进行读写交互。
关闭连接。
客户端构建(代码后附)
创建Socket实例对象。
connect()连接服务器。
与服务器端进行读写交互。
关闭连接
程序内容及代码
创建数据库
使用Pycharm自带数据库创建一个小型数据库,用来存放和读取用户的账号密码。
create table user
(
id nchar(20) not null,
password nchar(20) not null
);
create unique index user_id_uindex
on user (id);
定义数据库功能
# 定义数据库类
class Database:
def __init__(self):
self.conn = sqlite3.connect('test.db')
self.c = self.conn.cursor()
# 查询功能,登录验证调用
def select(self,id):
sql = f"select password from user where id='{id}'"
cursor = self.c.execute(sql)
for raw in cursor:
return raw[0]
# 增加功能,注册功能
def insert(self,id, password):
sql = f"insert into user (id,password) values ('{id}','{password}')"
try:
self.c.execute(sql)
self.conn.commit()
return True
except:
self.conn.rollback()
return False
# 根据账号上传密码功能,修改密码功能
def update(self,id,password):
sql = f"update user set password='{password}' where id='{id}'"
try:
self.c.execute(sql)
self.conn.commit()
return True
except:
print('update error')
self.conn.rollback()
return False
# 删除功能,删除账户功能
def delete(self,id):
sql = f"delete from user where id='{id}'"
try:
self.c.execute(sql)
self.conn.commit()
return True
except:
print('delete error')
self.conn.rollback()
return False
自定义应用层协议
请求(Request)报文
自定义一个UCCP请求报文,在请求行中,用方法确定所选择的协议函数,UCCP表示协议版本,由于UCCP为自定义协议,故这里只写UCCP;在请求头中,id表示账号,即请求头中的key,password表示密码,也是请求头中的key,请求头中的value就是客户端需要输入的账号密码,这里均用冒号分割成键值对的形式;空白行是请求报头与请求正文的分界,不可省略;请求正文body,空行以后的都是请求正文,可以为空字符串。具体格式如表4.1所示。
表4.1 自定义请求报文格式
请求行 | 方法 | 空格 | UCCP | \r\n |
---|---|---|---|---|
Key(id) | : | {id} | \r\n | |
请求头 | Key(password) | : | {password} | \r\n |
…n个请求头信息 | ||||
空白行 | \r\n | |||
请求体 | body |
响应(Response)报文
自定义一个UCCP响应报文,在响应行中,以空格为界,分为三个区域,即协议版本、状态码和状态码解释三部分;响应头部均以冒号分割的键值对形式出现;空白行是响应报头和响应数据的分界,不可省略;响应数据body允许为空。具体格式如表4.2所示。
表4.2 自定义响应报文格式
响应行 | UCCP | 空格 | 状态码 | 空格 | 状态码解释 | \r\n |
---|---|---|---|---|---|---|
Key | : | Value | \r\n | |||
响应头部 | Key | : | Value | \r\n | ||
…n个响应头信息 | ||||||
空白行 | \r\n | |||||
响应数据 | body |
UCCP协议函数的方法
表4.3 UCCP协议的方法
方法 | 说明 |
---|---|
LOG | 客户端登陆 |
MSG | 获取信息 |
REG | 注册信息 |
CPW | 修改密码 |
DEL | 删除账户 |
CLS | 关闭客户端 |
状态码
状态码 | 状态码解释 |
---|---|
100 | OK,请求成功,服务器端答应了请求 |
200 | ERROR,服务器未答应请求 |
UCCP协议函数方法
# 创建客户端发送协议
def create_request(ty, id, passwd):
return f'{ty} UCCP {id} {passwd}'
# 创建聊天协议
def create_message(message):
return f'MSG UCCP {message}'
# 创建返回协议,谁发送谁返回
def create_response(code):
if code == 100:
return 'UCCP 100 OK'
elif code == 200:
return 'UCCP 200 ERROR'
# 解析客户端发送协议
def analysis_request(mess):
msl = mess.split(' ')
ty = msl[0]
id = msl[2]
password = msl[3]
return ty, id, password
# 解析聊天协议
def analysis_message(mess):
message = mess.split(' ')[2]
return message
# 解析返回协议
def analysis_response(mess):
code = mess.split(' ')[1]
return int(code)
程序编码
客户端(client)编码
from socket import *
from Protocol import *
使用套接字进行构建客户端
def client():
# 定义一个计数器count
count = 0
# 定义一个常量
i = 3
# 创建一个Socket实例对象
Socket = socket(AF_INET, SOCK_STREAM)
# connect()连接服务器
Socket.connect(('localhost', 8099)
制作一个选择器,让人们选择所需的功能
# 与服务器端进行读写交互
while True:
print('''1.登录聊天\n2.注册账户\n3.修改密码\n4.删除账户\n5.退出\n''')
tynum = int(input('请选择所需服务:'))
while tynum > 5 or tynum < 1:
tynum = int(input('服务选择错误!请重新选择:'))
登录功能
# 登录功能
if tynum == 1:
# 输入客户端账号和密码
client_number = input("请输入您的客户端账号:")
client_passwd = input("请输入您的客户端密码:")
message = create_request('LOG', client_number, client_passwd)
Socket.send(message.encode('utf8'))
code = analysis_response(Socket.recv(1024).decode('utf8'))
if code == 200:
print('您输入的账户或密码有误,请重新登录。')
count += 1
print("剩余输入次数:", i - count)
if count >= 3:
print("你的账户已被锁死!")
break
else:
print('登陆成功!')
注册功能
# 注册功能
elif tynum == 2:
client_number = input("请输入您的客户端账号:")
while True:
client_passwd = input("请输入您的客户端密码:")
if client_passwd != input("请再次输入您的客户端密码:"):
print('两次输入密码不一致,请重新输入!')
else:
break
message = create_request('REG', client_number, client_passwd)
Socket.send(message.encode('utf8'))
code = analysis_response(Socket.recv(1024).decode('utf8'))
if code == 200:
print('注册成功!')
else:
print('注册失败!')
修改密码功能
# 修改密码功能
elif tynum == 3: # 修改密码
client_number = input("请输入您要修改的客户端账号:")
client_passwd = input("请输入您的客户端新密码:")
message = create_request('CPW', client_number, client_passwd)
Socket.send(message.encode('utf8'))
code = analysis_response(Socket.recv(1024).decode('utf8'))
if code == 200:
print('修改成功!')
else:
print('修改失败!')
删除账户功能
elif tynum == 4:
client_number = input("请输入您要删除的客户端账号:")
message = create_request('DEL', client_number, '')
Socket.send(message.encode('utf8'))
code = analysis_response(Socket.recv(1024).decode('utf8'))
if code == 200:
print('删除成功!')
else:
print('删除失败!')
退出功能
elif tynum == 5:
print('正在关闭。。。')
message = create_request('CLS', '', '')
Socket.send(message.encode('utf8'))
Socket.close()
break
else:
pass
聊天功能
while True:
# 客户端向服务器发送消息
data = input('客户:')
mess = create_message(data)
Socket.send(mess.encode('utf8'))
# 判断客户端输入的消息,是bye则断开连接,不是就继续与服务器交互
if data == 'bye':
close = analysis_message(Socket.recv(1024).decode('utf8'))
print('服务:', close)
break
else:
close = analysis_message(Socket.recv(1024).decode('utf8'))
print('服务:', close)
完整客户端代码
from socket import *
from Protocol import *
def client():
# 定义一个计数器count
count = 0
# 定义一个常量
i = 3
# 创建一个Socket实例对象
Socket = socket(AF_INET, SOCK_STREAM)
# connect()连接服务器
Socket.connect(('localhost', 8099))
# 与服务器端进行读写交互
while True:
print('''1.登录聊天\n2.注册账户\n3.修改密码\n4.删除账户\n5.退出\n''')
tynum = int(input('请选择所需服务:'))
while tynum > 5 or tynum < 1:
tynum = int(input('服务选择错误!请重新选择:'))
# 登录功能
if tynum == 1:
# 输入客户端账号和密码
client_number = input("请输入您的客户端账号:")
client_passwd = input("请输入您的客户端密码:")
message = create_request('LOG', client_number, client_passwd)
Socket.send(message.encode('utf8'))
code = analysis_response(Socket.recv(1024).decode('utf8'))
if code == 200:
print('您输入的账户或密码有误,请重新登录。')
count += 1
print("剩余输入次数:", i - count)
if count >= 3:
print("你的账户已被锁死!")
break
else:
print('登陆成功!')
while True:
# 客户端向服务器发送消息
data = input('客户:')
mess = create_message(data)
Socket.send(mess.encode('utf8'))
# 判断客户端输入的消息,是bye则断开连接,不是就继续与服务器交互
if data == 'bye':
close = analysis_message(Socket.recv(1024).decode('utf8'))
print('服务:', close)
break
else:
close = analysis_message(Socket.recv(1024).decode('utf8'))
print('服务:', close)
# 注册功能
elif tynum == 2:
client_number = input("请输入您的客户端账号:")
while True:
client_passwd = input("请输入您的客户端密码:")
if client_passwd != input("请再次输入您的客户端密码:"):
print('两次输入密码不一致,请重新输入!')
else:
break
message = create_request('REG', client_number, client_passwd)
Socket.send(message.encode('utf8'))
code = analysis_response(Socket.recv(1024).decode('utf8'))
if code == 200:
print('注册成功!')
else:
print('注册失败!')
# 修改密码功能
elif tynum == 3: # 修改密码
client_number = input("请输入您要修改的客户端账号:")
client_passwd = input("请输入您的客户端新密码:")
message = create_request('CPW', client_number, client_passwd)
Socket.send(message.encode('utf8'))
code = analysis_response(Socket.recv(1024).decode('utf8'))
if code == 200:
print('修改成功!')
else:
print('修改失败!')
# 删除账户
elif tynum == 4:
client_number = input("请输入您要删除的客户端账号:")
message = create_request('DEL', client_number, '')
Socket.send(message.encode('utf8'))
code = analysis_response(Socket.recv(1024).decode('utf8'))
if code == 200:
print('删除成功!')
else:
print('删除失败!')
#退出功能
elif tynum == 5:
print('正在关闭。。。')
message = create_request('CLS', '', '')
Socket.send(message.encode('utf8'))
Socket.close()
break
else:
pass
client()
服务器(server)编码
引入数据库类、协议
from sqlite import Database
from Protocol import analysis_request, create_response,analysis_message,create_message
定义一个Server()函数,将服务器的功能都封装在函数中
def Server():
......
Server()
接收客户端的需求,账号,密码
ty, id, password =
analysis_request(socket_connect.recv(1024).decode('utf8'))
根据服务器的功能选择所需的操作
if ty == 'LOG':
Tpassword = db.select(id)
con = password != Tpassword
print(f'Result:LOG {con}')
if con:
message = create_response(200)
socket_connect.send(message.encode('utf8'))
else:
message = create_response(100)
socket_connect.send(message.encode('utf8'))
elif ty == 'REG':#注册账户
con = db.insert(id, password)
print(f'Result:REG {con}')
if con:
message = create_response(200)
socket_connect.send(message.encode('utf8'))
else:
message = create_response(100)
socket_connect.send(message.encode('utf8'))
elif ty == 'CPW':#修改密码
con = db.update(id, password)
print(f'Result:CPW {con}')
if con:
message = create_response(200)
socket_connect.send(message.encode('utf8'))
else:
message = create_response(100)
socket_connect.send(message.encode('utf8'))
elif ty == 'DEL':#删除账户
con = db.delete(id)
print(f'Result:DEL {not con}')
if con:
message = create_response(200)
socket_connect.send(message.encode('utf8'))
else:
message = create_response(100)
socket_connect.send(message.encode('utf8'))
elif ty == 'CLS':#关闭服务
socket_connect.close()
print(f'close connection: {addr}')
#重新等待建立连接
socket_connect, addr = Socket.accept()
print(f'create connection: {addr}')
else:
message = create_response(200)
socket_connect.send(message.encode('utf8'))
与客户端的对话功能
# 与客户端进行读写交互
while True:
# 接受客户端发送的消息
recive = analysis_message(socket_connect.recv(1024).decode('utf8'))
# 打印消息
print('客户:', recive)
# 如果接受到的信息为‘拜拜’,则终止程序
if recive == 'bye':
data = create_message('bye')
socket_connect.send(data.encode('utf8'))
break
# 服务器向客户端发送消息
data = create_message(input('服务:'))
socket_connect.send(data.encode('utf8'))
完整服务器端代码
from socket import *
from sqlite import Database
from Protocol import analysis_request, create_response,analysis_message,create_message
def Server():
# 创建socket实例对象
db = Database()
Socket = socket(AF_INET, SOCK_STREAM)
# 使用bind()方法绑定socket地址
Socket.bind(('localhost', 8099))
# listen()开始监听
Socket.listen(3)
print('Server is runing')
# accept()接收客户端连接,会阻塞
socket_connect, addr = Socket.accept()
print(f'create connection: {addr}')
# 与客户端进行读写交互
while True:
ty, id, password = analysis_request(socket_connect.recv(1024).decode('utf8'))
print(f'Command:{ty} {id} {password}')
if ty == 'LOG':
Tpassword = db.select(id)
con = password != Tpassword
print(f'Result:LOG {con}')
if con:
message = create_response(200)
socket_connect.send(message.encode('utf8'))
else:
message = create_response(100)
socket_connect.send(message.encode('utf8'))
# 与客户端进行读写交互
while True:
# 接受客户端发送的消息
recive = analysis_message(socket_connect.recv(1024).decode('utf8'))
# 打印消息
print('客户:', recive)
# 如果接受到的信息为‘拜拜’,则终止程序
if recive == 'bye':
data = create_message('bye')
socket_connect.send(data.encode('utf8'))
break
# 服务器向客户端发送消息
data = create_message(input('服务:'))
socket_connect.send(data.encode('utf8'))
elif ty == 'REG':#注册账户
con = db.insert(id, password)
print(f'Result:REG {con}')
if con:
message = create_response(200)
socket_connect.send(message.encode('utf8'))
else:
message = create_response(100)
socket_connect.send(message.encode('utf8'))
elif ty == 'CPW':#修改密码
con = db.update(id, password)
print(f'Result:CPW {con}')
if con:
message = create_response(200)
socket_connect.send(message.encode('utf8'))
else:
message = create_response(100)
socket_connect.send(message.encode('utf8'))
elif ty == 'DEL':#删除账户
con = db.delete(id)
print(f'Result:DEL {not con}')
if con:
message = create_response(200)
socket_connect.send(message.encode('utf8'))
else:
message = create_response(100)
socket_connect.send(message.encode('utf8'))
elif ty == 'CLS':#关闭服务
socket_connect.close()
print(f'close connection: {addr}')
#重新等待建立连接
socket_connect, addr = Socket.accept()
print(f'create connection: {addr}')
else:
message = create_response(200)
socket_connect.send(message.encode('utf8'))
Server()