1. 程式人生 > >C/C++大檔案/資料網路傳輸方法總結

C/C++大檔案/資料網路傳輸方法總結

在C/C++網路程式設計中不免會遇到需要傳輸大資料、大檔案的情況,而由於socket本身緩衝區的限制,大概一次只能傳送4K左右的資料,所以在傳輸大資料時客戶端就需要進行分包,在目的地重新組包。而實際上已有一些訊息/通訊中介軟體對此進行了封裝,提供了直接傳送大資料/檔案的介面;除此之外,利用共享目錄,ftp,ssh等系統命令來實現大檔案/資料也不失為一種好的方法。

1.基礎的基於socket進行傳輸

基礎的基於socket進行傳輸關鍵在於控制,需要自己行分包和組包。


原理很簡單那,我們就直接看一下程式碼吧。
程式碼引用自: http://blog.csdn.net/ljd_1986413/article/details/7940938

    ////////////////////////////////////////////////////////////////////////  
    // file_server.c -- socket檔案傳輸伺服器端示例程式碼   
    // /////////////////////////////////////////////////////////////////////  
    #include<netinet/in.h>   
    #include<sys/types.h>   
    #include<sys/socket.h>   
    #include<stdio.h>   
    #include<stdlib.h>   
    #include<string.h>   
      
    #define HELLO_WORLD_SERVER_PORT    6666  
    #define LENGTH_OF_LISTEN_QUEUE     20  
    #define BUFFER_SIZE                1024  
    #define FILE_NAME_MAX_SIZE         512  
      
    int main(int argc, char **argv)  
    {  
        // set socket's address information   
        // 設定一個socket地址結構server_addr,代表伺服器internet的地址和埠  
        struct sockaddr_in   server_addr;  
        bzero(&server_addr, sizeof(server_addr));  
        server_addr.sin_family = AF_INET;  
        server_addr.sin_addr.s_addr = htons(INADDR_ANY);  
        server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);  
      
        // create a stream socket   
        // 建立用於internet的流協議(TCP)socket,用server_socket代表伺服器向客戶端提供服務的介面  
        int server_socket = socket(PF_INET, SOCK_STREAM, 0);  
        if (server_socket < 0)  
        {  
            printf("Create Socket Failed!\n");  
            exit(1);  
        }  
      
        // 把socket和socket地址結構繫結   
        if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)))  
        {  
            printf("Server Bind Port: %d Failed!\n", HELLO_WORLD_SERVER_PORT);  
            exit(1);  
        }  
      
        // server_socket用於監聽   
        if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE))  
        {  
            printf("Server Listen Failed!\n");  
            exit(1);  
        }  
      
        // 伺服器端一直執行用以持續為客戶端提供服務   
        while(1)  
        {  
            // 定義客戶端的socket地址結構client_addr,當收到來自客戶端的請求後,呼叫accept  
            // 接受此請求,同時將client端的地址和埠等資訊寫入client_addr中  
            struct sockaddr_in client_addr;  
            socklen_t          length = sizeof(client_addr);  
      
            // 接受一個從client端到達server端的連線請求,將客戶端的資訊儲存在client_addr中  
            // 如果沒有連線請求,則一直等待直到有連線請求為止,這是accept函式的特性,可以  
            // 用select()來實現超時檢測   
            // accpet返回一個新的socket,這個socket用來與此次連線到server的client進行通訊  
            // 這裡的new_server_socket代表了這個通訊通道  
            int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);  
            if (new_server_socket < 0)  
            {  
                printf("Server Accept Failed!\n");  
                break;  
            }  
      
            char buffer[BUFFER_SIZE];  
            bzero(buffer, sizeof(buffer));  
            length = recv(new_server_socket, buffer, BUFFER_SIZE, 0);  
            if (length < 0)  
            {  
                printf("Server Recieve Data Failed!\n");  
                break;  
            }  
      
            char file_name[FILE_NAME_MAX_SIZE + 1];  
            bzero(file_name, sizeof(file_name));  
            strncpy(file_name, buffer,  
                    strlen(buffer) > FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(buffer));  
      
            FILE *fp = fopen(file_name, "r");  
            if (fp == NULL)  
            {  
                printf("File:\t%s Not Found!\n", file_name);  
            }  
            else  
            {  
                bzero(buffer, BUFFER_SIZE);  
                int file_block_length = 0;  
                while( (file_block_length = fread(buffer, sizeof(char), BUFFER_SIZE, fp)) > 0)  
                {  
                    printf("file_block_length = %d\n", file_block_length);  
      
                    // 傳送buffer中的字串到new_server_socket,實際上就是傳送給客戶端  
                    if (send(new_server_socket, buffer, file_block_length, 0) < 0)  
                    {  
                        printf("Send File:\t%s Failed!\n", file_name);  
                        break;  
                    }  
      
                    bzero(buffer, sizeof(buffer));  
                }  
                fclose(fp);  
                printf("File:\t%s Transfer Finished!\n", file_name);  
            }  
      
            close(new_server_socket);  
        }  
      
        close(server_socket);  
      
        return 0;  
    }  

    //////////////////////////////////////////////////////  
    // file_client.c  socket傳輸檔案的client端示例程式   
    // ///////////////////////////////////////////////////  
    #include<netinet/in.h>                         // for sockaddr_in  
    #include<sys/types.h>                          // for socket  
    #include<sys/socket.h>                         // for socket  
    #include<stdio.h>                              // for printf  
    #include<stdlib.h>                             // for exit  
    #include<string.h>                             // for bzero  
      
    #define HELLO_WORLD_SERVER_PORT       6666  
    #define BUFFER_SIZE                   1024  
    #define FILE_NAME_MAX_SIZE            512  
      
    int main(int argc, char **argv)  
    {  
        if (argc != 2)  
        {  
            printf("Usage: ./%s ServerIPAddress\n", argv[0]);  
            exit(1);  
        }  
      
        // 設定一個socket地址結構client_addr, 代表客戶機的internet地址和埠  
        struct sockaddr_in client_addr;  
        bzero(&client_addr, sizeof(client_addr));  
        client_addr.sin_family = AF_INET; // internet協議族  
        client_addr.sin_addr.s_addr = htons(INADDR_ANY); // INADDR_ANY表示自動獲取本機地址  
        client_addr.sin_port = htons(0); // auto allocated, 讓系統自動分配一個空閒埠  
      
        // 建立用於internet的流協議(TCP)型別socket,用client_socket代表客戶端socket  
        int client_socket = socket(AF_INET, SOCK_STREAM, 0);  
        if (client_socket < 0)  
        {  
            printf("Create Socket Failed!\n");  
            exit(1);  
        }  
      
        // 把客戶端的socket和客戶端的socket地址結構繫結   
        if (bind(client_socket, (struct sockaddr*)&client_addr, sizeof(client_addr)))  
        {  
            printf("Client Bind Port Failed!\n");  
            exit(1);  
        }  
      
        // 設定一個socket地址結構server_addr,代表伺服器的internet地址和埠  
        struct sockaddr_in  server_addr;  
        bzero(&server_addr, sizeof(server_addr));  
        server_addr.sin_family = AF_INET;  
      
        // 伺服器的IP地址來自程式的引數   
        if (inet_aton(argv[1], &server_addr.sin_addr) == 0)  
        {  
            printf("Server IP Address Error!\n");  
            exit(1);  
        }  
      
        server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);  
        socklen_t server_addr_length = sizeof(server_addr);  
      
        // 向伺服器發起連線請求,連線成功後client_socket代表客戶端和伺服器端的一個socket連線  
        if (connect(client_socket, (struct sockaddr*)&server_addr, server_addr_length) < 0)  
        {  
            printf("Can Not Connect To %s!\n", argv[1]);  
            exit(1);  
        }  
      
        char file_name[FILE_NAME_MAX_SIZE + 1];  
        bzero(file_name, sizeof(file_name));  
        printf("Please Input File Name On Server.\t");  
        scanf("%s", file_name);  
      
        char buffer[BUFFER_SIZE];  
        bzero(buffer, sizeof(buffer));  
        strncpy(buffer, file_name, strlen(file_name) > BUFFER_SIZE ? BUFFER_SIZE : strlen(file_name));  
        // 向伺服器傳送buffer中的資料,此時buffer中存放的是客戶端需要接收的檔案的名字  
        send(client_socket, buffer, BUFFER_SIZE, 0);  
      
        FILE *fp = fopen(file_name, "w");  
        if (fp == NULL)  
        {  
            printf("File:\t%s Can Not Open To Write!\n", file_name);  
            exit(1);  
        }  
      
        // 從伺服器端接收資料到buffer中   
        bzero(buffer, sizeof(buffer));  
        int length = 0;  
        while(length = recv(client_socket, buffer, BUFFER_SIZE, 0))  
        {  
            if (length < 0)  
            {  
                printf("Recieve Data From Server %s Failed!\n", argv[1]);  
                break;  
            }  
      
            int write_length = fwrite(buffer, sizeof(char), length, fp);  
            if (write_length < length)  
            {  
                printf("File:\t%s Write Failed!\n", file_name);  
                break;  
            }  
            bzero(buffer, BUFFER_SIZE);  
        }  
      
        printf("Recieve File:\t %s From Server[%s] Finished!\n", file_name, argv[1]);  
      
        // 傳輸完畢,關閉socket   
        fclose(fp);  
        close(client_socket);  
        return 0;  
      
    }  


