進擊のpython
網絡編程——文件處理
有了上次的模板之后啊
我們就應該可以對於文件進行操作了
對於文件的操作就是上傳和下載
而在上文上我也提到了,上文寫的是一個模板
既然是模板,就應該是拿過來稍加改動就能完成我的要求
好,分析一下吧
在剛開始的時候是這樣的
客戶端鍵入命令
order = input() # get a.txt
然后把這條命令的字節形式,編碼為gbk的形式傳給服務端
phone.send(order.encode("gbk"))
然后服務端開始接收,並進行解碼
order = phone.recv(1024)
filename = order.decode("gbk").split(" ")[1]
我拿到的就是想要下載的文件名
然后就進行報頭的設計,就可以直接用模板
順便說一下如何獲取文件的大小?
os.path.getsize(文件名)
發完了報頭就應該傳輸數據了是吧
因為字節才能在管道傳輸,所以我選擇的打開方式是rb
打開文件之后,別一口氣全讀出來,萬一很大呢?所以一行一行循環讀
with open(filename,"rb") as f:
for i in f.read():
connet.send(i)
好了,這面發送了,你那面就該接收了吧
表面上是我把包丟給你了,實際上是你在原地新建了一個文件
然后把數據寫入了
那我就在本地寫個文件被,然后將收到的東西都寫進來,怎么寫?還是字節啊wb
with open(filename,"wb") as f:
recv_size = 0
while recv_size<re_len:
res = phone.recv(1024)
f.write(res)
recv_size+=len(res)
是吧!大約是這個套路吧
那整合一下吧
# 客戶端
import json
import socket
# 買手機
import struct
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 撥號
phone.connect(("127.0.0.1", 8080))
# 發收信息
while 1:
order = input(">>>")
phone.send(order.encode("gbk"))
res = phone.recv(4)
re_msg = struct.unpack("i", res)[0]
msg = phone.recv(re_msg)
msg = msg.decode("gbk")
msg = json.loads(msg)
re_len = msg["file_size"]
re_size = 0
r = b"" # 我傳過來的是字節模式
with open("b", "wb") as f:
recv_size = 0
while recv_size < re_len:
res = phone.recv(1024)
f.write(res)
recv_size += len(res)
print(f'打印成功')
connet.close()
# 關閉
phone.close()
# 服務端
import json
import os
import socket
import struct
import subprocess
# 買手機
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 綁定手機卡
phone.bind(("127.0.0.1", 8080))
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 開機
phone.listen(5)
# 等電話
connet, client_addr = phone.accept()
# 收發消息
while 1:
try:
order = connet.recv(1024)
filename = order.decode("gbk").split(" ")[1]
# obj = subprocess.Popen(order.decode("gbk"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# stdout = obj.stdout.read()
# stderr = obj.stderr.read()
dic = {
"file_name": filename,
"MD5": "XXXXXXXXXXXXXXXX",
'file_size': os.path.getsize(filename)
}
head = json.dumps(dic)
head = head.encode("gbk")
res = struct.pack("i", len(head))
connet.send(res)
connet.send(head)
with open(filename) as f:
for line in f.read():
print(line)
connet.send(bytes(line, encoding="gbk"))
except ConnectionResetError:
break
# 掛電話
connet.close()
# 關機
phone.close()
但是你不覺得我寫的太亂了嗎??
所以,我們用函數寫!
首先前面的全局變量是不適合放進函數里的是吧
而下面的代碼塊是不是都是執行語句
所以我們可以把這些代碼整體進行縮進
def run():
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 綁定手機卡
phone.bind(("127.0.0.1", 8080))
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 開機
phone.listen(5)
# 等電話
connet, client_addr = phone.accept()
# 收發消息
while 1:
try:
order = connet.recv(1024)
filename = order.decode("gbk").split(" ")[1]
# obj = subprocess.Popen(order.decode("gbk"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# stdout = obj.stdout.read()
# stderr = obj.stderr.read()
dic = {
"file_name": filename,
"MD5": "XXXXXXXXXXXXXXXX",
'file_size': os.path.getsize(filename)
}
head = json.dumps(dic)
head = head.encode("gbk")
res = struct.pack("i", len(head))
connet.send(res)
connet.send(head)
with open(filename) as f:
for line in f.read():
print(line)
connet.send(bytes(line, encoding="gbk"))
except ConnectionResetError:
break
# 掛電話
connet.close()
# 關機
phone.close()
然后我們再來看,是不是中間有個下載的功能啊
然后我們默認傳過來的命令是get是吧
所以這個是不是也可以封裝成一個函數?
封裝完之后是不是應該傳參數啊
def down(connet, filename):
dic = {
"file_name": filename,
"MD5": "XXXXXXXXXXXXXXXX",
'file_size': os.path.getsize(filename)
}
head = json.dumps(dic)
head = head.encode("gbk")
res = struct.pack("i", len(head))
connet.send(res)
connet.send(head)
with open(filename) as f:
for line in f.read():
print(line)
connet.send(bytes(line, encoding="gbk"))
最后是不是應該再加個點睛之筆啊!
if __name__ == '__main__':
run()
那客戶端是不是也可以封裝函數啊
遠離其實都差不多,就不再贅述了,直接放代碼
def get(phone):
res = phone.recv(4)
re_msg = struct.unpack("i", res)[0]
msg = phone.recv(re_msg)
msg = msg.decode("gbk")
msg = json.loads(msg)
re_len = msg["file_size"]
with open("b", "wb") as f:
recv_size = 0
while recv_size < re_len:
res = phone.recv(1024)
f.write(res)
recv_size += len(res)
print(f'打印成功')
def run():
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(("127.0.0.1", 8080))
while 1:
order = input(">>>")
phone.send(order.encode("gbk"))
order = order.split()
if order[0] == "get":
get(phone)
phone.close()
最后一樣,注入靈魂
if __name__ == '__main__':
run()
最后整理一下:
# 客戶端
import json
import socket
# 買手機
import struct
def get(phone):
res = phone.recv(4)
re_msg = struct.unpack("i", res)[0]
msg = phone.recv(re_msg)
msg = msg.decode("gbk")
msg = json.loads(msg)
re_len = msg["file_size"]
with open("b.mp4", "wb") as f:
recv_size = 0
while recv_size < re_len:
res = phone.recv(1024)
f.write(res)
recv_size += len(res)
print(f'打印成功')
def put(phone):
pass
def run():
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(("127.0.0.1", 8080))
while 1:
order = input(">>>")
phone.send(order.encode("gbk"))
order = order.split()
if order[0] == "get":
get(phone)
elif order[0] == "put":
put(phone)
phone.close()
if __name__ == '__main__':
run()
# 服務端
import json
import os
import socket
import struct
import subprocess
def down(connet, filename):
dic = {
"file_name": filename,
"MD5": "XXXXXXXXXXXXXXXX",
'file_size': os.path.getsize(filename)
}
head = json.dumps(dic)
head = head.encode("gbk")
res = struct.pack("i", len(head))
print(len(head))
connet.send(res)
connet.send(head)
with open(filename, "rb") as f:
for line in f.readlines():
connet.send(line)
def put(connet, filename):
pass
def run():
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 綁定手機卡
phone.bind(("127.0.0.1", 8080))
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 開機
phone.listen(5)
# 等電話
connet, client_addr = phone.accept()
# 收發消息
while 1:
try:
order = connet.recv(1024)
filename = order.decode("gbk").split(" ")[1]
# obj = subprocess.Popen(order.decode("gbk"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# stdout = obj.stdout.read()
# stderr = obj.stderr.read()
if order.decode("gbk").split(" ")[0] == "get":
down(connet, filename)
elif order.decode("gbk").split(" ")[0] == "put":
put(connet, filename)
except ConnectionResetError:
break
# 掛電話
connet.close()
# 關機
phone.close()
if __name__ == '__main__':
run()
建議你們可以自己把另一個功能補上!
但是吧,其實這么寫還不太好!
因為我們學過面向對象
所以我們也可以用面向對象來處理這個問題!
關於面向對象,就是將所有的方法進行打散,然后把每個方法都進行封裝
最后自己調用自己寫的方法就行
這一段就直接放代碼了(關注一下反射的使用)
# 服務端
import socket
import struct
import json
import subprocess
import os
class MYTCPServer:
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
allow_reuse_address = False
max_packet_size = 8192
coding='utf-8'
request_queue_size = 5
server_dir='file_upload'
def __init__(self, server_address, bind_and_activate=True):
"""Constructor. May be extended, do not override."""
self.server_address=server_address
self.socket = socket.socket(self.address_family,
self.socket_type)
if bind_and_activate:
try:
self.server_bind()
self.server_activate()
except:
self.server_close()
raise
def server_bind(self):
"""Called by constructor to bind the socket.
"""
if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_address)
self.server_address = self.socket.getsockname()
def server_activate(self):
"""Called by constructor to activate the server.
"""
self.socket.listen(self.request_queue_size)
def server_close(self):
"""Called to clean-up the server.
"""
self.socket.close()
def get_request(self):
"""Get the request and client address from the socket.
"""
return self.socket.accept()
def close_request(self, request):
"""Called to clean up an individual request."""
request.close()
def run(self):
while True:
self.conn,self.client_addr=self.get_request()
print('from client ',self.client_addr)
while True:
try:
head_struct = self.conn.recv(4)
if not head_struct:break
head_len = struct.unpack('i', head_struct)[0]
head_json = self.conn.recv(head_len).decode(self.coding)
head_dic = json.loads(head_json)
print(head_dic)
#head_dic={'cmd':'put','filename':'a.txt','filesize':123123}
cmd=head_dic['cmd']
if hasattr(self,cmd):
func=getattr(self,cmd)
func(head_dic)
except Exception:
break
def put(self,args):
file_path=os.path.normpath(os.path.join(
self.server_dir,
args['filename']
))
filesize=args['filesize']
recv_size=0
print('----->',file_path)
with open(file_path,'wb') as f:
while recv_size < filesize:
recv_data=self.conn.recv(self.max_packet_size)
f.write(recv_data)
recv_size+=len(recv_data)
print('recvsize:%s filesize:%s' %(recv_size,filesize))
tcpserver1=MYTCPServer(('127.0.0.1',8080))
tcpserver1.run()
# 客戶端
import socket
import struct
import json
import os
class MYTCPClient:
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
allow_reuse_address = False
max_packet_size = 8192
coding='utf-8'
request_queue_size = 5
def __init__(self, server_address, connect=True):
self.server_address=server_address
self.socket = socket.socket(self.address_family,
self.socket_type)
if connect:
try:
self.client_connect()
except:
self.client_close()
raise
def client_connect(self):
self.socket.connect(self.server_address)
def client_close(self):
self.socket.close()
def run(self):
while True:
inp=input(">>: ").strip()
if not inp:continue
l=inp.split()
cmd=l[0]
if hasattr(self,cmd):
func=getattr(self,cmd)
func(l)
def put(self,args):
cmd=args[0]
filename=args[1]
if not os.path.isfile(filename):
print('file:%s is not exists' %filename)
return
else:
filesize=os.path.getsize(filename)
head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize}
print(head_dic)
head_json=json.dumps(head_dic)
head_json_bytes=bytes(head_json,encoding=self.coding)
head_struct=struct.pack('i',len(head_json_bytes))
self.socket.send(head_struct)
self.socket.send(head_json_bytes)
send_size=0
with open(filename,'rb') as f:
for line in f:
self.socket.send(line)
send_size+=len(line)
print(send_size)
else:
print('upload successful')
client=MYTCPClient(('127.0.0.1',8080))
client.run()
好,最后這一種就是面向對象的寫法了
至此,TCP就完事了,接下來就是UDP的天下了!