14.9 Socket 高效文件傳輸

来源:https://www.cnblogs.com/LyShark/archive/2023/10/17/17768898.html
-Advertisement-
Play Games

網路上的文件傳輸功能也是很有必要實現一下的,網路傳輸文件的過程通常分為客戶端和伺服器端兩部分。客戶端可以選擇上傳或下載文件,將文件分塊並逐塊發送到伺服器,或者從伺服器分塊地接收文件。伺服器端接收來自客戶端的請求,根據請求類型執行對應的操作,並根據發送的文件名或其他標識來確定要傳輸的文件。在實現文件傳... ...


網路上的文件傳輸功能也是很有必要實現一下的,網路傳輸文件的過程通常分為客戶端和伺服器端兩部分。客戶端可以選擇上傳或下載文件,將文件分塊並逐塊發送到伺服器,或者從伺服器分塊地接收文件。伺服器端接收來自客戶端的請求,根據請求類型執行對應的操作,並根據發送的文件名或其他標識來確定要傳輸的文件。

在實現文件傳輸之前,需要先打開要傳輸的文件,並獲取文件的大小信息,也可以通過其他方式獲取文件的信息。在客戶端和伺服器端都準備就緒後,可以通過套接字來發送文件數據。在傳輸文件的過程中,可以將文件分解為若幹個數據包進行傳輸,以減少數據傳輸中的丟包或傳輸錯誤。每個數據包的長度可以根據實際情況進行選擇,通常選擇1024位元組或更大,也可以設置成更小的值。傳輸文件的過程中,還需要實現一定的錯誤處理機制,例如檢測傳輸過程中的超時、丟包、不完整數據等情況,併在必要時進行錯誤重傳或協商其他解決方案。

首先無論時服務端還是客戶端都需要封裝兩個函數,其中GetFileName()函數用於當用戶傳入文件的具體路徑信息時自動獲取到該文件的文件名,第二個函數GetFileSize()則用於傳入文件路徑並自動獲取到該文件的位元組數。

// 傳入路徑得到文件名
char* GetFileName(char* Path)
{
  if (strchr(Path, '\\'))
  {
    char ch = '\\';
    char* ref = strrchr(Path, ch) + 1;
    return ref;
  }
  else
  {
    char ch = '/';
    char* ref = strrchr(Path, ch) + 1;
    return ref;
  }
}

// 獲取文件大小
int GetFileSize(std::string FileName)
{
  FILE* pointer = NULL;
  pointer = fopen(FileName.c_str(), "rb");
  if (pointer != NULL)
  {
    fseek(pointer, 0, SEEK_END);
    int size = ftell(pointer);
    fclose(pointer);
    return size;
  }
  return 0;
}

接著我們來看一下RecvFile()接收文件函數是如何實現的,首先第一個發送用於向服務端發出我需要下載具體的那個目錄下的文件,接著服務端會返回該目錄文件的長度,此時我們通過fopen()創建一個新文件,並以此迴圈接收該文件的長度,每次接收成功後自動的fwrite寫出到文件中,當文件被接收完畢後,則通過fclose(pointer)保存並關閉文件。

// 接收文件
bool RecvFile(SOCKET ptr, char* LocalPath, char* RemoteFile)
{
  // 發送需要下載的文件路徑
  send(ptr, RemoteFile, strlen(RemoteFile), 0);

  // 接收文件長度
  long long file_size = 0;
  recv(ptr, (char*)&file_size, sizeof(int), 0);
  if (file_size <= 0)
  {
    return false;
  }

  // 保存文件到指定目錄下
  char *file_name = GetFileName(RemoteFile);
  char file_all_name[1024] = { 0 };

  strcat(file_all_name, LocalPath);
  strcat(file_all_name, file_name);

  std::cout << "生成保存路徑: " << file_all_name << std::endl;
  FILE* pointer = fopen(file_all_name, "wb");
  char buffer[1024] = { 0 };

  if (pointer != NULL)
  {
    long long length = 0;
    long long total_length = 0;

    // 迴圈接收位元組數據,每次接收1024位元組
    while ((length = recv(ptr, buffer, 1024, 0)) > 0)
    {
      // 寫出文件並判斷是否寫出成功
      if (fwrite(buffer, sizeof(char), length, pointer) < length)
      {
        break;
      }

      // 每次累加遞增
      total_length += length;
      memset(buffer, 0, 1024);

      // 判斷文件長度是否全部接收完畢
      if (total_length >= file_size)
      {
        std::cout << "文件接收完畢, 接收位元組數: " << total_length << std::endl;
        fclose(pointer);
        return true;
      }
    }
    fclose(pointer);
  }
  return false;
}