2.使用現有的通訊中介軟體

2.1 ActiveMQ 傳送檔案介面

為了解決傳輸大檔案的問題,ActiveMQ在jms規範之外引入了jms streams的概念。PTP模式下,連到同一個destination的兩端,可以通過broker中轉來傳輸大檔案。

傳送端使用connection.createOutputStream開啟一個輸出流,往流裡寫檔案。

OutputStream out =connection.createOutputStream(destination);

接收端則簡單的使用connection.createInputStream拿到一個輸入流,從中讀取檔案資料即可。

InputStream in = connection.createInputStream(destination)

2.2 ZeroMQ 介面

ZeroMQ沒有直接提供傳送檔案的介面。但ZeroMQ中send(void * data, size_t len)介面已經做好了封裝,可以send任意大小的資料。程式碼如下:

	zmq::context_t ctx(1);
    zmq::socket_t sock(ctx, ZMQ_REQ);

	sock.connect("tcp://192.168.20.111:20310");

	sock.send(pData,len);//資料大小沒有限制,可以直接傳送任意大小的資料

	
	char reply[100];
	sock.recv (reply,100);

	sock.disconnect(addr);

接收端程式碼如下:

m_context = new zmq::context_t(1);
    m_socket = new zmq::socket_t (*m_context, ZMQ_REP);
	m_socket->bind ("tcp://*:20310");

	zmq::message_t request;

        // Wait for next request from client
        m_socket->recv (&request) // request可以接受傳送來的任意大小的資料
        
        m_socket->send("ok",2);


