最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python實(shí)現(xiàn)基于UDP的文件傳輸?shù)娜^(guò)程

 更新時(shí)間:2025年12月10日 10:11:22   作者:剛哥的進(jìn)化路  
本文介紹了UDP文件傳輸協(xié)議的設(shè)計(jì)思路,包括分塊傳輸、包標(biāo)識(shí)、確認(rèn)重傳等關(guān)鍵步驟,通過(guò)客戶端和服務(wù)端代碼實(shí)現(xiàn)文件傳輸,并提供了基礎(chǔ)版使用步驟、關(guān)鍵優(yōu)化和避坑指南,以確保文件傳輸?shù)姆€(wěn)定性和可靠性,需要的朋友可以參考下

UDP本身是無(wú)連接、不可靠的傳輸協(xié)議,直接用來(lái)傳文件容易出現(xiàn)丟包、亂序問(wèn)題,但通過(guò)“分塊傳輸+校驗(yàn)+重傳”的設(shè)計(jì),依然能實(shí)現(xiàn)穩(wěn)定的文件傳輸。

一、UDP文件傳輸?shù)暮诵脑O(shè)計(jì)思路

UDP不保證數(shù)據(jù)送達(dá),所以要先解決三個(gè)關(guān)鍵問(wèn)題:

  1. 分塊傳輸:將大文件拆分成固定大小的數(shù)據(jù)包(如1024字節(jié)),避免單次發(fā)送數(shù)據(jù)過(guò)大導(dǎo)致丟包;
  2. 包標(biāo)識(shí):每個(gè)數(shù)據(jù)包添加“序號(hào)+總包數(shù)+校驗(yàn)位”,服務(wù)端接收后能校驗(yàn)完整性、排序重組;
  3. 確認(rèn)重傳:服務(wù)端接收每個(gè)數(shù)據(jù)包后返回確認(rèn)(ACK),客戶端未收到ACK則重傳該數(shù)據(jù)包,保證可靠性。

核心流程:

  • 客戶端:讀取文件→分塊→加標(biāo)識(shí)→發(fā)送數(shù)據(jù)包→等待ACK→重傳失敗包→發(fā)送結(jié)束標(biāo)識(shí);
  • 服務(wù)端:接收數(shù)據(jù)包→校驗(yàn)→排序→重組→保存文件→返回ACK。

二、完整實(shí)現(xiàn)代碼

1. 服務(wù)端代碼(接收文件)

import socket
import os

# 配置參數(shù)
UDP_IP = ""  # 監(jiān)聽(tīng)所有本機(jī)IP
UDP_PORT = 9999
BUFFER_SIZE = 1024  # 數(shù)據(jù)包大?。ㄐ韬涂蛻舳艘恢拢?
SAVE_DIR = "received_files"  # 文件保存目錄

# 創(chuàng)建保存目錄
if not os.path.exists(SAVE_DIR):
    os.makedirs(SAVE_DIR)

