加减乘除服务线程池_第1页
加减乘除服务线程池_第2页
加减乘除服务线程池_第3页
加减乘除服务线程池_第4页
加减乘除服务线程池_第5页
已阅读5页,还剩23页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、电 子 科 技 大 学作 业 报 告 一、作业名称 实现加减乘除服务线程池二、作业要求1.使用多个队列,每一个计算线程有独立的队列用于存储计算请求,请求线程可用一个队列用于接收结果;2.只需要实现A(+,-,X,/)B简单两元计算;3.请求线程与计算线程是多对多关系。三、设计与实现1.总体设计从题目中可以看出,设计时需要多线程和服务的概念。多线程相对多进程而言,可以减少系统的开销,并且能提高 服务的延迟时间,因为线程间的切换时间要远远小于进程间切换时间,多线程编程可以提高程序并发性,实现要求中的请求 线程与计算线程多对多的关系。而服务的概念就是有要有服务器、客户端,并且要协同服务端与客户端的操

2、作一致,于是先入先出的队列操作满足此要求,而服务端与客户端的数据传输需要用到套接口编程。目前最常用的套接口是字:字节流套接口(基于TCP)和数据报套接口(基于UDP),当然还有原始套接口(原始套接口提供TCP套接口和UDP套接口所不提供的功能,如构造自己的TCP或UDP分组)等,本次设计主要采用TCP套接口模式。 主进程在最初建立若干个子线程后,形成一个线程池,把外部请求的任务投递到请求队列最小的线程中,然后线程再从请求队列中逐个取出处理,直到线程处理完任务。总体结构框图如下:图1 总体结构框图2.客户/服务器模型在客户/服务器模型中,多个相互通信的终端都作为客户端,与网络服务器进行连接,并通

3、过服务器进行信息的传递,所以多个客户端之间的通信就变为了客户端与服务端的通信。客户/服务器模型主要有并发型交互和重复型交互两种具体类型。在并发交互模式下,程序的主要运行步骤如下:等待一个客户请求的带到来 ;生成一个进程或线程来处理这个客户请求,同时这里还可以接受其他客户请求,处理后,终止这个 进程或线程;反馈客户端;等待新的客户请求带来并进行下一次服务,如此 循环运作。在重复型交互模式下,程序的主要运行步骤如下:等待一个客户请求的到来;处理客户的请求,对客户进行 服务;给客户反馈信息,服务结束;等待下一个请求 到来,如此循环 。重复型交互的缺点是在服务器进行客户服务处理时,不能接受其他客户的请

4、求;而并发型交互就可以解决此问题,但需要多线程技术的支持。由于作业要求请求与计算是多对多关系,并且有队列保存请求,所以设计采用重复型交互方式进行加减乘除服务线程池设计可以满足设计要求,并且需要分别编写服务器端和客户端的程序,服务器和客户端之间相互通信的同步关系和各自的程序流程如图2所示。图2 客户/服务器通信流程图事实上,send()和recv()函数默认都是阻塞方式通信的,但由于本次设计要求的通信数据小于1024字节,所以为了通信方便,编程时可以把服务器端的套接字修改为非阻塞方式,这样一般不会造成数据的丢失,且可以方便客户端的并发请求得到响应。2.1网络套接字(socket)的概念上世纪中后

5、期,在美国国防部高研署(DARPA)将TCP /IP 的软件提供给加利尼亚大学Berkeley 分校后,TCP /IP 很快被集成到Unix 中,同时出现了许多成熟的TCP /IP 应用程序接口(API)。这个API 称为Socket 接口( 套接口)。Socket 在计算机中提供了一个通信端口, 可以通过这个端口与任何一个具有Socket 接口的计算机通信。应用程序在网络上传输,接收的信息都通过这个Socket 接口来实现。在应用开发中就像使用文件句柄一样, 可以对Socket 句柄进行读、写操作。今天,Socket 接口是TCP /IP 网络最为通用的API,也是在Internet 上进行