是不是很簡單呢?

3.基於共享檔案、ftp、scp等


這就不細說了。要麼就是寫共享目錄,要麼就是呼叫系統命令。

4.總結


1)直接基於socket程式設計難度較高,所以不推薦。

2)使用現有的庫方便,但需要學習。一般推薦。

3)共享檔案、ftp、scp等難度低,簡單易用,在符合使用場景時是首選。(但一些自命不凡的程式設計師或許會對你嗤之以鼻,考慮之...)

相關推薦

C/C++檔案/資料網路傳輸方法總結

在C/C++網路程式設計中不免會遇到需要傳輸大資料、大檔案的情況,而由於socket本身緩衝區的限制,大概一次只能傳送4K左右的資料,所以在傳輸大資料時客戶端就需要進行分包,在目的地重新組包。而實際上已有一些訊息/通訊中介軟體對此進行了封裝,提供了直接傳送大資料/檔案的

C#讀取XML檔案資料和把資料儲存至xml的方法

原文在百度知道中,來源於多個網友。 新浪微博:http://blog.sina.com.cn/s/blog_ad7fd0f4010180md.html (一) 儲存 var xml =XElement.Load(@"路徑");xml.Element("節點名字").AddA

[C#] 計算檔案的MD5的兩種方式(直接呼叫方法計算,流計算-適用於檔案)

通過.NET中的預設類實現,但是採用不同類,針對不同的情況: 具體如下: 類: /// <summary> /// 檔案MD5操作類 /// </summary> public class MD5Checker {

javascript+C#本地檔案上傳到伺服器方法(WebUploader)

  在前後端進行資料傳輸時,有時需要傳輸大檔案。WebUploader提供了一些前端傳輸圖片和檔案等地方法。但是,當上傳檔案較大時,會被伺服器端限制,阻止其上傳。   在ASP.Net中,調整伺服器接受檔案的大小的配置方法如下: <httpRuntim

C++讀取txt檔案資料

本次實驗主要目的是實現C++提取txt檔案的資料,txt檔案中的資料為double型。 txt檔案的資料為 1.123456789098 2.123456789098 3.123456789098 4.123456789098 5.123456789098 6.123456789098 7

C#實現檔案的分割複製

使用FileStream類將檔案以指定的檔案流大小進行分割,追加到一個同名的空檔案,該類支援非同步操作,例項程式碼分享 例: Public void Copyfile(stringFormerfile,string tofile,int sectsize) {   

Python實現Windows和Linux之間互相傳輸檔案(資料夾)的方法

   專案中需要從Windows系統傳輸ISO檔案到Linux測試系統,然後再Linux測試系統裡安裝這個ISO檔案。所以就需要實現如何把檔案從Windows系統傳輸到Linux系統中。 在專案中使用了pscp.exe這個工具,只要按照pscp.exe的使用說明操作即可。只要進入pscp.exe的安裝位置

伺服器之間如何跨國傳輸檔案資料

跨國大檔案傳輸,是各大企業普遍面臨的問題,其中主要突出的矛盾有: 怎麼保證跨國傳輸的資料可靠性? 怎麼提高跨國傳輸的傳輸效率? 這兩個矛盾,要從底層傳輸協議去分析解決:保證傳輸資料的可靠性,首先你使用的傳輸協議需要是可靠的,比如使用經典的TCP協議,面向連結的可靠的位元組流服務,提供超時

C#實現檔案上傳功能(二)---webuploader上傳

                                        一、問題來源 近些時候,處理專案的時候發現如果使用者上傳大檔案的時候使用HtmlInputFile通過httppostfilebase 來實現上傳,如果檔案較小的話較小的話,上傳沒有問題(4M)

C# 讀取txt檔案資料,StreamReader.BaseStream.Seek()後ReadLine()有錯誤的問題

readerPOSPath.BaseStream.Seek(n, SeekOrigin.Begin); linepos = readerPOSPath.ReadLine();//讀一行 按照上面的方法。Seek到指定位置後,緊接著ReadLine(),得到的結果有Seek之前的資料,也就是

C#:開啟 檔案/資料夾選擇框,選取,並獲得路徑名稱

using System; using System.Windows.Forms; //選取檔案,並獲得路徑 private void button1_Click(object sender, EventArgs e) { var f = n

C# 讀取檔案

/// <summary> /// 讀取大檔案,每次讀取1M,優化可考慮分割讀取 /// </summary> /// <returns></returns> public static string ReadBinaryFileToString(Fi

MFC/C++ CFile寫入檔案資料,輸出utf-8的文字。(防止中文亂碼)

需求說明:有時候用CFile寫入檔案的內容會出現中文亂碼問題,這時候就需要把寫入的檔案編碼型別改為utf-8的型別。 程式碼功能:輸出utf-8格式的內容到檔案中 作者:weekdawn try {

C/C++ Windows API——檔案/資料夾建立、刪除、移動及檢視時間

// FileOperationDemo.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <Windows.h>

C\C++對檔案的快速讀寫(記憶體對映)

1、 建立檔案(CreateFile),如下: HANDLE CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBU

C#讀取Excel檔案資料

相當簡單,Excel就像資料庫,每個Sheet就是一個Table. Microsoft.Jet.OLEDB驅動.之後是DataReader迴圈,或DataSet處理都非常簡單. #region set connectionstring strConn = @"Provide

c#匯出Excel檔案的幾種方法

using System; using System.Collections.Generic; using System.Text; using System.Data; using System.Windows.Forms; using System.Re

C++中的資料型別轉換方法總結

int到char*,或者反過來從char*到int,在C/C++中到底有多少種轉換方法呢?符合標準的大概有四種。即C資料轉換函式族、sprintf/snprintf/sscanf函式族、字串流std::stringstream、std::strsteam。不符合標準卻又廣為使用的包括CString和boost

C++將csv檔案資料讀入陣列中

將形如 1,2,3 4,5,6 7,8,9 的csv檔案資料放入二維陣列中。 #include <iostream> #include <string

c++讀取文字檔案最高效的方法

從網上找到的方法,可以一下子讀取檔案的所有內容到字串中,實驗了很有用,發出來共享。 /* * main.cpp * * Created on: 2014年6月17日 * Author: Spike */ /eclip