def udp_file_server():
    # 1. 創(chuàng)建UDP Socket
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server_socket.bind((UDP_IP, UDP_PORT))
    # 允許端口復(fù)用,避免重啟報(bào)錯(cuò)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    print(f"UDP文件服務(wù)端已啟動(dòng),監(jiān)聽(tīng)端口 {UDP_PORT}...")

    # 初始化接收狀態(tài)
    file_data = {}  # 存儲(chǔ)接收的數(shù)據(jù)包 {序號(hào): 數(shù)據(jù)}
    total_packets = 0  # 總包數(shù)
    file_name = ""  # 文件名
    client_addr = None  # 客戶端地址

    try:
        while True:
            # 2. 接收數(shù)據(jù)包(阻塞)
            data, client_addr = server_socket.recvfrom(BUFFER_SIZE + 32)  # 預(yù)留標(biāo)識(shí)位空間
            if not data:
                continue

            # 解析數(shù)據(jù)包標(biāo)識(shí):"文件名|總包數(shù)|當(dāng)前序號(hào)|數(shù)據(jù)"
            try:
                # 分割標(biāo)識(shí)和數(shù)據(jù)(用|分隔,最后一段是文件數(shù)據(jù))
                parts = data.decode('utf-8', errors='ignore').split('|', 3)
                if len(parts) != 4:
                    # 不是文件數(shù)據(jù)包,可能是結(jié)束標(biāo)識(shí)
                    if data.decode('utf-8') == "TRANSFER_FINISH":
                        print("客戶端發(fā)送結(jié)束標(biāo)識(shí),開(kāi)始重組文件...")
                        break
                    continue

                file_name, total_packets_str, packet_num_str, file_chunk = parts
                total_packets = int(total_packets_str)
                packet_num = int(packet_num_str)
            except Exception as e:
                print(f"數(shù)據(jù)包解析失?。簕e}")
                # 返回錯(cuò)誤ACK
                server_socket.sendto(f"ERR|{packet_num}".encode('utf-8'), client_addr)
                continue

            # 3. 校驗(yàn)并保存數(shù)據(jù)包
            if 1 <= packet_num <= total_packets:
                file_data[packet_num] = file_chunk
                # 返回成功ACK
                server_socket.sendto(f"ACK|{packet_num}".encode('utf-8'), client_addr)
                # 打印進(jìn)度
                progress = (len(file_data) / total_packets) * 100
                print(f"接收進(jìn)度:{progress:.1f}% ({len(file_data)}/{total_packets})", end='\r')

        # 4. 重組并保存文件
        if file_data and total_packets > 0:
            # 按序號(hào)排序數(shù)據(jù)包
            sorted_chunks = [file_data[i] for i in range(1, total_packets + 1) if i in file_data]
            # 拼接所有數(shù)據(jù)
            file_path = os.path.join(SAVE_DIR, file_name)
            with open(file_path, 'wb') as f:
                for chunk in sorted_chunks:
                    f.write(chunk.encode('utf-8'))  # 若傳二進(jìn)制文件,需調(diào)整編碼邏輯(見(jiàn)進(jìn)階部分)
            print(f"\n文件接收完成!保存路徑:{file_path}")
            # 發(fā)送完成確認(rèn)
            server_socket.sendto("FILE_SAVED".encode('utf-8'), client_addr)
        else:
            print("\n未接收到完整文件數(shù)據(jù)")

    except KeyboardInterrupt:
        print("\n服務(wù)端手動(dòng)停止")
    finally:
        server_socket.close()

if __name__ == "__main__":
    udp_file_server()

2. 客戶端代碼(發(fā)送文件)

import socket
import os
import time

# 配置參數(shù)
SERVER_IP = "127.0.0.1"  # 服務(wù)端IP(遠(yuǎn)程傳輸改實(shí)際IP)
SERVER_PORT = 9999
BUFFER_SIZE = 1024  # 每個(gè)數(shù)據(jù)包的文件數(shù)據(jù)大小
RETRY_TIMES = 3  # 單個(gè)數(shù)據(jù)包最大重傳次數(shù)
RETRY_INTERVAL = 0.5  # 重傳間隔(秒)