6、应用开发最为通用的API。Linux 操作系统具有良好的稳定性和出色的网络性能,因此被广泛应用于网络服务领域。而在Linux下开发高性能的网络通信程序,是充分发挥Linux 网络特性的一个关键因素。 当用电话与他人通信时,必须拿起话筒,拨叫对方的电话号码, 然后等待对方的应答; 当双方进行通话的时候, 就建立了一个具有两个端点的通信线路,这两个端点是:本地的电话号码(在本地位置);对方的电话号码(在对方的位置处)。双方的通信与通信的两个端点和他们之间的通信线路有关。Linux 中的套接口与电话非常相似。套接口代表通信线路中的端点,两端点之间就是数据通信网络。套接口与电话的相似性还表现在另一方面

7、。当给某人打电话时, 拨叫的是对方用户的电话号码。而套接口中的网络地址就相当于电话号码。通过在程序中指定远程套接口的地址, 就可以建立从本地套接口到远端套接口的通信。TCP /IP 是计算机互连最常使用的网络通讯协议, TCP /IP 的核心部分由网络操作系统的内核实现,应用程序通过编程接口来访问TCP /IP。套接字( Socket) 是介于网络应用层和传输层之间的编程接口, 套接字接口提供了访问下层通信协议的大量系统调用和相应的数据结构。在Linux 中, 套接字接口是应用程序访问下层的网络协议的惟一方法。具体讲,套接字在用户级实现了两个应用程序之间的网络连接和数据交换, 所以Linux

8、中的套接字意味着网络上的连接。套接字在TCP /IP 网络模型中的地位如图3所示。图3 socket的TCP/IP模型结构图Socket 分为以下三种类型。 流式套接字( Stream Socket) : 是最常用的套接字类型, 文件传送协议( FTP) 即使用流式套接字。提供一个面向连接、可靠的数据传输服务, 数据无差错、无重复地发送, 且按发送顺序接收。内设流量控制, 避免数据流超限; 数据被看作是字节流, 无长度限制。数据报套接字(Datagram Socket) : TCP /IP 协议族中的UDP 协议使用此类接口, 它是无连接的服务,数据通过相互独立的报文进行传输, 提供了一个无连

9、接服务。数据包以独立包形式被发送, 不提供无错保证, 数据可能丢失或重复, 并且接收顺序混乱。原始数据报套接字(Raw Socket) : 该接口允许对较低层协议, 如IP、ICMP 直接访问。常用于检验新的协议实现或访问。2.1. Socket 通信过程 基于TCP 可靠连接的客户与服务器连接进程流程图如图4所示。图4 基于TCP的客户服务器模型Socket 工作过程如下: 服务器首先启动, 通过调用Socket(), 建立一个Socket, 然后调用bind()将该Socket 和本地网络地址绑定在一起, 再调用listen()使Socket 做好侦听的准备, 并规定它的请求队列的长度,之

10、后就调用accept()来接收连接。客户在建立Socket 后就可调用connect()和服务器建立连接。连接一旦建立,客户机和服务器之间就可以通过调用send()和recv()来发送和接收数据。最后, 待数据传送结束后, 双方调用close()关闭Socket。2.2. Socket 通信程序开发下面详细说明Socket 通信程序的开发过程:步骤1: 建立套接口socket()int sockfd = socket ( int domain, int type, int protocol)其中domain 参数指定socket 协议族, 包括PF_LOACL 和PF_INET, PF_LOA

11、CL 表示使用本地套接口, PF_INET 表示使用Internet 套接口。Type 参数定义了套接口的类型, 包括SOCK_STREAM 和SOCK_DGRAM, SOCK_STREAM 指定为流套接口,SOCK_DGRAM指定为数据报套接口。protocol 通常赋值0, 意味套接口使用默认的TCP /IP 协议。socket()调用返回一个非负整型socket 描述符, 可以在后面的调用使用它, 当其值为- 1 时, 说明有错误发生。 步骤2: 绑定套接口bind()当用socket()建立套接口后, 该套接口还是处于无名状态的, 无名套接口就像没有号码的电话一样,别人无法向发送信息(

12、 在同一linux 内核下可实现无名状态下通信) 。为了像电话分配电话号码一样, 可以通过bind()为建立的套接口绑定一个名字,即分配IP地址和 端口。这一步对客户端不是必需的。int bind ( int sockfd, struct sockaddr _my_addr,int addrlen)sockfd 是一个socket 描述符, my_addr 是一个指向包含有本机IP 地址及端口号等信息的sockaddr 类型的指针; addrlen 常被设置为sizeof ( structockaddr) , 如果函数调用成功,就返回0,否则就返回- 1。需要指出的是, 可以用下面的赋值实现自

