应用系统之间数据传输的几种方式

时间:2024-02-18 07:52:23

大文件/数据网络传输方法总结(转载)

网络编程中不免会遇到需要传输大数据、大文件的情况,而由于socket本身缓冲区的限制,大概一次只能发送4K左右的数据,所以在传输大数据时客户端就需要进行分包,在目的地重新组包。而实际上已有一些消息/通讯中间件对此进行了封装,提供了直接发送大数据/文件的接口;除此之外,利用共享目录,ftp,ssh等系统命令来实现大文件/数据也不失为一种好的方法。

1.基础的基于socket进行传输

基础的基于socket进行传输关键在于控制,需要自己行分包和组包。

data-cke-saved-src=http://www.2cto.com/uploadfile/Collfiles/20140117/20140117084908177.jpg

//////////////////////////////////////////////////////////////////////// 
    // 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 必须约定文件数据的格式,当改变文件格式的时候,需要各个系统都同步做修改。

 

数据库共享数据方式

系统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