對於SendFile()發送文件而言首先我們接收到客戶端傳來的文件路徑,並通過該路徑得到該文件的具體長度,第一次調用發送函數將文件的長度傳遞給客戶端,此時打開我們所需要發送的文件,並通過迴圈的方式向客戶端傳輸,當數據包傳輸完畢後則自動關閉文件。

// 發送指定文件
bool SendFile(SOCKET ptr)
{
  // 接收文件路徑
  char file_path[1024] = { 0 };
  recv(ptr, file_path, 1024, 0);

  // 得到文件長度併發送給服務端
  long long file_size = GetFileSize(file_path);

  if (file_size <= 0)
  {
    return false;
  }
  send(ptr, (char*)&file_size, sizeof(int), 0);
  std::cout << "發送文件長度: " << file_size << std::endl;

  // 迴圈發送數據
  char buffer[1024] = { 0 };
  FILE* pointer = fopen(file_path, "rb");
  if (pointer != NULL)
  {
    long long length = 0;
    long long total_length = 0;

    // 迴圈發送數據
    while ((length = fread(buffer, sizeof(char), 1024, pointer)) > 0)
    {
      send(ptr, buffer, length, 0);
      memset(buffer, 0, 1024);
      total_length += length;
    }

    if (total_length == file_size)
    {
      return true;
    }
  }
  return false;
}

14.9.1 服務端實現

如下代碼展示瞭如何使用Winsock進行TCP協議的文件傳輸。首先使用WSAStartup函數對Winsock庫進行初始化。然後創建一個socket,設置IP地址、埠號等信息,並將該socket和本地服務端的地址綁定起來。接下來對該socket進行監聽,等待客戶端的連接請求。

當有客戶端連接請求到來時,accept函數會接收請求,並創建一個新的socket與客戶端進行通信。在與客戶端通信的過程中,可以通過sendrecv函數進行數據的傳輸,實現文件的上傳和下載功能。此處的代碼調用RecvFile函數,該函數為自定義實現的接收文件函數,負責接收數據並將接收到的文件存儲到指定的路徑下。

int main(int argc, char* argv[])
{
  WSADATA wsaData;
  if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    exit(1);

  // 聲明並初始化一個服務端(本地)的地址結構 
  sockaddr_in server_addr;
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.S_un.S_addr = INADDR_ANY;
  server_addr.sin_port = htons(8087);

  // 創建socket 
  SOCKET m_Socket = socket(AF_INET, SOCK_STREAM, 0);
  if (SOCKET_ERROR == m_Socket)
    exit(1);

  // 綁定socket和服務端(本地)地址 
  if (SOCKET_ERROR == bind(m_Socket, (LPSOCKADDR)&server_addr, sizeof(server_addr)))
    exit(1);

  // 監聽 
  if (SOCKET_ERROR == listen(m_Socket, 10))
    exit(1);

  sockaddr_in client_addr;
  int client_addr_len = sizeof(client_addr);

  SOCKET m_New_Socket = accept(m_Socket, (sockaddr*)&client_addr, &client_addr_len);

  // 接收遠程d://lyshark.exe放到本地的d://11g/目錄下
  bool ref = RecvFile(m_New_Socket, (char*)"d://11g/", (char*)"d://lyshark.exe");
  std::cout << "接收狀態: " << ref << std::endl;

  closesocket(m_New_Socket);
  closesocket(m_Socket);

  WSACleanup();
  system("pause");
  return 0;
}