13、动获得本机IP 地址和随机获取一个没有被占用的端口号:my_addr.sin_port=0; 系统随机选择一个未被使用的端口号:my_addr.sin_addr.s_addr=INADDR_ANY; 填入本机IP 地址,通过将my_addr.sin_port 置为0, 函数会自动为选择一个未占用的端口来使用。同样, 通过将my_addr.sin_addr.s_addr 置为INADDR_ANY, 系统会自动填入本机IP 地址。bind()函数在成功被调用时返回0; 遇到错误时返回- 1。另外要注意的是, 当调用函数时, 一般不要将端口号置为小于1024 的值, 因为11024 是保留端口号,

14、可以使用大于1024 中任何一个没有被占用的端口号。步骤3: 请求连接connect()当客户端绑定地址后, 发送请求连接信号connect()来与远端TCP服务器建立连接,一般用于客户端编程。connect()函数原型为:int connect ( int sockfd, struct sockaddr serv_addr, int addrlen)sockfd 是客户端的socket 描述符; serv_addr是包含目的机IP 地址和端口号的指针, addrlen 为结构的大小。遇到错误时返回- 1。进行客户端程序设计无须调用bind(), 因为这种情况下只需知道目的机器的IP 地址,

15、而客户通过哪个端口与服务器建立连接并不需要关心, 内核会自动选择一个未被占用的端口供客户端来使用。步骤4: 监听连接listen()在服务器端程序中, 当socket 与某一端口捆绑以后, 就需要监听该端口, 以便对到达的服务请求加以处理。int listen( int sockfd, int backlog)sockfd 是Socket 系统调用返回的socket 描述符;backlog 指定在请求队列中允许的最大请求数, 进入的连接请求将在队列中等待accept()它们。backlog 对队列中等待服务的请求的数目进行了限制, 对于小型服务器, 队列长度应该为5 或是稍大一些的值, 而对于

16、网站服务器, 我们就需要更大的值, 比如说16 或是更大。当调用socket()函数建立套接口时,该接口默认为主动套接口,主动套接口的含义是指该套接口是用于主动发起连接的客户端套接口,一旦调用listen()函数,则套接口变为被动套接口,就是说,套接口变为被动接受连接的服务器端套接口。Listen()函数返回值为0表示调用成功, - 1表示调用出错。步骤5: 连接端口的服务请求当某个客户端试图与服务器监听的端口连接时,该连接请求将排队等待服务器accept()它。通过调用accept()函数为其建立一个连接, accept()函数将返回一个新的socket 描述符, 来供这个新连接来使用。而服

17、务器可以继续在以前的那个socket 上监听, 同时可以在新的socket 描述符上进行数据发送send ()和接收recv()操作。int accept( int sockfd, void *addr, int *addrlen)sockfd 是被监听的socket 描述符, addr 通常是一个指向sockaddr_in 变量的指针, 该变量用来存放提出连接请求服务的主机的信息( 某台主机从某个端口发出该请求) ; addrlen 通常为一个指向值为sizeof( struct sockaddr_in) 的整型指针变量。错误发生时返回一个-1。accept()之前, 通常将addrlen

18、初始化为0。步骤6: 数据传输send()和recv()send()和recv()这两个函数是用于面向连接的socket 上进行数据传输。它们并不是真的从网络中发送/接收数据,而是对套接口的数据 缓冲区进行发送/读取 ,二者默认方式都是阻塞方式。send()函数原型为:int send ( int sockfd, const void *msg, int len, int flags)sockfd 是想用来传输数据的socket 描述符, msg是一个指向要发送数据( 可以是字符型、整型、浮点型等) 的指针,len 是以字节为单位的数据的长度。flags一般情况下置为0。send()函数返回实

