在實(shí)現(xiàn)發(fā)送文件功能之前我們先來理解下粘包的問題,下面有兩張圖,我覺得很清晰的就可以理解到了。
正常情況下發(fā)送文件
第一步: 客戶端把獲取到的文件總大小(size=65426)先放到緩沖區(qū),然后發(fā)送給服務(wù)端
第二步: 此時(shí)客戶端接收到的文件總大小就是65426
粘包的問題下發(fā)送文件
第一步: 客戶端把獲取到的文件總大小(size=65426)先放到緩沖區(qū)
第二步: 此時(shí)可能由于文件讀取太快,導(dǎo)致緩存區(qū)的內(nèi)容還沒有發(fā)送到服務(wù)端,客戶端就把讀取到的文件內(nèi)容(hello)也放到緩存區(qū);
第三步: 然后客戶端就把緩存區(qū)的全部?jī)?nèi)容都發(fā)送到服務(wù)端,那么客戶端本來第一次應(yīng)該接收到的數(shù)據(jù)室文件大小(size=65426),但實(shí)際接收到的數(shù)據(jù)確實(shí):65426+hello,那么這個(gè)流程就是粘包的問題;
解決粘包問題
如果出現(xiàn)粘包的問題,那么傳輸?shù)臄?shù)據(jù)就有問題了,如何解決這個(gè)問題呢?看下圖:
第一步: 客戶端把文件大小放到緩沖區(qū)
第二步: 放入緩沖區(qū)之后立刻陷入阻塞的狀態(tài),登臺(tái)服務(wù)端回復(fù)已收到文件大小,此時(shí)是不會(huì)再向服務(wù)端發(fā)送任何數(shù)據(jù)的
第三步: 緩存區(qū)的數(shù)據(jù)會(huì)發(fā)送到服務(wù)端
第四步: 服務(wù)端接收到客戶端發(fā)來的文件大小之后立刻回復(fù)客戶端,說我收到你發(fā)過來的文件大小了;
執(zhí)行結(jié)果如下
client.py
文件內(nèi)容
root@root:~/socket_file$ cat client.py
#!/usr/bin/env python
# _*_coding:utf-8 _*_
import socket
import os
# 創(chuàng)建一個(gè)socket對(duì)象
obj = socket.socket()
# 服務(wù)端的IP和端口
obj.connect(('127.0.0.1', 6542))
# 用os模塊獲取要傳送的文件總大小
size = os.stat("old_file.txt").st_size
# 把文件總大小發(fā)送給服務(wù)端
obj.sendall(bytes(str(size), encoding="utf-8"))
# 接受服務(wù)端返回的信息
obj.recv(1024)
# 以rb的模式打開一個(gè)要發(fā)送的文件d
with open("old_file.txt", "rb") as f:
# 循環(huán)文件的所有內(nèi)容
for line in f:
# 發(fā)送給服務(wù)端
obj.sendall(line)
# 關(guān)閉退出
obj.close()
service.py
文件內(nèi)容
root@root:~/socket_file$ cat service.py
#!/usr/bin/env python
# _*_coding:utf-8 _*_
import socket
# 創(chuàng)建一個(gè)socket對(duì)象
sk = socket.socket()
# 允許連接的IP和端口
sk.bind(('127.0.0.1', 6542))
# 最大連接數(shù)
sk.listen(5)
while True:
# 會(huì)一直阻塞,等待接收客戶端的請(qǐng)求,如果有客戶端連接會(huì)獲取兩個(gè)值,conn=創(chuàng)建的連接,address=客戶端的IP和端口
conn, address = sk.accept()
# 客戶端發(fā)送過來的文件大小
file_size = str(conn.recv(1024),encoding="utf-8")
# 給客戶端發(fā)送已經(jīng)收到文件大小
conn.sendall(bytes("ack", encoding="utf-8"))
# 文件大小轉(zhuǎn)換成int類型
total_size = int(file_size)
# 創(chuàng)建一個(gè)默認(rèn)的值
has_recv = 0
# 打開一個(gè)新文件,以wb模式打開
f = open('new_file.txt', 'wb')
# 進(jìn)入循環(huán)
while True:
# 如果傳送過來的大小等于文件總大小,那么就退出
if total_size == has_recv:
break
# 接受客戶端發(fā)送過來的內(nèi)容
data = conn.recv(1024)
# 寫入到文件當(dāng)中
f.write(data)
# 現(xiàn)在的大小加上客戶端發(fā)送過來的大小
has_recv += len(data)
# 關(guān)閉
f.close()
本文出自 “一盞燭光” 博客,謝絕轉(zhuǎn)載!
更多建議: