【python自动化第八篇:网络编程】
一、拾遗
动态导入模块
目的是为了在导入模块的过程中将模块以字符的格式导入。
#!/usr/bin/env python # -*- coding:utf-8 -*- #Author:wanghui lib = __import__("lib.aa") #传统方法 lib.aa import importlib aa = importlib.import_module("lib.aa") #官方做法 print(aa.C().name)
断言
类似于if,实际的意思就是如多对应的条件为真就执行,不为真就报错。
assert type(1223) is str print("this is str") #等同于 if type(1223) is str: exit("must be int") print("this is str")
二、socket继续
1、浅谈server端的原理:
(1)、申明socket实例 server = socket.socket(AF.INET(地址簇),sock.SOCK_STREAM(代表tcp/ip))
地址簇分类:socket.AF_INET ----> ipv4 , socket.AF_INET6 -------> ipv6 , socket.AF_UNIX --------> 本地通信
协议类型:socket.SOCK_STREAM -----> tcp/ip , socket.SOCK_DGRAM -----> udp
(2)、绑定ip地址和端口 server.bind(("localhost",port))
(3)、开始监听:while True:
conn,addr = server.accept() 阻塞状态
(4)、循环接受信息
while True:
print("new conn",addr) #打印连接的客户端ip
data = conn.recv(1024) #接受数据大小(官方最大建议8192),这边接收数据的时候默认也是阻塞的
if not data: ***如果客户端 已断开的话conn.recv就会接收到空数据
break
conn.send(data.upper()) #发送数据
浅谈客户端的原理:
(1)、实例化一个socket client = socket.socket()
(2)、定义一个 链接的地址和端口 client.connect(("server_ip",port))
(3)、这会儿就可以发数据了 client.send(data)
(4)、接收数据 client.recv()
2、通过socket实现一个简单的ssh协议:
client端:
#!/usr/bin/env python # -*- coding:utf-8 -*- #Author:wanghui import socket,os client = socket.socket() #实例化socket client.connect(("localhost",9999)) #开始连接啦 while True: #发送数据啦 cmd = input("请输入命令>>:").strip() if len(cmd) == 0:continue #如果长度为0,就继续返回循环 client.send(cmd.encode('utf-8')) #发送命令(byte) cmd_res = client.recv(1024) #接收返回结果 print(cmd_res.decode()) #打印结果 client.close() #关闭连接
server端:
#!/usr/bin/env python # -*- coding:utf-8 -*- #Author:wanghui import socket,os server = socket.socket() #申明实例 server.bind(('localhost',9999)) #绑定ip和端口 server.listen() #等待连接 while True: conn,addr= server.accept() #监听 print("开始连接啦",addr) while True: #正式接受数据啦 print("开始新连接啦") data = conn.recv(1024) #定义传输大小 if not data: #如果客户端断开,那么就退出此次接收,重新回到监听状态 break print("开始 执行客户端命令",data) cmd_res = os.popen(data.decode()).read() #读取客户端命令(bytes转换成str) print("接受之前:",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd has no output ..." conn.send(cmd_res.encode('utf-8')) #向端发送数据,必须是bytes print("发送完成!") server.close() #断开连接
上面的基本连接模式会出现客户端发送的指令客户端不能一次性全部返回的问题,这样的话解决方式只能有:超时和确认缓冲区多少次发完的问题
然而多少次将缓冲区的内容发完呢?不晓得。。。所以只能通过在超时问题上做文章了
client:
#!/usr/bin/env python # -*- coding:utf-8 -*- #Author:wanghui import socket,os client = socket.socket() #实例化socket client.connect(("localhost",9999)) #开始连接啦 while True: #发送数据啦 cmd = input("请输入命令>>:").strip() if len(cmd) == 0:continue #如果长度为0,就继续返回循环 client.send(cmd.encode('utf-8')) #发送命令(byte) cmd_res_size = client.recv(1024) #接受返回数据的大小 print("接受的数据",cmd_res_size) #打印接收大小 received_size = 0 received_data = b'' while received_size < int(cmd_res_size.decode()): #只要不相等就一直收 data = client.recv(1024) received_size += len(data) #每次接收到的数据有可能小于1024,所以要用len判断 received_data += data #每次读取进来的data写入received_data # print(data.decode()) else: print("cmd rees received done",received_size) print(received_data.decode()) client.close() #关闭连接
server:
#!/usr/bin/env python # -*- coding:utf-8 -*- #Author:wanghui import socket,os server = socket.socket() #申明实例 server.bind(('localhost',9999)) #绑定ip和端口 server.listen() #等待连接 while True: conn,addr= server.accept() #监听 print("开始连接啦",addr) while True: #正式接受数据啦 print("等待新指令:") data = conn.recv(1024) #定义传输大小 if not data: #如果客户端断开,那么就退出此次接收,重新回到监听状态 print("客户端已经断开!") break print("执行命令",data) cmd_res = os.popen(data.decode()).read() #读取客户端命令(bytes转换成str) print("接受之前:",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd has no output ..." conn.send(str(len(cmd_res.encode())).encode('utf-8')) #先发大小给客户端 conn.send(cmd_res.encode('utf-8')) #向客户端发送数据,必须是bytes print("发送完成!") server.close() #断开连接
3.粘包问题
server连续调用send的时候缓冲区会将挨着的两次操作发给客户端,导致两次send的内容都同时发给了客户端,所以其中的一个方法就是在服务器端的send之间加入sleep时间,可以解决这个问题
server:
#!/usr/bin/env python # -*- coding:utf-8 -*- #Author:wanghui import socket,os,time server = socket.socket() #申明实例 server.bind(('localhost',9999)) #绑定ip和端口 server.listen() #等待连接 while True: conn,addr= server.accept() #监听 print("开始连接啦",addr) while True: #正式接受数据啦 print("等待新指令:") data = conn.recv(1024) #定义传输大小 if not data: #如果客户端断开,那么就退出此次接收,重新回到监听状态 print("客户端已经断开!") break print("执行命令",data) cmd_res = os.popen(data.decode()).read() #读取客户端命令(bytes转换成str) print("接受之前:",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd has no output ..." conn.send(str(len(cmd_res.encode())).encode('utf-8')) #先发大小给客户端 #time.sleep(0.5) #防止粘包的一种方法 client_ack= conn.recv(1024) #等待确认 print('ack from client:',client_ack) conn.send(cmd_res.encode('utf-8')) #向客户端发送数据,必须是bytes print("发送完成!") server.close() #断开连接
client:
#!/usr/bin/env python # -*- coding:utf-8 -*- #Author:wanghui import socket,os client = socket.socket() #实例化socket client.connect(("localhost",9999)) #开始连接啦 while True: #发送数据啦 cmd = input("请输入命令>>:").strip() if len(cmd) == 0:continue #如果长度为0,就继续返回循环 client.send(cmd.encode('utf-8')) #发送命令(byte) cmd_res_size = client.recv(1024) #接受返回数据的大小 print("接受的数据",cmd_res_size) #打印接收大小 client.send("准备确认啦".encode('utf-8')) received_size = 0 received_data = b'' while received_size < int(cmd_res_size.decode()): #只要不相等就一直收 data = client.recv(1024) received_size += len(data) #每次接收到的数据有可能小于1024,所以要用len判断 received_data += data #每次读取进来的data写入received_data # print(data.decode()) else: print("cmd rees received done",received_size) print(received_data.decode()) client.close() #关闭连接
4.简易的文件传输案例
server端的设计:
-
-
- 读取文件名
- 检测文件是否存在
- 打开文件
- 检测文件大小
- 发送文件大小和MD5值给客户端
- 等待客户端确认
- 开始边读取边发数据
-
#!/usr/bin/env python # -*- coding:utf-8 -*- #Author:wanghui import socket,os,time,hashlib server = socket.socket() #申明实例 server.bind(('localhost',9999)) #绑定ip和端口 server.listen() #等待连接 while True: conn,addr= server.accept() #监听 print("开始连接啦",addr) while True: #正式接受数据啦 print("等待新指令:") data = conn.recv(1024) #定义传输大小 if not data: #如果客户端断开,那么就退出此次接收,重新回到监听状态 print("客户端已经断开!") break cmd,filename = data.decode().split() #分割命令和文件名称 print(filename) #打印文件名 if os.path.isfile(filename): #判断是否为文件 f = open(filename,'rb') #打开文件 m = hashlib.md5() #定义MD5加密方式 file_size = os.stat(filename).st_size #确定文件大小 conn.send(str(file_size).encode()) #发送文件大小 conn.recv(1024) #等待确认 for line in f: #读取文件 m.update(line) #边读取边加密 conn.send(line) #边发送文件 print('file md5',m.hexdigest()) #打印文件的MD5 f.close() #关闭文件 conn.send(m.hexdigest().encode()) #发送MD5 print("发送完成!!") server.close()
client端:
#!/usr/bin/env python # -*- coding:utf-8 -*- #Author:wanghui import socket,os,hashlib client = socket.socket() #实例化socket client.connect(("localhost",9999)) #开始连接啦 while True: #发送数据啦 cmd = input(">>:").strip() if len(cmd) == 0:continue #如果长度为0,就继续返回循环 if cmd.startwith('get'): #定义开始 client.send(cmd.encode()) #客户端发送命令 server_resbonce = client.recv(1024) #接收服务器端数据 print("server resbonce:",server_resbonce) client.send("准备接收文件".encode()) file_total_size = int(server_resbonce.decode()) #定义接受的文件总大小 received_size=0 #初始接收文件 filename = cmd.split()[1] #获取文件名 f = open(filename + '.new','wb') #开始写文件 m = hashlib.md5() #定义MD5加密 while received_size < file_total_size: #如果接收的文件小于总文件大小,就执行如下 data = client.recv(1024) #定义接收文件 received_size+=len(data) #接受的文件大小变化 m.update(data) #生成客户端接受的MD5 f.write(data) #写入数据 #print(file_total_size,received_size) #打印文件接受大小和总大小 else: new_file_md5 = m.hexdigest() print("接受完了文件:",file_total_size,received_size) f.close() #关闭文件 server_md5 = client.recv(1024) #接收服务器端MD5 print(server_md5,new_file_md5) #对比MD5 client.close()