19、际上发送出的字节数, 可能会少于希望发送的数据。所以需要对send()的返回值进行测量。当send()返回值与len 不匹配时, 应该对这种情况进行处理。recv()函数原型为:int recv ( int sockfd, void *buf, int len, unsigned int flags)sockfd 是接收数据的socket 描述符; buf 是存放接收数据的缓冲区; len 是缓冲的长度。flags 也被置为0。recv()返回实际上接收的字节数, 或当出现错误时,返回- 1。步骤7: 关闭连接close()或shutdown()当所有的数据操作结束以后, 可以调用close(

20、)或shutdown()函数来关闭套接口。TCP连接是全双工的连接 ,即在同一个连接上既可以发送数据,又可以接收数据。在调用close()函数后,套接口既不能用于读取数据,也不能用于发送数据。而调用shutdown()函数允许指定只关闭一个方向(读或写)的通信或者关闭双向通信。2.4多线程的概念上述点对点通信的实现知识完成了主机进程与服务器进程之间的连接,建立连接的进程之间是一对一的联系,即主机的一个进程与服务器的一个进程之间建立的连接。而每个进程进行通信的环节都包括了发送信息和接口信息两个任务,这两个任务通过一个端口地址发送和接收。对于多个并发的任务需要创建多个线程去实现。使用一个进程去完成

21、发送信息是没有问题的,因为发送总是主动的;而使用同一个进程再去完成接受信息去不一定会成功,因为接受信息是被动的,所以当没有信息可以接收时,该进程就会被阻塞,从而导致发送任务也一起被阻塞。同一个端口的发送和接收是两个并发任务,应该由两个不同的任务去分别完成信息的发送和接收。这样,当接收信息任务因没有信息而被阻塞时,不至于影响发送任务的执行。那么,发送和接收两个任务是使用两个进程还是两个进程去完成呢?在网络通信中,端口地址是以进程为单位进程分配的,而一个进程与外界的消息发送与接收必须通过分配给它的同一个端口进行。因此,不能通过创建进程方式来解决上诉问题,因为两个进程会分别对应两个不同的端口,而发送

22、和接收必须使用同一端口。线程不是资源分配的单位,所以如果使用两个线程不会对线程分配新的端口。因此,本实验需要使用两个线程去分别完成发送和接收信息的任务,这两个线程共享其进程拥有的统一个端口地址。由于创建进程的进程本身会作为一个线程来调度,所以只需要再创建一个线程专门负责接收信息就可以了。因此,对于从每个客户端发来的请求,服务器端都要创建相应的线程去接收并处理;同理,对于客户端而言,也要创建一个线程去读取服务器端发来的信息。3.程序设计实现传输数据结构形式为保证计算数据的正确发送给服务器 ,需要首先设计传输数据结构。具体定义如下:typedef struct mathoptint oprate;

23、float value1;float value2;mopt;其中,oprate为操作类型,取值为1、2、3、4、5、6,分别代表加、减 、乘、除、帮助、退出;value1与value2是加减乘除的第一和第二操作数。线程状态标志数据结构 线程状态主要有请求队列、当前处理的套接字、线程工作状态标志位等,具体结构 定义如下:typedef struct threadatomqueue<int>waitque;/请求队列 int currentsocket;/当前处理的请求套接字int flag; / 工作标记值tatom; 其中,请求队列waitque中保存的是请求客户端的套接字;cu

24、rrentsocket是线程当前处理的请求套接字,主要用于线程池状态的 显示;flag是线程工作状态标志位,置1表示线程正在处理客户端请求,置0表示线程 处于空闲等待状态。服务端线程处理函数线程处理函数是整个 服务端的核心,主要负责请求数据的接收、结果计算、返回计算结果等任务。线程处理函数的流程图如图 5所示:图5 线程程序流程图四、测试1.程序编译因为线程pthread的库不是linux系统的库所以在对程序编译的时候要加上-lpthread,编译用法如下:#gcc filename lpthread o resultname服务端程序server2.c和客户端程序client.c编译过程截图

