目录
- socketserver模块
- 通过模块实现并发
socketserver模块
我们可以通过socketserver模块来模拟并发的一个状态
接下来我们通过使用socketserver来编写程序模拟并发的一个过程
程序编写:
思路:
- 用户注册登录界面
- 选择功能
- 功能的选择
- 上传
- 下载
- 查看文件
- 删除文件
- 功能的选择
- 对TCP协议粘包的处理
了解粘包:
- 粘包就是当数据过大或者过小,导致一直存储在内存中。过小导致多次数据同时发送,过大导致数据无法接收完全。
粘包代码:
服务器
import socket
#生成一个socket对象
soc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定地址跟端口号
soc.bind(('127.0.0.1',8001))
#监听(半连接池的大小),不是连接数
soc.listen(3)
#等着客户端来连接,conn相当于连接通道,addr是客户端的地址
while True:
print('等待客户端连接')
conn,addr=soc.accept() #卡主,如果没有客户端连接,会一直卡在这,当有连接,才继续往下走
print('有个客户端连接上了',addr)
while True:
try:
data=conn.recv(1024)
print(data)
data2=conn.recv(1024)
print(data2)
data3=conn.recv(1024)
print(data3)
except Exception:
break
# 关闭通道
conn.close()
# 关闭套接字
soc.close()
客服端
import socket
soc=socket.socket()
soc.connect(('127.0.0.1',8001))
while True:
in_s=input('请输入要发送的数据:')
soc.send(b'a')
soc.send(b'b')
soc.send(b'c')
通过模块实现并发
登录界面代码实现:
import os
import json
import time
import nbK
def register():
user_dict = {
'username':None,
'password':None
}
while True:
print('--------注册---------')
name = input('请输入你的用户名').strip()
pwd = input('请输入你要使用的密码:').strip()
re_pwd = input('确认要使用的密码:').strip()
if name == 'q' or name == 'Q':
break
if os.path.isdir(f'user/{name}'):
print('用户已经存在')
continue
if pwd == re_pwd:
print('注册成功')
os.mkdir(f'user/{name}')
user_dict['username'] = name
user_dict['password'] = pwd
with open(f'user_info/{name}.json','w') as fw:
json.dump(user_dict,fw)
fw.flush()
break
def login():
s = 3
while True:
print('--------登录---------')
name = input('请输入你的用户名')
pwd = input('请输入你的密码')
if not os.path.exists(f'user_info/{name}.json'):
while s>0:
print(f'\r用户不存在,{s-1}s后跳转到注册界面',end='')
time.sleep(1)
s-=1
print('\n')
register()
continue
with open(f'user_info/{name}.json','r') as fr:
user_dic = json.load(fr)
if name == user_dic.get('username') and pwd == user_dic.get('password'):
print('登录成功')
nbK.run(name)
break
else:
print('用户名密码不正确')
def run():
FUN_DIC = {
'1':register,
'2':login
}
while True:
print('''
请选择你的选项
1——注册
2——登录
''')
select = input('请输入要选择的功能').strip()
if not select.isdigit():
print('请输入正确的选项')
if not FUN_DIC.get(select):
print('没有你要选择的功能')
FUN_DIC[select]()
run()
客户端界面:
import socket
import struct
import os
soc = socket.socket()
soc.connect(('127.0.0.1', 8080))
user_dic = {'username':None}
def uploading():
soc.send(b'1')
while True:
fife_name = input('请输入你要上传的文件名,加后缀哦')
if fife_name == 'q' or fife_name == 'Q':
break
if '.' not in fife_name:
print('请重新输入你要上传的文件夹名')
continue
#传入用户长度
user_len = struct.pack('i',len(user_dic['username']))
soc.send(user_len)
#传入用户名
soc.send(bytes(user_dic['username'].encode('utf8')))
#传入文件名长度
fife_name_len = struct.pack('i',len(fife_name))
soc.send(fife_name_len)
# 传入文件名
soc.send(bytes(fife_name.encode('utf8')))
with open(fife_name,'rb') as fr:
byts = fr.read()
#传入数据长度
b_len = struct.pack('i',len(byts))
soc.send(b_len)
# #传入数据
soc.send(byts)
print(f'上传文件{fife_name}成功')
#下载文件
def download():
soc.send(b'2')
while True:
try:
user_info_list = os.listdir(f'F:\py\9.10并发\\user_name\{user_dic["username"]}')
for index,name in enumerate(user_info_list):
print(index+1,name)
select = input('请选择你要下载的文件')
if select == 'q' or select == 'Q':
break
if len(user_info_list) < int(select):
print('没有你想要下载的文件')
continue
# 传入用户长度
user_len = struct.pack('i', len(user_dic['username']))
soc.send(user_len)
# 传入用户名
soc.send(bytes(user_dic['username'].encode('utf8')))
#获取文件名
fife_name = user_info_list[int(select)-1]
# 传入文件名长度
fife_name_len = struct.pack('i', len(fife_name))
soc.send(fife_name_len)
# 传入文件名
soc.send(bytes(fife_name.encode('utf8')))
# 获取长度信息
re_len = soc.recv(4)
# 获取真正的长度
l = struct.unpack('i', re_len)[0]
# 进行数据拼接
count = 0
date_total = b''
while count < l:
if l < 1024: # 如果接受的数据小于1024 ,直接接受数据大小
data = soc.recv(l)
else: # 如果接受的数据大于1024
if l - count >= 1024: # 总数据长度-count(目前收到多少,count就是多少) 如果还大于1024 ,在收1024
data = soc.recv(1024)
else: # 总数据长度-count(目前收到多少,count就是多少) 如果小于1024,只收剩下的部分就可
data = soc.recv(l - count)
date_total += data
count += len(data)
with open(f'{fife_name}', 'wb') as fw:
fw.write(date_total)
print(f'下载文件{fife_name}成功')
except Exception:
print('请输入要下载的文件序号')
continue
#删除文件
#########
#偷懒写法#
#########
def delfife():
while True:
user_info_list = os.listdir(f'F:\py\9.10并发\\user_name\{user_dic["username"]}')
for index, name in enumerate(user_info_list):
print(index + 1, name)
select = input('请选择你要删除的文件')
if select == 'q' or select == 'Q':
break
if len(user_info_list)<int(select):
print('没有你想要删除的文件')
os.remove(f'F:\py\9.10并发\\user_name\{user_dic["username"]}\{user_info_list[int(select)-1]}')
print('删除成功')
def run(name):
FUN_DIC = {
'1': uploading,
'2': download,
'3':delfife
}
user_dic['username'] = name
while True:
print('''
请选择你需要的功能:
1-上传文件
2-下载文件
3-删除文件
''')
select = input('请输入要选择的功能:').strip()
if select == 'q' or select == 'Q':
break
if not select.isdigit():
print('请输入正确的选项')
if not FUN_DIC.get(select):
print('没有你要选择的功能')
FUN_DIC[select]()
服务器端
import socketserver
import struct
user_dic = {'username':None}
class Mytcp(socketserver.BaseRequestHandler):
#重写handle方法
def handle(self):
try:
while True:
select = self.request.recv(1)
select = int(select)
if select == 1:
print(self.client_address)
while True:
#用户名长度
age_len = self.request.recv(4)
if not age_len:
break
#用户名真正长度
age_l = struct.unpack('i',age_len)[0]
user_dic['username'] = self.request.recv(age_l).decode('utf8')
#文件名长度
fife_len = self.request.recv(4)
#文件名真正长度
fife_l = struct.unpack('i',fife_len)[0]
#读取文件名
fife_name = self.request.recv(fife_l).decode('utf8')
#获取长度信息
re_len = self.request.recv(4)
#获取真正的长度
l = struct.unpack('i',re_len)[0]
#进行数据拼接
count = 0
date_total =b''
while count < l:
if l < 1024: # 如果接受的数据小于1024 ,直接接受数据大小
data = self.request.recv(l)
else: # 如果接受的数据大于1024
if l - count >= 1024: # 总数据长度-count(目前收到多少,count就是多少) 如果还大于1024 ,在收1024
data = self.request.recv(1024)
else: # 总数据长度-count(目前收到多少,count就是多少) 如果小于1024,只收剩下的部分就可
data = self.request.recv(l - count)
date_total += data
count += len(data)
with open(f'F:\py\9.10并发\\user_name\{user_dic.get("username")}\{fife_name}','wb') as fw:
fw.write(date_total)
else:
while True:
# 用户名长度
age_len = self.request.recv(4)
if not age_len:
break
# 用户名真正长度
age_l = struct.unpack('i', age_len)[0]
user_dic['username'] = self.request.recv(age_l).decode('utf8')
# 文件名长度
fife_len = self.request.recv(4)
# 文件名真正长度
fife_l = struct.unpack('i', fife_len)[0]
# 读取文件名
fife_name = self.request.recv(fife_l).decode('utf8')
##打开文件
with open(f'F:\py\9.10并发\\user_name\{user_dic["username"]}\{fife_name}','rb') as fr:
byts = fr.read()
# 传入数据长度
b_len = struct.pack('i', len(byts))
self.request.send(b_len)
# #传入数据
self.request.send(byts)
#第一个参数是用于绑定地址,第二个参数传一个类
#ThreadingTCPServer自动开线程处理链接状态
except Exception:
pass
server = socketserver.ThreadingTCPServer(('127.0.0.1',8080),Mytcp)
#一直监听
server.serve_forever()
原文地址:https://www.cnblogs.com/ledgua/p/11528787.html
时间: 2024-11-08 20:42:45