14.9.2 客戶端實現

如下客戶端代碼實現了一個基於TCP協議的文件傳輸客戶端。首先使用WSAStartup函數對Winsock庫進行初始化。然後創建一個socket,並設置服務端的IP地址和埠號。之後通過connect函數與服務端建立連接,連接成功後調用SendFile函數進行文件傳輸,將指定的文件發送到服務端。文件傳輸完成後,關閉socket連接,清除Winsock資源。

int main(int argc, char* argv[])
{
  while (true)
  {
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
      exit(1);

    // 創建socket 
    SOCKET c_Socket = socket(AF_INET, SOCK_STREAM, 0);

    //指定服務端的地址 
    sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    server_addr.sin_port = htons(8087);

    if (SOCKET_ERROR != connect(c_Socket, (LPSOCKADDR)&server_addr, sizeof(server_addr)))
    {
      bool ref = SendFile(c_Socket);
      std::cout << "文件發送狀態: " << ref << std::endl;
    }
    closesocket(c_Socket);
    WSACleanup();
    Sleep(1000);
  }
  return 0;
}

文件傳輸功能代碼就這些,其實理解起來並不難,讀者可自行編譯並運行上述代碼,運行後則可接收遠程d://lyshark.exe文件,並放到本地的d://11g/目錄下,輸出效果圖如下;

本文作者: 王瑞
本文鏈接: https://www.lyshark.com/post/323aa250.html
版權聲明: 本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!

文章作者:lyshark (王瑞)
文章出處:https://www.cnblogs.com/LyShark/p/17768898.html
本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 配置之前,您需要知道的是,Tomcat, Jetty, Undertow 作為三大主流 Servelt 容器,Undertow 的性能要優於前兩者。 所以,我們推薦您使用 Undertow 容器。接下來,就我們看看如何在 Spring Boot 中快捷地集成 Undertow。 一、添加 Maven ...
  • ${eval("env['ss.purchase.exchange.rate'].search([('currency_id', '=', 'USD'), ('crawler_day', '=', datetime.datetime(object.delivery_order_id.ATD.year ...
  • 前言 我打算寫一個系列,內容是將python註入到其他進程實現inline hook和主動調用。本篇文章是這個系列的第一篇,後面用到的案例是註入python到PC微信實現基本的收發消息。文章著重於python方面的內容,所以對於微信找收發消息的call不會去講過程,有興趣的可以直接百度搜PC微信逆向 ...
  • 1 2FA 的定義 雙因素身份驗證 (2FA) 是一種身份和訪管理安全方法,需要經過兩種形式的身份驗證才能訪河資源和數據,2FA使企業能夠監視和幫助保護其最易受攻擊的信息和網路。 2 2FA 的身份驗證方法 使用雙因素身份驗證時有不同的身份驗證方法。此處列出了一些最受歡迎的選項。 2.1 硬體令牌 ...
  • package chap03; import java.io.IOException;import java.io.PrintWriter;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultS ...
  • 一、審核功能實現的方式 1、普通 方案:經辦時入A表,審核後從A表讀取數據,然後操作目標B表; 優勢:思路簡單 劣勢:對後端功能實行高度的嵌入;審核功能數據操作不統一 2、彈框式 方案:前臺實現,操作時判斷是否需要許可權控制,如果需要,則彈出框,由審核人員進行審核,審核通過後,進行後續操作。 優勢:對 ...
  • C語音-數據類型 數據類型 中文名稱 空間大小(bite - 位元組) char 字元串數據類 1 short (int) 短整型 2 int 整形 4 long 長整形 4 long long 更長的整形 8 float 單精度浮點數 4 double 雙精度浮點數 8 include <> int ...
  • 最近這段時間收到了一些讀者的私信,問我某個技術要不要學,還有一些在國外的同學竟然對 Java 圖形化很感興趣,還想找這方面的工作。 比較忙,一直沒抽出時間去回答這類問題,剛好看到我關註的一位大佬回答過,這裡分享一下,希望對你能有幫助。 下麵是正文。 原文鏈接:https://www.zhihu.co ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...