25、如图6所示:图6 server2.c和client.c编译过程2.程序调试在实际调试中,发现由于处理服务请求的时间非常短,所以无法明确看出保存客户端套接字的队列是否起作用,所以为了真正测试程序,在处理服务请求程序段中使用sleep()函数适当延长服务时间,方便调试队列作用。另外,考虑到电脑屏幕有限,所以设定计算线程数为2,客户端数量上限受到listen()函数限制,设定最大为16,在实际调试中,采用一个服务器,三个客户端的形式,端口号为5678,IP地址为,以下图片为测试过程截图:图7 服务端启动后界面图8 添加一个客户端后界面图9 添加三个客户端后界面图10 处理计算请求后

26、界面图11 客户端退出界面五、对本课程或本作业的建议和意见通过本作业的设计学习,我更加熟悉了linux下的网络套接字和线程编程过程,对于以后的学习打下了很好的基础。关于对课程的建议,我希望以后可以更多的在机房上课,让学生在学习理论的同时,更加直观的实际操作,这样或许对于学习的知识印象更加深刻。六、附录1.服务端程序server2.c:#include <iostream>#include <stdlib.h>#include <string.h> #include <queue> #include <fcntl.h> #include

27、 <pthread.h> #include <sys/socket.h>#include <arpa/inet.h> using namespace std;#define SERVER_PORT 5678/定义端口号#define MAX_NUM 2/定义计算线程数typedef struct mathopt /定义计算数据的数据结构int oprate;float value1;float value2;mopt;typedef struct threadatom/定义线程状态的数据结构queue<int>waitque;/请求队列int c

28、urrentsocket;/当前工作套接字int flag;/ 工作状态标志位:1 working;0 waiting tatom;struct threadatom threadpoolatomMAX_NUM;/定义线程状态数组pthread_t threadidMAX_NUM;/定义线程ID数组void cleanthreadpool()/线程池初始化int i;for(i = 0; i < MAX_NUM; i+)while(!threadpoolatomi.waitque.empty() threadpoolatomi.waitque.pop();/队列清空threadpoola

29、tomi.currentsocket = 0;threadpoolatomi.flag = 0;threadidi = 0;void showthreadstatus()/显示线程池里各线程的状态信息int i;for( i = 0; i < MAX_NUM; i+) cout<<"i = "<<i<<":threadid"<<i<<" clientsocket"<<threadpoolatomi.currentsocket<<" fl

30、ag"if(threadpoolatomi.flag>=1)cout<<"working waitsize"elsecout<<"waiting waitsize"cout<<threadpoolatomi.waitque.size()<<''<<endl;void* processthread(void *para)/线程处理函数int index = (int)para;/线程标号cout<<"Entery the processthre

31、ad "<<index<<" and waiting"<<endl;while(1)while(!threadpoolatomindex.waitque.empty()/队列非空开始处理int iDataNum;char buffer1024;int tempclient=threadpoolatomindex.waitque.front();/取客户端套接字fcntl(tempclient,F_SETFL,O_NONBLOCK);/将套接字变为非阻塞iDataNum = recv(tempclient,buffer,1024,

32、0);/接收计算数据int length = sizeof(struct mathopt);if(iDataNum < length)/数据不完整,跳出threadpoolatomindex.waitque.pop();/将套接字弹出放到队尾threadpoolatomindex.waitque.push(tempclient); threadpoolatomindex.flag = 0;/工作标志位清零continue ;threadpoolatomindex.currentsocket=tempclient;threadpoolatomindex.flag=1;/数据完整,置位工作标

33、志位cout<<"now show thread pool status:"<<endl;showthreadstatus();/显示线程池状态if(threadpoolatomindex.flag)/计算struct mathopt *pMp = (struct mathopt *)buffer;float result = 0;if(pMp->oprate = 1)result = pMp->value1 + pMp->value2;else if(pMp->oprate = 2)result = pMp->valu

34、e1 - pMp->value2;else if(pMp->oprate = 3)result = pMp->value1 * pMp->value2;else if(pMp->oprate = 4)result = pMp->value1 / pMp->value2;else if(pMp->oprate = 6)/客户端退出处理threadpoolatomindex.waitque.pop();close(threadpoolatomindex.currentsocket);cout<<"Clientsocket &qu

35、ot;<<threadpoolatomindex.currentsocket<<" disconnect!"<<endl;threadpoolatomindex.currentsocket=0;threadpoolatomindex.flag= 0;continue;else continue;sleep(15); /测试队列效果用char buf100;char ASMM4='+','-','*','/'sprintf(buf,"%f",result);