def udp_file_client(file_path):
    # 1. 檢查文件是否存在
    if not os.path.exists(file_path):
        print(f"錯(cuò)誤:文件 {file_path} 不存在")
        return

    # 獲取文件名和文件大小
    file_name = os.path.basename(file_path)
    file_size = os.path.getsize(file_path)
    # 計(jì)算總包數(shù)(向上取整)
    total_packets = (file_size + BUFFER_SIZE - 1) // BUFFER_SIZE
    print(f"準(zhǔn)備發(fā)送文件:{file_name},大小:{file_size}字節(jié),總包數(shù):{total_packets}")

    # 2. 創(chuàng)建UDP Socket
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 設(shè)置超時(shí)(避免等待ACK卡死)
    client_socket.settimeout(2)

    try:
        # 3. 讀取文件并分塊發(fā)送
        with open(file_path, 'r', encoding='utf-8') as f:  # 二進(jìn)制文件用'rb'(見(jiàn)進(jìn)階部分)
            for packet_num in range(1, total_packets + 1):
                # 讀取當(dāng)前塊數(shù)據(jù)
                file_chunk = f.read(BUFFER_SIZE)
                if not file_chunk:
                    break

                # 構(gòu)造數(shù)據(jù)包:"文件名|總包數(shù)|當(dāng)前序號(hào)|數(shù)據(jù)"
                packet_data = f"{file_name}|{total_packets}|{packet_num}|{file_chunk}".encode('utf-8')
                retry_count = 0
                ack_received = False

                # 4. 發(fā)送并等待ACK,失敗則重傳
                while retry_count < RETRY_TIMES and not ack_received:
                    try:
                        # 發(fā)送數(shù)據(jù)包
                        client_socket.sendto(packet_data, (SERVER_IP, SERVER_PORT))
                        # 等待ACK
                        ack_data, _ = client_socket.recvfrom(128)
                        ack_parts = ack_data.decode('utf-8').split('|')
                        if ack_parts[0] == "ACK" and int(ack_parts[1]) == packet_num:
                            ack_received = True
                            # 打印進(jìn)度
                            progress = (packet_num / total_packets) * 100
                            print(f"發(fā)送進(jìn)度:{progress:.1f}% ({packet_num}/{total_packets})", end='\r')
                    except socket.timeout:
                        retry_count += 1
                        print(f"\n數(shù)據(jù)包 {packet_num} 超時(shí),重傳 {retry_count}/{RETRY_TIMES}")
                        time.sleep(RETRY_INTERVAL)
                    except Exception as e:
                        print(f"\n數(shù)據(jù)包 {packet_num} 發(fā)送失?。簕e}")
                        retry_count += 1
                        time.sleep(RETRY_INTERVAL)

                if not ack_received:
                    print(f"\n錯(cuò)誤:數(shù)據(jù)包 {packet_num} 重傳{RETRY_TIMES}次失敗,傳輸終止")
                    return

        # 5. 發(fā)送結(jié)束標(biāo)識(shí)
        client_socket.sendto("TRANSFER_FINISH".encode('utf-8'), (SERVER_IP, SERVER_PORT))
        # 等待服務(wù)端保存完成確認(rèn)
        try:
            finish_ack, _ = client_socket.recvfrom(128)
            if finish_ack.decode('utf-8') == "FILE_SAVED":
                print("\n文件發(fā)送完成!服務(wù)端已保存")
        except socket.timeout:
            print("\n未收到服務(wù)端完成確認(rèn),但數(shù)據(jù)已發(fā)送完畢")

    except KeyboardInterrupt:
        print("\n客戶端手動(dòng)停止")
    finally:
        client_socket.close()

if __name__ == "__main__":
    # 替換為你要發(fā)送的文件路徑(本地測(cè)試用絕對(duì)/相對(duì)路徑)
    target_file = "test.txt"  # 示例:發(fā)送當(dāng)前目錄的test.txt
    udp_file_client(target_file)

三、基礎(chǔ)版使用步驟

  1. 準(zhǔn)備測(cè)試文件:在客戶端目錄創(chuàng)建一個(gè)test.txt文件(內(nèi)容任意);
  2. 啟動(dòng)服務(wù)端:運(yùn)行服務(wù)端代碼,控制臺(tái)顯示“UDP文件服務(wù)端已啟動(dòng)”;
  3. 啟動(dòng)客戶端:修改客戶端代碼中target_file為實(shí)際文件路徑,運(yùn)行客戶端;
  4. 查看結(jié)果:服務(wù)端控制臺(tái)顯示傳輸進(jìn)度,完成后文件會(huì)保存到received_files目錄。

四、關(guān)鍵優(yōu)化:支持二進(jìn)制文件(圖片/視頻/壓縮包)

基礎(chǔ)版僅支持文本文件,要傳輸圖片、視頻等二進(jìn)制文件,需修改編碼邏輯(核心是避免字符編碼導(dǎo)致的數(shù)據(jù)損壞):

1. 客戶端修改(讀取二進(jìn)制文件)

