用socket实现之后就试着用一下socketserver模块
import socketserver
import json
import struct
import os
import hashlib
class Myserver(socketserver.BaseRequestHandler):
def handle(self) -> None:
self.options_list={'download':self.download,'upload':self.upload}
self.conn = self.request
self.login()
self.opt_dic = self.my_recv()
if self.opt_dic['operate'] in self.options_list:
self.options_list[self.opt_dic['operate']]()
def get_md5(self,username, password):
md5 = hashlib.md5(username.encode('utf-8'))
md5.update(password.encode('utf-8'))
return md5.hexdigest()
def my_recv(self):
leng = self.conn.recv(4)
msg_dic = self.conn.recv(struct.unpack('i', leng)[0]).decode('utf-8')
msg = json.loads(msg_dic)
return msg
def my_send(self,dic):
str_dic = json.dumps(dic)
length = struct.pack('i', len(str_dic))
self.conn.send(length)
self.conn.send(str_dic.encode('utf-8'))
def upload(self):
msg = self.my_recv()
with open(msg['filename'], mode='wb') as f:
while msg['filesize'] > 0:
content = self.conn.recv(1024)
msg['filesize'] -= len(content) # 防止接收信息不够,由于tcp的优化,发送方发送1024个字节,接收时可能会分为几块,每一块多大,这里总长度就减去多少
f.write(content)
def download(self):
abs_path = r'/home/lmh/PycharmProjects/网络编程/tmp'
filename = os.path.basename(abs_path)
filesize = os.path.getsize(abs_path)
dic = {'filename': filename, 'filesize': filesize}
self.my_send(dic)
with open(abs_path, mode='rb') as f:
while filesize > 0:
content = f.read(1024)
filesize -= len(content)
self.conn.send(content)
def login(self):
flag = True
while flag:
msg = self.my_recv()
with open(r'/home/lmh/PycharmProjects/网络编程/socketserver模块/userinfo', 'r', encoding='utf-8') as f:
for line in f:
name, pwd = line.strip().split('|')
if name == msg['username'] and pwd == self.get_md5(name, msg['password']):
res = True
flag = False
break
else:
res = False
dic = {'operate': 'login', 'result': res}
self.my_send(dic)
server = socketserver.ThreadingTCPServer(('127.0.0.1',9001),Myserver)
server.serve_forever() #固定的写法
import socket
import os
import sys
import struct#处理黏包
import json
sk = socket.socket()
sk.connect(('127.0.0.1',9001))
def upload(sk):
opt_dic = {'operate':'upload'}
my_send(sk,opt_dic)
#文件名/文件大小
abs_path = r'/home/lmh/PycharmProjects/网络编程/tmp'
filename = os.path.basename(abs_path)
filesize = os.path.getsize(abs_path)
dic = {'filename':filename,'filesize':filesize}
my_send(sk,dic)
with open(abs_path,mode='rb') as f:
while filesize>0:
content = f.read(1024)
filesize-=len(content)
sk.send(content)
sk.close()
def download(sk):
opt_dic = {'operate':'download'}
my_send(sk,opt_dic)
msg=my_recv(sk)
with open(msg['filename'], mode='wb') as f:
while msg['filesize'] > 0:
content = sk.recv(1024)
msg['filesize'] -= len(content) # 防止接收信息不够,由于tcp的优化,发送方发送1024个字节,接收时可能会分为几块,每一块多大,这里总长度就减去多少
f.write(content)
# sk.close()
def my_send(sk,dic):
str_dic = json.dumps(dic)
b_dic = str_dic.encode('utf-8')
length = struct.pack('i', len(b_dic))
sk.send(length)
sk.send(b_dic)
def my_recv(sk):
length = sk.recv(4)
msg = sk.recv(struct.unpack('i', length)[0]).decode('utf-8')
msg = json.loads(msg)
return msg
'''
登录
'''
def login(sk):
flag = True
while flag:
username = input('用户名:').strip()
password = input('密 码:').strip()
dic = {'username': username, 'password': password}
my_send(sk,dic)
msg = my_recv(sk)
if msg['result']:
print('登录成功')
flag=False
else:
print('登录失败')
login(sk)
# 上传和下载的逻辑
opt_lst=['upload','download']
for index,opt in enumerate(opt_lst,1):
print(index,opt)
num = int(input('请选择你要操作的序号:'))
if hasattr(sys.modules[__name__],opt_lst[num-1]):
getattr(sys.modules[__name__],opt_lst[num-1])(sk)
sk.close()
在实现的过程中,server端的函数反射遇到了问题,由于之前都是用的classmethod,这次写反射的过程中由于对对象和类的区分不深刻,总是错误,最后还是用字典去实现反射。
对象是类的实例,类是对象的模板。对象是通过new classname产生的,用来调用类的方法;类的构造方法
classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。如果定义的方法里传了self,在调用的时候也要传self
静态变量 是个所有对象共享的变量 由对象/类调用,但不能重新赋值
绑定方法 自带self参数的函数,由对象调用
类方法 是个自带cls参数的函数 由对象/类调用
class A(object):
# 属性默认为类属性(可以给直接被类本身调用)
num = "类属性"
# 实例化方法(必须实例化类之后才能被调用)
def func1(self): # self : 表示实例化类后的地址id
print("func1")
print(self)
# 类方法(不需要实例化类就可以被类本身调用)
@classmethod
def func2(cls): # cls : 表示没用被实例化的类本身
print("func2")
print(cls)
print(cls.num)
cls().func1()
# 不传递传递默认self参数的方法(该方法也是可以直接被类调用的,但是这样做不标准)
def func3():
print("func3")
print(A.num) # 属性是可以直接用类本身调用的
# A.func1() 这样调用是会报错:因为func1()调用时需要默认传递实例化类后的地址id参数,如果不实例化类是无法调用的
A.func2()
A.func3()