36、cout<<"Task: "<<pMp->value1<<ASMMpMp->oprate-1<<pMp->value2<<"="<<result<<" Done"<<endl;send(threadpoolatomindex.currentsocket,buf,sizeof(buf),0);/发送计算结果threadpoolatomindex.currentsocket=0;/工作套接字清零threadpoolatomin

37、dex.flag = 0;cout<<"now show thread pool status:"<<endl;showthreadstatus();/显示线程池状态sleep(1);/无任务,休息1sint main(int argc, char const *argv)/主函数int serverSocket;/声明服务端套接字struct sockaddr_in server_addr;struct sockaddr_in clientAddr;int addr_len = sizeof(clientAddr);if(serverSocket

38、= socket(AF_INET,SOCK_STREAM,0) < 0)/创建服务端套接字cout<<"error: create server socket!"<<endl;exit(1);bzero(&server_addr,sizeof(server_addr);/清空服务端套接字数据结构server_addr.sin_family =AF_INET;/服务端套接字数据结构赋值server_addr.sin_port = htons(SERVER_PORT);server_addr.sin_addr.s_addr = htonl(

39、INADDR_ANY);if(bind(serverSocket,(struct sockaddr *)&server_addr,sizeof(server_addr) < 0)/绑定IP和端口号cout<<"error: bind address !"<<endl;exit(1);if(listen(serverSocket,16)<0)/监听套接字,客户端最多为16cout<<"error: listen !"<<endl;exit(1);cleanthreadpool();/线程池

40、初始化int i;for( i = 0 ; i < MAX_NUM; i+)/创建计算线程pthread_t temp;cout<<"i = "<<i<<endl;int err = pthread_create(&temp, NULL, processthread, (void *)i);if(err = 0)cout<<"thread"<<i<<" start ok."<<endl;elsecout<<"thre

41、ad"<<i<<" create failed!"<<endl;sleep(2);cout<<"init threadpool status:"<<endl;showthreadstatus();/显示线程池状态while(1)int clientsocket;cout<<"accetp conn."<<endl;clientsocket = accept(serverSocket,(struct sockaddr *)&client

42、Addr,(socklen_t*)&addr_len);/更新与客户端连接套接字if(clientsocket < 0)cout<<"error: accept client socket !"<<endl;continue;cout<<"find pool."<<endl;/把客户端套接字放到队列最小的线程中int MinSize=threadpoolatom0.waitque.size();int temp=0;for( i = 0 ; i < MAX_NUM; i+)if(MinS

43、ize>threadpoolatomi.waitque.size() temp=i;threadpoolatomtemp.waitque.push(clientsocket);cout<<"now show thread pool status:"<<endl;/显示线程池状态showthreadstatus();close(serverSocket);return 0;2.客户端程序client.c:#include <iostream>#include <stdlib.h>#include <iomanip&g

44、t;#include <sys/socket.h>#include <arpa/inet.h>using namespace std;#define PORT 5678/定义端口号#define SERVER_IP ""/定义IP地址typedef struct mathopt/定义计算数据的数据结构int oprate;/操作类型float value1;/第一操作数float value2;/第二操作数mopt;void createopt(struct mathopt *pMp)/获取计算数据cout<<"

45、;please input operand one:"cin>>pMp->value1;cout<<"please input operand two:"cin>>pMp->value2;void help()/帮助菜单显示 cout<<"date:2014.10"<<endl; cout<<"author:yang"<<endl; cout<<"="<<endl; cout<&l

46、t;left<<setw(6)<<"CmdNo"<<setw(20)<<"Fuction"<<endl; cout<<left<<setw(6)<<"1"<<setw(20)<<"Adder computing"<<endl; cout<<left<<setw(6)<<"2"<<setw(20)<<&quo

47、t;Minus computing"<<endl; cout<<left<<setw(6)<<"3"<<setw(20)<<"Multiply computing"<<endl; cout<<left<<setw(6)<<"4"<<setw(20)<<"Divide computing"<<endl; cout<<left<<setw(6)<<"5"<<setw(20)<<"he

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论