轉載請註明出處:http://blog.csdn.net/jmppok/article/details/18360595
html
在C/C++網絡編程中難免會遇到須要傳輸大數據、大文件的狀況,而因爲socket自己緩衝區的限制,大概一次只能發送4K左右的數據,因此在傳輸大數據時客戶端就須要進行分包,在目的地從新組包。而實際上已有一些消息/通信中間件對此進行了封裝,提供了直接發送大數據/文件的接口;除此以外,利用共享目錄,ftp,ssh等系統命令來實現大文件/數據也不失爲一種好的方法。程序員
基礎的基於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; }
爲了解決傳輸大文件的問題,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
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);
是否是很簡單呢?
這就不細說了。要麼就是寫共享目錄,要麼就是調用系統命令。
1)直接基於socket編程難度較高,因此不推薦。 2)使用現有的庫方便,但須要學習。通常推薦。 3)共享文件、ftp、scp等難度低,簡單易用,在符合使用場景時是首選。(但一些自命不凡的程序員或許會對你嗤之以鼻,考慮之...)