python学习笔记_week8
一、Socket
当服务端传的东西大于客户端的最大值时怎么办?①改大buffer不行(有上限)②多传几次(用for循环必须要知道循环几次,所以不用for循环,用while)
服务端:
import os
import socket
server=socket.socket()
server.bind(("localhost",9999)) server.listen() while True:
conn,addr=server.accept()
print("new conn:",addr)
while True:
print("等待新指令")
data=conn.recv(700)
if not data :
print("客户端已断开")
break
print("执行指令:",data)
cmd_res=os.popen(data.decode()).read() #接受字符串,执行结果也是字符串
print("before send",len(cmd_res.encode()))
if len(cmd_res)==0:
cmd_res="cmd has no output..."
conn.send(str(len(cmd_res.encode())).encode("utf-8")) #先发大小给客户端
#字符串才能encode,cmd_res要encode 否则因为中文的原因长度会不相等
conn.send(cmd_res.encode("utf-8"))
print("send done")
server.close()
客户端:
import socket
client=socket.socket()
client.connect(("localhost",9999)) while True:
cmd=input(">>:").strip()
if len(cmd) == 0 :
continue
client.send(cmd.encode("utf-8"))
cmd_res_size=client.recv(700) #接收命定结果的长度
print("命令结果:",cmd_res_size)
received_size=0
received_data=b""
while received_size != int(cmd_res_size.decode()):
data=client.recv(700)
received_size+=len(data) #每次收到的有可能小于700,所以必须用len判断
# print(data.decode())
received_data+=data
else:
print("cmd res receive done",received_size)
# cmd_res=client.recv(700)
print(received_data.decode())
client.close()
socket粘包:两次send紧挨着,缓冲区将其打包成一次send,导致出错(用time.sleep()解决太low了,不要用),可以在两次send间插入一次交互,如在服务器端client_ack=conn.recv(700) #wait client to confirm,在客户端client.send("准备好接收了,loser可以发了".encode("utf-8")) PS:windows上粘包现象可能不会显示出来,但Linux一定会。
ftp server:1.读取文件名;2.检测文件是否存在;3.打开文件;4.检测文件大小;5.发送文件大小给客户端;6.等客户端确认;7.开始边读边发数据;8.发送md5;
服务端:
import hashlib
import os
import socket
server=socket.socket()
server.bind(("localhost",9999)) server.listen() while True:
conn,addr=server.accept()
print("new conn:",addr)
while True:
print("等待新指令")
data=conn.recv(700)
if not data :
print("客户端已断开")
break
cmd,filename=data.decode().split()
print(filename)
if os.path.isfile(filename): #判断文件是否存在
f=open(filename,"rb")
m=hashlib.md5()
file_size=os.stat(filename).st_size #文件大小
conn.send(str(file_size).encode()) #send file size
conn.recv(700) #wait for ack
for line in f:
m.update(line)
conn.send(line)
print("file md5",m.hexdigest()) #加上md5速度会慢下来
f.close()
conn.send(m.hexdigest().encode()) #send md5
print("send done")
server.close()
客户端:
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())
server_response=client.recv(700)
print("server response",server_response)
client.send(b"ready to recv file")
file_total_size=int(server_response.decode())
received_size=0
filename=cmd.split()[1]
f=open(filename+".new","wb")
m=hashlib.md5()
while received_size < file_total_size:
if file_total_size - received_size > 700:#要收不止一次
size=700
else: #最后一次了,剩多少就只收多少
size=file_total_size-received_size
#上面的判断是为了防止粘包。粘包只可能发生在最后一次
print("last receive:",size)
data=client.recv(size)
received_size+=len(data)
m.update(data)
f.write(data)
#print(file_total_size,received_size)
else:
new_file_md5=m.hexdigest()
print("file recv done",file_total_size,received_size)
f.close()
server_file_md5=client.recv(700)
print("server file md5:",server_file_md5)
print("client file md5:",new_file_md5)
client.close()
二、Socketsever
最主要的作用:并发处理。定义:简化网络任务服务器端的编写(对socket的再封装)
1.socketserver.
TCPServer;2.
socketserver.
UDPServer;3.
socketserver.
UnixStreamServer;4.
socketserver.
UnixDatagramServer
创建SocketServer的步骤:
1.你必须自己创建一个请求处理类,并且这个类要继承BaseRequestHandler,并且还要重写父类里的handle()。
2.你必须实例化TCPserver(其他也行),并且传递server ip 和你上面创建的请求处理类给这个TCPserver。
3.server.handle_request() #只处理一个请求(不常用)
server.handle_forever()#处理多个请求,永远执行
调用server_close()来关闭
跟客户端所有的交互都是在handle里完成的
import socketserver class MyTCPHandler(socketserver.BaseRequestHandler):
"""
The request handler class for our server. It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
""" def handle(self):
while True:
try:
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip() #每一个客户端的请求过来都会实例化MyTCPHandler
print("{} wrote:".format(self.client_address[0]))
print(self.data)
# if not self.data:#客户端断了
# print(self.client_address,"断开了")
# break
# just send back the same data, but upper-cased
self.request.send(self.data.upper())
except ConnectionResetError as e:
print("err:",e)
break if __name__ == "__main__":
HOST, PORT = "localhost", 9999 # Create the server, binding to localhost on port 9999
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) #把ip地址和类当做参数传给TCPServer,TCPServer就开始监听
# 和实例化MyTCPHandler,拿handle()与客户端交互 # Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
多并发:每来一个请求,开启一个新线程。
让你的socketserver并发起来, 必须选择使用以下一个多并发的类
class socketserver.
ForkingTCPServer
class socketserver.
ForkingUDPServer
class socketserver.
ThreadingTCPServer
class socketserver.
ThreadingUDPServer
ForkingTCPServer 多进程(效果与多线程一样)。(在windows上不能执行,没有fork)
socketserver.
BaseServer
(server_address, RequestHandlerClass) 主要有以下方法:1.fileno() 返回文件描述符(一般用不到)2.handle_request() 处理单个请求
3.serve_forever(poll_interval=0.5) 每0.5秒检测一下是否有shutdown信号 4.service_actions() 5.shutdown() 6.server_close() 7.address_family 8.RequestHandlerClass 9.server_address 10.socket 11.allow_reuse_address 12.request_queue_size 13.socket_type 14.timeout 15.finish_request()(self.setup(),self.handle(),self.finish())16.get_request() 17.handle_error(request, client_address) 18.handle_timeout() 19.process_request(request, client_address) 20.server_activate() 21.server_bind() 22.verify_request(request, client_address)
作业:开发一个支持多用户在线的FTP程序
要求:
- 用户加密认证
- 允许同时多用户登录
- 每个用户有自己的家目录 ,且只能访问自己的家目录
- 对用户进行磁盘配额,每个用户的可用空间不同
- 允许用户在ftp server上随意切换目录
- 允许用户查看当前目录下文件
- 允许上传和下载文件,保证文件一致性
- 文件传输过程中显示进度条
- 附加功能:支持文件的断点续传
服务器端:
import socketserver
import json,os
class MyTCPHandler(socketserver.BaseRequestHandler):
def put(self,*args):
'''接受客户端文件'''
cmd_dic=args[0]
filename=cmd_dic["filename"]
file_size=cmd_dic["size"]
if os.path.isfile(filename):
f=open(filename+".new","wb")
else:
f=open(filename,"wb")
self.request.send(b"200 ok") #返回客户端请求
receive_size=0
while receive_size<file_size:
data=self.request.recv(1024)
f.write(data)
receive_size+=len(data)
else:
print("file [%s] has uploaded..."%filename)
def handle(self):
while True:
try:
self.data = self.request.recv(1024).strip() #每一个客户端的请求过来都会实例化MyTCPHandler
print("{} wrote:".format(self.client_address[0]))
print(self.data)
cmd_dic=json.loads(self.data.decode())
action=cmd_dic["action"]
if hasattr(self,action):
func=getattr(self,action)
func(cmd_dic)
self.request.send(self.data.upper())
except ConnectionResetError as e:
print("err:",e)
break
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) #把ip地址和类当做参数传给TCPServer,TCPServer就开始监听
server.serve_forever()
客户端:
import socket
import json
import os
class Ftpclient(object):
def __init__(self):
self.client = socket.socket()
def help(self):
msg='''
ls
pwd
cd../..
get filename
put filename'''
print(msg)
def connect(self,ip,port):
self.client.connect((ip, port))
def interactive(self):
#self.authenticate() #用户登录
while True:
cmd=input(">>:")
if len(cmd)==0:continue
cmd_str=cmd.split()[0]#第一个值是指令
if hasattr(self,"cmd_%s"%cmd_str):
func=getattr(self,"cmd_%s"%cmd_str)
func(cmd)
else:
self.help()
def cmd_put(self,*args):
cmd_split=args[0].split()
if len(cmd_split) > 1:
filename=cmd_split[1]
if os.path.isfile(filename):
file_size=os.stat(filename).st_size
# msg_str="%s|%s"%(filename,file_size) #写死了,要考虑长远
msg_dic={
"action":"put",
"filename":filename,
"size":file_size,
"overriden":True
}
self.client.send(json.dumps(msg_dic).encode("utf-8"))
print("send",json.dumps(msg_dic).encode("utf-8"))
#防止粘包,等服务器确认
server_response=self.client.recv(1024)
f=open(filename,"rb")
for line in f:
self.client.send(line)
else:
print("file upload success...")
f.close()
else:
print(filename,"is not exist")
def cmd_get(self):
pass
ftp=Ftpclient()
ftp.connect("localhost",9999)
ftp.interactive()
python学习笔记_week8的更多相关文章
- python学习笔记整理——字典
python学习笔记整理 数据结构--字典 无序的 {键:值} 对集合 用于查询的方法 len(d) Return the number of items in the dictionary d. 返 ...
- VS2013中Python学习笔记[Django Web的第一个网页]
前言 前面我简单介绍了Python的Hello World.看到有人问我搞搞Python的Web,一时兴起,就来试试看. 第一篇 VS2013中Python学习笔记[环境搭建] 简单介绍Python环 ...
- python学习笔记之module && package
个人总结: import module,module就是文件名,导入那个python文件 import package,package就是一个文件夹,导入的文件夹下有一个__init__.py的文件, ...
- python学习笔记(六)文件夹遍历,异常处理
python学习笔记(六) 文件夹遍历 1.递归遍历 import os allfile = [] def dirList(path): filelist = os.listdir(path) for ...
- python学习笔记--Django入门四 管理站点--二
接上一节 python学习笔记--Django入门四 管理站点 设置字段可选 编辑Book模块在email字段上加上blank=True,指定email字段为可选,代码如下: class Autho ...
- python学习笔记--Django入门0 安装dangjo
经过这几天的折腾,经历了Django的各种报错,翻译的内容虽然不错,但是与实际的版本有差别,会出现各种奇葩的错误.现在终于找到了解决方法:查看英文原版内容:http://djangobook.com/ ...
- python学习笔记(一)元组,序列,字典
python学习笔记(一)元组,序列,字典
- Pythoner | 你像从前一样的Python学习笔记
Pythoner | 你像从前一样的Python学习笔记 Pythoner
- OpenCV之Python学习笔记
OpenCV之Python学习笔记 直都在用Python+OpenCV做一些算法的原型.本来想留下发布一些文章的,可是整理一下就有点无奈了,都是写零散不成系统的小片段.现在看 到一本国外的新书< ...
随机推荐
- WebService的几种验证方式
转 http://www.cnblogs.com/yoshiki1895/archive/2009/06/03/1495440.html WebService的几种验证方式 1.1 WebS ...
- kali 创建用户
root登录 adduser 用户名 passwd 用户名
- OpenTSDB(时序数据库官网)
官网地址:http://opentsdb.net/ 下载地址:https://github.com/OpenTSDB/opentsdb/releases ----------------------- ...
- HTTP协议之chunk介绍
http chunked 当客户端向服务器请求一个静态页面或者一张图片时,服务器可以很清楚的知道内容大小,然后通过Content-Length消息首部字段告诉客户端需要接收多少数据.但是如果是动态页面 ...
- 峰Redis学习(7)Redis 之Keys 通用操作
keys * 显示所有key 查找所有以s开头的key 用s* *代表任意字符 127.0.0.1:6379> keys s* 1) "set3" 2) "s ...
- appium的log详细分析
下面介绍appium日志的大概分析 //启动appium服务成功2017-03-24 11:22:49:218 - info: [Appium] Welcome to Appium v1.6.3201 ...
- SFINAE简单实例
SFINAE(Substitution failure is not an error),是C++11以来推出的一个重要概念,这里,只是简单举一个例子,可能会有人需要. // 添加 scalar nu ...
- ospf精确宣告地址
ospf的一点小问题 http://bbs.51cto.com/thread-881459-1.html 参照博客地址 network 172.20.1.0 0.0.0.3 area 0 networ ...
- IP地址基础
第一台计算机的名字 1946年2月14日,世界上第一台电脑ENIAC在美国宾夕法尼亚大学诞生,名叫ENIAC(爱尼阿克). 第一个网络的名字: arpanet 计算机网络定义: 物理位置不同.功能 ...
- python实现简单的登陆认证(含简单的文件操作)
需求: 让用户输入用户名密码 认证成功后显示欢迎信息 输错三次后退出程序 可以支持多个用户登录 (提示,通过列表存多个账户信息) 用户3次认证失败后,退出程序,再次启动程序尝试登录时,还是锁定状态(本 ...