# 替換客戶端文件讀取部分
with open(file_path, 'rb') as f:  # 改為二進(jìn)制讀取
    for packet_num in range(1, total_packets + 1):
        file_chunk = f.read(BUFFER_SIZE)
        if not file_chunk:
            break
    # 構(gòu)造數(shù)據(jù)包:用特殊分隔符(如b'|||'),避免二進(jìn)制數(shù)據(jù)沖突
    packet_header = f"{file_name}|{total_packets}|{packet_num}".encode('utf-8')
    packet_data = packet_header + b'|||' + file_chunk  # 二進(jìn)制拼接

2. 服務(wù)端修改(解析二進(jìn)制數(shù)據(jù))

# 替換服務(wù)端數(shù)據(jù)包解析部分
try:
    # 分割頭部和二進(jìn)制數(shù)據(jù)(按b'|||'分割)
    header, file_chunk = data.split(b'|||', 1)
    # 解析頭部(轉(zhuǎn)字符串)
    parts = header.decode('utf-8').split('|', 2)
    file_name, total_packets_str, packet_num_str = parts
    total_packets = int(total_packets_str)
    packet_num = int(packet_num_str)
except Exception as e:
    print(f"二進(jìn)制數(shù)據(jù)包解析失?。簕e}")
    continue

# 保存時(shí)直接寫入二進(jìn)制數(shù)據(jù)
with open(file_path, 'wb') as f:
    for chunk in sorted_chunks:
        f.write(chunk)  # 無(wú)需encode,直接寫二進(jìn)制

五、避坑指南:常見(jiàn)問(wèn)題與解決方案

1. 數(shù)據(jù)包丟包/重傳失敗

  • 原因:UDP無(wú)可靠性保證,網(wǎng)絡(luò)波動(dòng)易丟包;
  • 解決方案:
    • 增大RETRY_TIMES(如改為5),延長(zhǎng)RETRY_INTERVAL
    • 減小BUFFER_SIZE(如改為512),降低單包傳輸壓力;
    • 遠(yuǎn)程傳輸時(shí)確保服務(wù)端端口已開(kāi)放防火墻。

2. 大文件傳輸卡頓

  • 原因:循環(huán)發(fā)送未做速率控制,網(wǎng)絡(luò)擁塞;
  • 解決方案:在客戶端發(fā)送每個(gè)數(shù)據(jù)包后添加time.sleep(0.001),控制發(fā)送速率。

3. 數(shù)據(jù)包解析錯(cuò)誤

  • 原因:分隔符(|)與文件內(nèi)容沖突;
  • 解決方案:改用更復(fù)雜的分隔符(如|||或隨機(jī)字符串),或?qū)︻^部做Base64編碼。

4. 端口被占用

  • 解決方案:修改UDP_PORT(如改為10000),并在服務(wù)端添加SO_REUSEADDR選項(xiàng)(代碼已包含)。

