Python網(wǎng)絡(luò)編程之基于socket實(shí)現(xiàn)文件上傳

2018-06-08 17:32 更新

粘包

在實(shí)現(xiàn)發(fā)送文件功能之前我們先來理解下粘包的問題,下面有兩張圖,我覺得很清晰的就可以理解到了。

  • 正常情況下發(fā)送文件

socket-sticky-package-01

  1. 第一步: 客戶端把獲取到的文件總大小(size=65426)先放到緩沖區(qū),然后發(fā)送給服務(wù)端

  2. 第二步: 此時(shí)客戶端接收到的文件總大小就是65426

  • 粘包的問題下發(fā)送文件

socket-sticky-package-02

  1. 第一步: 客戶端把獲取到的文件總大小(size=65426)先放到緩沖區(qū)

  2. 第二步: 此時(shí)可能由于文件讀取太快,導(dǎo)致緩存區(qū)的內(nèi)容還沒有發(fā)送到服務(wù)端,客戶端就把讀取到的文件內(nèi)容(hello)也放到緩存區(qū);

  3. 第三步: 然后客戶端就把緩存區(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è)問題呢?看下圖:

socket-sticky-package-03

  1. 第一步: 客戶端把文件大小放到緩沖區(qū)

  2. 第二步: 放入緩沖區(qū)之后立刻陷入阻塞的狀態(tài),登臺(tái)服務(wù)端回復(fù)已收到文件大小,此時(shí)是不會(huì)再向服務(wù)端發(fā)送任何數(shù)據(jù)的

  3. 第三步: 緩存區(qū)的數(shù)據(jù)會(huì)發(fā)送到服務(wù)端

  4. 第四步: 服務(wù)端接收到客戶端發(fā)來的文件大小之后立刻回復(fù)客戶端,說我收到你發(fā)過來的文件大小了;

文件上傳

執(zhí)行結(jié)果如下

socket-03

client.py文件內(nèi)容

  1. root@root:~/socket_file$ cat client.py

  2. #!/usr/bin/env python

  3. # _*_coding:utf-8 _*_

  4. import socket

  5. import os

  6. # 創(chuàng)建一個(gè)socket對(duì)象

  7. obj = socket.socket()

  8. # 服務(wù)端的IP和端口

  9. obj.connect(('127.0.0.1', 6542))

  10. # 用os模塊獲取要傳送的文件總大小

  11. size = os.stat("old_file.txt").st_size

  12. # 把文件總大小發(fā)送給服務(wù)端

  13. obj.sendall(bytes(str(size), encoding="utf-8"))

  14. # 接受服務(wù)端返回的信息

  15. obj.recv(1024)

  16. # 以rb的模式打開一個(gè)要發(fā)送的文件d

  17. with open("old_file.txt", "rb") as f:

  18.    # 循環(huán)文件的所有內(nèi)容

  19.    for line in f:

  20.        # 發(fā)送給服務(wù)端

  21.        obj.sendall(line)

  22. # 關(guān)閉退出

  23. obj.close()

service.py文件內(nèi)容

  1. root@root:~/socket_file$ cat service.py

  2. #!/usr/bin/env python

  3. # _*_coding:utf-8 _*_

  4. import socket

  5. # 創(chuàng)建一個(gè)socket對(duì)象

  6. sk = socket.socket()

  7. # 允許連接的IP和端口

  8. sk.bind(('127.0.0.1', 6542))

  9. # 最大連接數(shù)

  10. sk.listen(5)

  11. while True:

  12.    # 會(huì)一直阻塞,等待接收客戶端的請(qǐng)求,如果有客戶端連接會(huì)獲取兩個(gè)值,conn=創(chuàng)建的連接,address=客戶端的IP和端口

  13.    conn, address = sk.accept()

  14.    # 客戶端發(fā)送過來的文件大小

  15.    file_size = str(conn.recv(1024),encoding="utf-8")

  16.    # 給客戶端發(fā)送已經(jīng)收到文件大小

  17.    conn.sendall(bytes("ack", encoding="utf-8"))

  18.    # 文件大小轉(zhuǎn)換成int類型

  19.    total_size = int(file_size)

  20.    # 創(chuàng)建一個(gè)默認(rèn)的值

  21.    has_recv = 0

  22.    # 打開一個(gè)新文件,以wb模式打開

  23.    f = open('new_file.txt', 'wb')

  24.    # 進(jìn)入循環(huán)

  25.    while True:

  26.        # 如果傳送過來的大小等于文件總大小,那么就退出

  27.        if total_size == has_recv:

  28.            break

  29.        # 接受客戶端發(fā)送過來的內(nèi)容

  30.        data = conn.recv(1024)

  31.        # 寫入到文件當(dāng)中

  32.        f.write(data)

  33.        # 現(xiàn)在的大小加上客戶端發(fā)送過來的大小

  34.        has_recv += len(data)

  35.    # 關(guān)閉

  36.    f.close()


本文出自 “一盞燭光” 博客,謝絕轉(zhuǎn)載!

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)