python之网络编程
Posted 燃烧着
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python之网络编程相关的知识,希望对你有一定的参考价值。
一、什么是网络编程
网络编程简单来说就是对信息的发送到接收,中间传输为物理线路的作用。
在我们没有学过网络编程之前,在应用上写的代码都是只能自己提供自己使用,不能与其他人交互。那么如何实现相互通信呢?
二、数据之间的相互通信
首先先介绍一下套接字模块socket
套接字是一种通信机制,凭借这种机制,客户/服务器系统的开发工作既可以在本地单机上进行,也可以跨网络进行,Linux所提供的功能(如打印服务,ftp等)通常都是通过套接字来进行通信的,套接字的创建和使用与管道是有区别的,因为套接字明确地将客户和服务器区分出来,套接字可以实现将多个客户连接到一个服务器。
基于python提供了socket模块,我们可以应用该模块创建一个简单的可以与它人互动的程序
1 服务端 2 import socket #导入socket套接字模块 3 server = socket.socket() #创建一个socket对象 4 server.bind(("192.168.13.132",8000)) #绑定IP与端口 5 server.listen(3) #规定只允许不超过三人链接服务端 6 conn,addr = server.accept() #创建媒介与客户端地址 7 data = conn.recv(1024) #新建一个变量,通过媒介接收客户端传输过来的字节并限制不超过1024 8 print(data) #打印客户端传来的讯息 9 conn.send(b"123456") #返回给客户端的字节 10 conn.close() #与服务器断开链接 11 server.close() # 关闭服务器的服务
1 # 客户端 2 import socket #导入socket模块 3 client = socket.socket() #创建一个socket对象 4 client.connect(("192.168.13.132",8000)) #与服务端建立链接 5 client.send(b"456789") #向服务断传输讯息(字节型) 6 data =client.recv(1024) #接服务端传来的讯息 不超过1024字节 7 print(data) # 打印 8 client.close() #关闭客户端
三、网络编程的简单应用
模拟ssh
在没有介绍代码之前需要引入subprocess模块,subprocess是Python 2.4中新增的一个模块,它允许你生成新的进程,连接到它们的 input/output/error 管道,并获取它们的返回(状态)码
我们需要学会一下代码就足够了
1 import subprocess 2 res = subprocess.Popen("ipconfig", 3 shell= True, 4 stderr=subprocess.PIPE, 5 stdout=subprocess.PIPE 6 7 ) 8 print(res.stdout.read().decode("gbk"))
建立服务端
1 # 服务端 2 import socket 3 import subprocess 4 server = socket.socket() 5 server.bind(("192.168.13.132",8000)) 6 server.listen(3) 7 print("建立链接中。。。") 8 conn,addr = server.accept() 9 print("等待响应中。。。") 10 while True: 11 cmd = conn.recv(1024).decode("utf-8") 12 res = subprocess.Popen(cmd, 13 shell= True, 14 stderr=subprocess.PIPE, 15 stdout=subprocess.PIPE 16 ) 17 result = res.stdout.read() 18 print("响应长度为%s" % len(result)) 19 conn.send(result.decode("gbk").encode("utf-8"))
建立客户端
# 客户端 import socket client =socket.socket() client.connect(("192.168.13.132",8000)) while True: user_input = input("请输入dose命令") client.send(user_input.encode("utf-8")) result = client.recv(1024).decode("utf-8") print(result)
文件的上传与下载
建立服务端
1 # 服务端 2 import socketserver,os,struct,hashlib,json 3 class Myserver(socketserver.BaseRequestHandler): 4 def handle(self): 5 print("等待响应...") 6 recevie_order = self.request.recv(1024).decode("utf-8") #接收一个客户端发来的指令 7 if recevie_order == "upload": #判断当指令为upload时 8 recevie_filename = self.request.recv(1024).decode("utf-8") # 接收文件名 9 currentfile_path = os.path.dirname(__file__) # 获取当前路径前两位 10 abs_path = os.path.join(currentfile_path, recevie_filename) # 拼接路径 11 maxbytes = self.request.recv(4) # 接收客户端传来的报头信息 12 md5 = hashlib.md5() #创建一个摘要对象 13 # 防止黏包 14 long = struct.unpack("i", maxbytes)[0] #解包获得文件的长度 15 num = 0 #设置一个初始长度 16 data = b"" #文件的字节 17 while num < long: #当长度大于文件的长度时 退出循环 18 receive = self.request.recv(1024) #循环接收 客户端传来的文件内容 19 data += receive #以字节的形式进行字符串拼接 20 num += len(receive) #累加 21 result = data #最后获得一个字节形式的文件内容 22 with open(abs_path, "wb") as f: #创建一个文件,以字节的形式写入 23 f.write(result) #将字节形式的内容写入到创建好的文件中 24 md5.update(result) # 进行摘要处理,目的是校验文件的完整性 25 print("上传成功") 26 check = self.request.recv(1024).decode("utf-8") #接收客户端传来的摘要信息,并解码 27 print(check) 28 if check == md5.hexdigest(): #判断写入文件时进行的摘要与接收客户端传来的摘要是否一致 29 self.request.send("文件完整".encode("utf-8")) 30 else: 31 self.request.send("文件被修改".encode("utf-8")) 32 33 elif recevie_order == "download": #判断当指令为upload时 34 filename = os.listdir(os.path.dirname(__file__)) #获取当前的路径的前两位的路径下的文件 35 for item in filename: #对这个文件列表进行遍历 36 abs_path = os.path.join(os.path.dirname(__file__), item) #在对文件的路径进行拼接 37 if os.path.isdir(abs_path): #判断是否是文件夹 38 filename.remove(item) #如果是则删除 39 fileshow = json.dumps(filename) #将列表序列化 40 print(fileshow) 41 self.request.send(fileshow.encode("utf-8")) #向客户端发送这个序列化后的列表 42 filename = self.request.recv(1024).decode("utf-8") #接收文件名 43 currentfile_path = os.path.dirname(__file__) #获取当前服务端所在的路径 44 abs_path = os.path.join(currentfile_path,filename) #拼接路径,目的是为了获取服务端所在文件夹下的文件,即客户端要下载的文件 45 md5 = hashlib.md5() #创建一个摘要对象 46 with open(abs_path, "rb") as f: #以rb的形式打开文件 47 result = f.read() #读取字节形式的文件 48 md5.update(result) #进行摘要处理 49 self.request.send(struct.pack("i", len(result))) #向客户端发送一个报头,目的是解决黏包现象 50 self.request.send(result) #向客户端发送字节形式的文件 51 print("传输成功") 52 check = self.request.recv(1024).decode("utf-8") #接收客户端传来的摘要信息,并解码 53 print(check) 54 if check == md5.hexdigest(): 55 self.request.send("文件完整".encode("utf-8")) 56 else: 57 self.request.send("文件被修改".encode("utf-8")) 58 59 server = socketserver.ThreadingTCPServer(("192.168.13.132",8000),Myserver) 60 server.serve_forever()
建立客户端
1 # 客户端 2 import socket,os,hashlib,struct,json 3 4 client = socket.socket() 5 client.connect(("192.168.13.132",8000)) 6 def upload(): #创建一个函数表示上传文件相关 7 client.send(os.path.basename(user_input_list[1]).encode("utf-8")) #向客户端发送文件名 8 md5 = hashlib.md5() # 校验 9 with open(user_input_list[1], "rb") as f: 10 result = f.read() 11 md5.update(result) 12 client.send(struct.pack("i", len(result))) 13 client.send(result) 14 print("传输成功") 15 check = md5.hexdigest() 16 print(check) 17 client.send(check.encode("utf-8")) 18 check_result = client.recv(1024).decode("utf-8") 19 print(check_result) 20 def download(): 21 client.send(user_input_list[1].encode("utf-8")) 22 maxbytes = client.recv(4) # 最大接收不超过4字节 23 md5 = hashlib.md5() 24 long = struct.unpack("i", maxbytes)[0] 25 num = 0 26 data = b"" 27 while num < long: 28 receive = client.recv(1024) 29 data += receive 30 num += len(receive) 31 result = data 32 abs_path = os.path.join(os.path.dirname(__file__), user_input_list[1]) 33 with open(abs_path, "wb") as f: 34 f.write(result) 35 md5.update(result) 36 print("下载成功") 37 check = md5.hexdigest() 38 print(check) 39 client.send(check.encode("utf-8")) 40 check_result = client.recv(1024).decode("utf-8") 41 print(check_result) 42 menu_list =["上传文件","下载文件"] 43 for i,item in enumerate(menu_list,1): 44 print(i,item) 45 user_choice = int(input("请输入序号选择功能")) 46 if user_choice == 1: 47 client.send("upload".encode("utf-8")) #向服务端发送该进行什么操作的指令 48 user_input = input("请输入指令进行操作(upload/路径)") 49 user_input_list = user_input.strip().split("/") 50 upload() 51 elif user_choice== 2: 52 client.send("download".encode("utf-8")) #当发送的指令为download时我们应该获得一个文件列表,方便与客户端知道该下载什么文件 53 fileshow = client.recv(1024).decode("utf-8") #接收到一个文件列表 54 file_list = json.loads(fileshow) #再将列表反序列化 55 print("可下载的文件列表为:") 56 print(file_list) #展示列表 57 user_input = input("请输入指令进行操作(download/文件)") 58 user_input_list = user_input.strip().split("/") 59 download()
四、黏包现象
黏包现象就是同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种显现就称之为黏包。
只有tcp协议会出现黏包现象,dcp协议不会出现。
解决黏包的理想方案:
1 # 服务端 2 import socket #套接字模块 3 import subprocess 4 import struct 5 server = socket.socket() 6 server.bind(("192.168.13.132",8000)) 7 server.listen(3) 8 print("建立链接中。。。") 9 conn,addr = server.accept() 10 print("等待响应中。。。") 11 while True: 12 cmd = conn.recv(1024).decode("utf-8") 13 res = subprocess.Popen(cmd, 14 shell= True, 15 stderr=subprocess.PIPE, 16 stdout=subprocess.PIPE 17 ) 18 result = res.stdout.read() 19 error = res.stderr.read() 20 print("响应长度为%s" % len(result)) 21 print("错误信息为%s" % error.decode("gbk")) 22 if error: 23 back_message = error 24 else: 25 back_message = result 26 conn.send(struct.pack("i", len(back_message))) # 构建报头 27 conn.send(back_message) # 发送数据
1 # 客户端 2 import socket 3 import struct 4 client =socket.socket() 5 client.connect(("192.168.13.132",8000)) 6 while True: 7 user_input = input("请输入dose命令") 8 client.send(user_input.encode("utf-8")) 9 maxbytes = client.recv(4) 10 long = struct.unpack("i",maxbytes)[0] 11 num = 0 12 data = b"" 13 while num < long: 14 receive = client.recv(1024) 15 data +=receive 16 num +=len(receive) 17 print(data.decode("gbk"))
五、osi七层模型
osi七层模型一般指开放系统互连参考模型
概要:
osi7层模型
·应用层 :使用软件
·表示层 :看到视频图片等 }产生数据(传输令牌)
·会话层 :保持你的登陆或链接状态
· 5 - 7
·传输层 :tcp / udp
·网络层 :找Ip
·4 - 5
·数据链路层 :MAC地址 }物理层
· 物理层 :
TCP三次握手/四次挥手
socket客户端向服务端发起连接请求:三次握手 ·connect
客户端和服务端断开连接:四次握手 ·close
补充!
当客户端主动断开链接时,会向服务端抛出异常,或发送空
以上是关于python之网络编程的主要内容,如果未能解决你的问题,请参考以下文章
JUC并发编程 共享模式之工具 JUC CountdownLatch(倒计时锁) -- CountdownLatch应用(等待多个线程准备完毕( 可以覆盖上次的打印内)等待多个远程调用结束)(代码片段