到此這篇關(guān)于Python實(shí)現(xiàn)基于UDP的文件傳輸?shù)娜^(guò)程的文章就介紹到這了,更多相關(guān)Python基于UDP的文件傳輸內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 什么是Python變量作用域

    什么是Python變量作用域

    在本篇文章里小編給大家整理的是關(guān)于Python變量作用域的相關(guān)知識(shí)點(diǎn)內(nèi)容,需要的朋友們可以學(xué)習(xí)下。
    2020-06-06
  • python中內(nèi)置函數(shù)range詳解

    python中內(nèi)置函數(shù)range詳解

    Python內(nèi)置函數(shù)range()是一個(gè)用于生成一系列連續(xù)的整數(shù)的函數(shù),它常用于循環(huán)結(jié)構(gòu)中,用于指定循環(huán)的次數(shù)或迭代的范圍,這篇文章主要介紹了python之內(nèi)置函數(shù)range,需要的朋友可以參考下
    2023-07-07
  • Python的經(jīng)緯度與xy坐標(biāo)系相互轉(zhuǎn)換方式

    Python的經(jīng)緯度與xy坐標(biāo)系相互轉(zhuǎn)換方式

    這篇文章主要介紹了Python的經(jīng)緯度與xy坐標(biāo)系相互轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • Python實(shí)現(xiàn)壁紙下載與輪換

    Python實(shí)現(xiàn)壁紙下載與輪換

    本人對(duì)于壁紙一直偏佛系,不愛(ài)特意去找一堆壁紙。因此用Python簡(jiǎn)單地搞了一個(gè)自動(dòng)下載壁紙,定時(shí)隨機(jī)輪換的功能來(lái)自?shī)首詷?lè),順便分享給大家。
    2020-10-10
  • 利用python實(shí)現(xiàn).dcm格式圖像轉(zhuǎn)為.jpg格式

    利用python實(shí)現(xiàn).dcm格式圖像轉(zhuǎn)為.jpg格式

    今天小編就為大家分享一篇利用python實(shí)現(xiàn).dcm格式圖像轉(zhuǎn)為.jpg格式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-01-01
  • python opencv實(shí)現(xiàn)圖片旋轉(zhuǎn)矩形分割

    python opencv實(shí)現(xiàn)圖片旋轉(zhuǎn)矩形分割

    這篇文章主要為大家詳細(xì)介紹了python opencv實(shí)現(xiàn)圖片旋轉(zhuǎn)矩形分割,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • Python基于Gensim實(shí)現(xiàn)文本相似度/匹配/查重

    Python基于Gensim實(shí)現(xiàn)文本相似度/匹配/查重

    Gensim是基于Python語(yǔ)言的自然語(yǔ)言處理庫(kù),用來(lái)主題建模、文本相似度等文本處理任務(wù),下面我們就來(lái)看看如何使用Gensim實(shí)現(xiàn)文本相似度/匹配/查重等操作吧
    2024-03-03
  • Python中的各個(gè)多線程模塊之間的區(qū)別解析

    Python中的各個(gè)多線程模塊之間的區(qū)別解析

    Python中涉及多線程的主要模塊包括threading、thread和concurrent.futures,現(xiàn)代Python編程推薦使用threading和concurrent.futures,以提供更高層次的抽象和可用性,感興趣的朋友跟隨小編一起看看吧
    2024-09-09
  • 用Python爬取618當(dāng)天某東熱門商品銷量數(shù)據(jù),看看大家喜歡什么!

    用Python爬取618當(dāng)天某東熱門商品銷量數(shù)據(jù),看看大家喜歡什么!

    618購(gòu)物節(jié),準(zhǔn)備分析一波購(gòu)物節(jié)大家都喜歡買什么?本文以某東為例,Python爬取618活動(dòng)的暢銷商品數(shù)據(jù),并進(jìn)行數(shù)據(jù)清洗,最后以可視化的方式從不同角度去了解暢銷商品中,名列前茅的商品是哪些?銷售數(shù)據(jù)如何?用戶好評(píng)如何?等等,需要的朋友可以參考下
    2021-06-06
  • Python OpenCV學(xué)習(xí)之圖像形態(tài)學(xué)

    Python OpenCV學(xué)習(xí)之圖像形態(tài)學(xué)

    形態(tài)學(xué)處理方法是基于對(duì)二進(jìn)制圖像進(jìn)行處理的,卷積核決定圖像處理后的效果。本文將為大家詳細(xì)介紹一下OpenCV中的圖像形態(tài)學(xué),感興趣的可以了解一下
    2022-01-01

最新評(píng)論

凤翔县| 阳曲县| 贵港市| 左贡县| 阿勒泰市| 清河县| 台东市| 留坝县| 香港| 泽库县| 海原县| 鸡西市| 景宁| 株洲县| 左云县| 当涂县| 泸溪县| 西乌| 林芝县| 勃利县| 如皋市| 长岛县| 天峨县| 汕头市| 宿州市| 正蓝旗| 金坛市| 阿拉尔市| 车致| 武安市| 密云县| 阿瓦提县| 兴国县| 漳州市| 沁阳市| 陆丰市| 富平县| 海阳市| 山阳县| 新野县| 外汇|