C/C++大文件/數據網絡傳輸方法總結

轉載請註明出處:http://blog.csdn.net/jmppok/article/details/18360595
html


在C/C++網絡編程中難免會遇到須要傳輸大數據、大文件的狀況,而因爲socket自己緩衝區的限制,大概一次只能發送4K左右的數據,因此在傳輸大數據時客戶端就須要進行分包,在目的地從新組包。而實際上已有一些消息/通信中間件對此進行了封裝,提供了直接發送大數據/文件的接口;除此以外,利用共享目錄,ftp,ssh等系統命令來實現大文件/數據也不失爲一種好的方法。程序員

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

基礎的基於socket進行傳輸關鍵在於控制,須要本身行分包和組包。apache


原理很簡單那,咱們就直接看一下代碼吧。
代碼引用自: 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);ssh

接收端則簡單的使用connection.createInputStream拿到一個輸入流,從中讀取文件數據便可。socket

InputStream in = connection.createInputStream(destination)

詳見:http://activemq.apache.org/jms-streams.htmltcp


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等難度低,簡單易用,在符合使用場景時是首選。(但一些自命不凡的程序員或許會對你嗤之以鼻,考慮之...)

相關文章
相關標籤/搜索