摘要
一到放假就杂事很多,这次的作业比较复杂,做了一个周,进度又拖了。不过结果还不错。
正文
粘包
在上一节中,如果连续发送过多数据,就可能发生粘包。粘包就是两次发送的数据粘在一起被接收,损坏了数据的完整性。解决方法有两种。
方案一:
在发送多个数据之间添加接收确认。这样在完成一次发送以后只有接收到另一端的确认以后才会开始新的发送,避免了粘包的发生。
方案二:
首先将发送数据的大小发送给另一端,另一端根据数据的大小接收。一次接收一次发送的数据量,这样也就避免了数据的粘包。例子。
#方案1
#server
f = open(path, 'wb')
for i in f:
server.socket.send(i)
f.close()
server.socket.recv(1024)
server.socket.send(b'发送完毕')
#client
recv_data = ''
while True:
data = client.socket.recv(1024)
if not data:
break
recv_data += data
client.socket.send(b'recv over')
other_data = client.socket.recv(1024)
#方案2
#server
size = os.path.getsize(path)
server.socket.send(size.encode())
server.socket.recv(1024)
f = open(path, 'wb')
for i in f:
server.socket.send(i)
f.close()
server.socket.send(b'发送完毕')
#client
size = client.socket.recv(1024)
client.socket.send(b'start')
recv_data = ''
recv_size = 0
while recv_size < size:
if size - recv_size < 1024:
single_size = size - recv_size
else:
single_size = 1024
data = client.socket.recv(single_size)
recv_size += len(data)
recv_data += data
other_data = client.socket.recv(1024)
FTP
一个简单的FTP过程主要包含以下几个步骤。
1.读取文件名
2.检测文件是否存在
3.打开文件
4.检测文件大小,文件名
5.发送文件大小 md5值给客户端
6.等待客户端确认
7.开始边读取数据边发送数据
8.md5确认
具体例子。
服务器
# 服务器端
import socket
import os
import hashlib server = socket.socket()
server.bind(('localhost', 9999))
server.listen() while True:
conn, addr = server.accept()
print('new conn:', addr)
while True:
data = conn.recv(1024)
data = data.decode()
if not data:
print('客户端已断开')
break
cmd, filename = data.split()
print(filename)
if os.path.isfile(filename):
f = open(filename, 'rb')
m = hashlib.md5()
file_size = os.stat(filename).st_size
print(file_size)
conn.send(str(file_size).encode('utf-8'))
conn.recv(1024)
for line in f:
m.update(line)
conn.send(line)
print('md5:', m.hexdigest())
f.close()
conn.send(m.hexdigest().encode('utf-8'))
客户端
# 客户端
import socket
import hashlib client = socket.socket()
client.connect(('localhost', 9999)) while True:
cmd = input('>>').strip()
if len(cmd) == 0:
continue
if cmd.startswith('get'):
client.send(cmd.encode('utf-8'))
cmd_res_size = client.recv(1024) # 接收长度
print('文件大小 %s' % (cmd_res_size.decode()))
client.send('准备完毕,开始发送数据'.encode('utf-8'))
recv_size = 0
file_name = cmd.split()[1]
f = open(file_name + '.new', 'wb')
m = hashlib.md5()
while recv_size < int(cmd_res_size.decode()):
if int(cmd_res_size.decode()) - recv_size > 1024:
size = 1024
else:
size = int(cmd_res_size.decode()) - recv_size
data = client.recv(size)
recv_size += len(data) # 读取每次接收的数据
f.write(data)
m.update(data)
#print(recv_size)
else:
file_md5 = m.hexdigest()
print('文件接收完毕', recv_size)
f.close()
recv_md5 = client.recv(1024)
print(file_md5, recv_md5.decode())
client.close()
SocketServer
SocketServer是Python的一个包,它在socket的基础上封装,可以更加简单的完成并发处理。
socketserver.TCPServer 继承BaseServer,完成TCP
socketserver.UDPServer 继承TCPServer,完成UDP
socketserver.UnixStreamServer 继承TCPServer,完成Unix的TCP
socketserver.UnixDatagramServer 继承UDPServer,完成Unix的UDP
使用socketserver的步骤:
1.创建一个请求处理类,并且这个类要继承BaseRequestHandler,并且需要重写父类中的Handle()方法。
2.实例化一个Server类,并且传递Server ip和1中创建的请求处理类给它。
3.server.handle_request()只处理一个请求 server.server_forever()处理多个请求,永久执行。
4.调用server_close()关闭它。
BaseServer中的常用方法,例子。
fileno() # 返回文件描述符
handle_request # 处理单个请求
serve_forever(poll_interval=0.5) # 一直运行直到收到shutdown()请求,每poll_interval检查一次,后调用service_actions()结束
service_actions() # 结束操作
shutdown() # 停止信号
server_close() # 清除server
address_family # 地址簇
RequestHandlerClass # 请求处理类
server_address # ip地址
socket # 同socket
self.allow_reuse_adress # 允许重用地址 socket中socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
socket_type # socket使用的协议类型
self.setup() # 请求来之前 self.handle() # 请求来时 self.finish() # 请求处理之后
作业
1.用户加密认证
2.允许多个用户登陆
3.每个用户有自己的家目录,不能互相访问
4.对用户进行磁盘配额,可用空间不同
5.允许用户在ftp_server上随意切换目录
6.允许用户查看当前目录文件
7.允许用户上传下载文件,保证文件一致性
8.传输过程显示进度条