大文件/数据网络传输方法总结(转载)
网络编程中不免会遇到需要传输大数据、大文件的情况,而由于socket本身缓冲区的限制,大概一次只能发送4K左右的数据,所以在传输大数据时客户端就需要进行分包,在目的地重新组包。而实际上已有一些消息/通讯中间件对此进行了封装,提供了直接发送大数据/文件的接口;除此之外,利用共享目录,ftp,ssh等系统命令来实现大文件/数据也不失为一种好的方法。
1.基础的基于socket进行传输
基础的基于socket进行传输关键在于控制,需要自己行分包和组包。
//////////////////////////////////////////////////////////////////////// // 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! ); exit( 1 ); } // 把socket和socket地址结构绑定 if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr))) { printf(Server Bind Port: %d Failed! , HELLO_WORLD_SERVER_PORT); exit( 1 ); } // server_socket用于监听 if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE)) { printf(Server Listen Failed! ); 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,(structsockaddr*)&client_addr,&length); if (new_server_socket < 0 ) { printf(Server Accept Failed! ); 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! ); 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: %s Not Found! , 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 ,file_block_length); // 发送buffer中的字符串到new_server_socket,实际上就是发送给客户端 if (send(new_server_socket, buffer, file_block_length, 0 ) < 0 ) { printf(Send File: %s Failed! , file_name); break ; } bzero(buffer, sizeof(buffer)); } fclose(fp); printf(File: %s Transfer Finished! , file_name); } close(new_server_socket); } close(server_socket); return 0 ; } </string.h></stdlib.h></stdio.h></sys></sys></netinet> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
////////////////////////////////////////////////////// // 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 , 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! ); exit( 1 ); } // 把客户端的socket和客户端的socket地址结构绑定 if (bind(client_socket, (struct sockaddr*)&client_addr, sizeof(client_addr))) { printf(Client Bind Port Failed! ); 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! ); 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! , 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. ); 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: %s Can Not Open To Write! , 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! , argv[ 1 ]); break ; } int write_length = fwrite(buffer, sizeof( char ), length, fp); if (write_length < length) { printf(File: %s Write Failed! , file_name); break ; } bzero(buffer, BUFFER_SIZE); } printf(Recieve File: %s From Server[%s] Finished! , file_name, argv[ 1 ]); // 传输完毕,关闭socket fclose(fp); close(client_socket); return 0 ; } </string.h></stdlib.h></stdio.h></sys></sys></netinet> |
2.使用现有的通讯中间件
2.1 ActiveMQ 传送文件接口
为了解决传输大文件的问题,ActiveMQ在jms规范之外引入了jms streams的概念。PTP模式下,连到同一个destination的两端,可以通过broker中转来传输大文件。
发送端使用connection.createOutputStream打开一个输出流,往流里写文件。
OutputStream out =connection.createOutputStream(destination);
接收端则简单的使用connection.createInputStream拿到一个输入流,从中读取文件数据即可。
1
|
InputStream in = connection.createInputStream(destination) |
详见:http://activemq.apache.org/jms-streams.html
2.2 ZeroMQ 接口
ZeroMQ没有直接提供传送文件的接口。但ZeroMQ中send(void * data, size_t len)接口已经做好了封装,可以send任意大小的数据。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
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); |
接收端代码如下:
1
2
3
4
5
6
7
8
9
10
|
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等难度低,简单易用,在符合使用场景时是首选。
转载地址:http://www.2cto.com/kf/201401/273290.html
第二种表述方式:
随着近年来SOA(面向服务技术架构)的兴起,越来越多的应用系统开始进行分布式的设计和部署。系统由原来单一的技术架构变成面向服务的多系统架构。原来在一个系统之间可以完成的业务流程,通过多系统的之间多次交互来实现。这里不打算介绍如何进行SOA架构的设计,而是介绍一下应用系统之间如何进行数据的传输。
应用系统之间数据传输有三个要素:传输方式,传输协议,数据格式
数据传输方式一般无非是以下几种:
1 socket方式
Socket方式是最简单的交互方式。是典型才c/s 交互模式。一台客户机,一台服务器。服务器提供服务,通过ip地址和端口进行服务访问。而客户机通过连接服务器指定的端口进行消息交互。其中传输协议可以是tcp/UDP 协议。而服务器和约定了请求报文格式和响应报文格式。如图一所示:
目前我们常用的http调用,java远程调用,webserivces 都是采用的这种方式,只不过不同的就是传输协议以及报文格式。
这种方式的优点是:
1 易于编程,目前java提供了多种框架,屏蔽了底层通信细节以及数据传输转换细节。
2 容易控制权限。通过传输层协议https,加密传输的数据,使得安全性提高
3 通用性比较强,无论客户端是.net架构,java,python 都是可以的。尤其是webservice规范,使得服务变得通用
而这种方式的缺点是:
1 服务器和客户端必须同时工作,当服务器端不可用的时候,整个数据交互是不可进行。
2 当传输数据量比较大的时候,严重占用网络带宽,可能导致连接超时。使得在数据量交互的时候,服务变的很不可靠。
2 ftp/文件共享服务器方式
对于大数据量的交互,采用这种文件的交互方式最适合不过了。系统A和系统B约定文件服务器地址,文件命名规则,文件内容格式等内容,通过上传文件到文件服务器进行数据交互。
最典型的应用场景是批量处理数据:例如系统A把今天12点之前把要处理的数据生成到一个文件,系统B第二天凌晨1点进行处理,处理完成之后,把处理结果生成到一个文件,系统A 12点在进行结果处理。这种状况经常发生在A是事物处理型系统,对响应要求比较高,不适合做数据分析型的工作,而系统B是后台系统,对处理能力要求比较高,适合做批量任务系统。
以上只是说明通过文件方式的数据交互,实际情况B完成任务之后,可能通过socket的方式通知A,不一定是通过文件方式。
这种方式的优点:
1 在数据量大的情况下,可以通过文件传输,不会超时,不占用网络带宽。
2 方案简单,避免了网络传输,网络协议相关的概念。
这种方式的缺点:
1 不太适合做实时类的业务
2 必须有共同的文件服务器,文件服务器这里面存在风险。因为文件可能被篡改,删除,或者存在泄密等。
3 必须约定文件数据的格式,当改变文件格式的时候,需要各个系统都同步做修改。
3 数据库共享数据方式
系统A和系统B通过连接同一个数据库服务器的同一张表进行数据交换。当系统A请求系统B处理数据的时候,系统A Insert一条数据,系统B select 系统A插入的数据进行处理。
这种方式的优点是
1 相比文件方式传输来说,因为使用的同一个数据库,交互更加简单。
2 由于数据库提供相当做的操作,比如更新,回滚等。交互方式比较灵活,而且通过数据库的事务机制,可以做成可靠性的数据交换。
这种方式的缺点:
1 当连接B的系统越来越多的时候,由于数据库的连接池是有限的,导致每个系统分配到的连接不会很多,当系统越来越多的时候,可能导致无可用的数据库连接
2 一般情况,来自两个不同公司的系统,不太会开放自己的数据库给对方连接,因为这样会有安全性影响
4 message方式
Java消息服务(Java Message Service)是message数据传输的典型的实现方式。系统A和系统B通过一个消息服务器进行数据交换。系统A发送消息到消息服务器,如果系统B订阅系统A发送过来的消息,消息服务器会消息推送给B。双方约定消息格式即可。目前市场上有很多开源的jms消息中间件,比如 ActiveMQ, OpenJMS 。
这种方式的优点
1 由于jms定义了规范,有很多的开源的消息中间件可以选择,而且比较通用。接入起来相对也比较简单
2 通过消息方式比较灵活,可以采取同步,异步,可靠性的消息处理,消息中间件也可以独立出来部署。
这种方式的缺点
1 学习jms相关的基础知识,消息中间件的具体配置,以及实现的细节对于开发人员来说还是有一点学习成本的
2 在大数据量的情况下,消息可能会产生积压,导致消息延迟,消息丢失,甚至消息中间件崩溃。
下面具体来分析一个场景,来看看系统之间数据传输的应用
场景 目前业务人员需要导入一个大文件到系统A,系统A保存文件信息,而文件里面的明细信息需要导入到系统B进行分析,当系统B分析完成之后,需要把分析结果通知系统A。
A 系统A先保存文件到文件服务器。
B 系统A 通过webservice 调用系统B提供的服务器,把需要处理的文件名发送到系统B。由于文件很大,需要处理很长时间,所以B不及时处理文件,而是保存需要处理的文件名到数据库,通过后台定时调度机制去处理。所以B接收请求成功,立刻返回系统A成功。
C 系统B定时查询数据库记录,通过记录查找文件路径,找到文件进行处理。这个过程很长。
D 系统B处理完成之后发送消息给系统A,告知系统A文件处理完成。
E 系统A 接收到系统B请求来的消息,进行展示任务结果
转载地址:http://www.cnblogs.com/aigongsi/archive/2012/04/26/2413646.html