《并发服务器》PPT课件.ppt_第1页
《并发服务器》PPT课件.ppt_第2页
《并发服务器》PPT课件.ppt_第3页
《并发服务器》PPT课件.ppt_第4页
《并发服务器》PPT课件.ppt_第5页
已阅读5页,还剩53页未读 继续免费阅读

下载本文档

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

文档简介

第五章 并发服务器,并发服务器基础 多进程服务器 多线程服务器 I/O多路复用服务器 非阻塞socket 信号驱动I/O,5.1并发服务器基础(1),理解多客户问题,连接到一个服务器上的多客户示意图 服务器以并发方式为多客户提供服务,5.1并发服务器基础(2),服务器分类 按连接类型 面向连接的服务器 采用TCP协议,可靠。不足之处是:每个连接需要相应的套接字,服务器要管理这些套接字,占用系统资源;连接需要建立和关闭过程,影响传输效率和响应时间,增加服务器负担。 无连接的服务器 采用uCP协议,但效率高,占用较少系统资源,服务器不必管理连接套接字。缺点:不可靠,需要程序中实现相应机制。 按处理方式 重复性服务器:每次只处理一个客户请求,当上一个客户请求处理完成后,才处理下一个请求,简单,效率低。 并发服务器:每次可处理多个客户请求,复杂,效率高。,5.1并发服务器基础(3),重复性服务器 实现:使用setsockopt()的SO_REUSEADDR选项实现 典型代码: int opt, len; len=sizeof(opt) opt=SO_REUSEADDR; setsockopt(fd,SOL_SOCKET,SO_REUSEADDR, 实例: 源程序名:itrclient.cpp和itrserver.cpp,5.1并发服务器基础(4),功能: 服务器等候客户连接,连接显示信息,并循环接收来自客户的信息;收到后显示之,反转后发回客户,直到某客户端输入“bye”关闭连接,就能等待下一客户请求。 客户首先与服务器连接,接收键盘输入字符串,发给服务器,接收服务器发回信息并显示,循环,直到bye,关闭连接。 运行程序: 服务器:%itrserver You got a connection from 127.0.0.1 received client message: 1234 客户1:%itrclient 127.0.0.1 connected to server. 输入: 1234 输出: 4321 (可以反复输入,均得到反序字符串,直到“bye”为止) 思考: 客户2、客户3同时运行该程序,结果如何?,5.1并发服务器基础(5),并发服务器与并发技术 进程 进程是程序的一次运行。多个进程可以执行相同的代码(如父子进程),支持并发;单CPU采用分时系统使多个进程同时执行。通过主进程和子进程实现并发,主进程只接收客户请求,子进程只与客户通信。 线程 与进程类似,也支持并发。同一进程中的线程共享相同的全程变量和系统分配给该进程的资源,线程间切换更快。进程是最小的资源分配单位,而线程是最小的调度单位。 I/O多路复用 系统提供select()函数,在多个描述符中选择被击活的描述符进行操作。如一个服务器同时与多个客户端连接,就有多个TCP套接字描述符,采用时分多路复用技术实现进程或线程的并发处理。,5.1并发服务器基础(6),三种并发服务器算法 (1)并发无连接服务器算法 无连接服务,不需要等待每个已连接上的数据,不必采用并发技术也能有效处理多个用户,用多进程/线程需处理大量系统开销,因此无连接服务一般不采用并发技术。 (2)并发面向连接服务器算法 通过主进程/线程和子进程/线程实现并发,主进程只接收客户请求,子进程只与客户通信。 (3)单进程/线程的并发服务器算法 进程/线程需要较多的系统开销,采用I/O多路复用实现处理多个客户连接,不必产生多个进程/线程。,5.2多进程服务器(1),服务器(父进程),服务器 (子进程),listenfd connfd,connect()函数,客户,连接建立,listenfd connfd,fork()函数,listenfd connfd,connect()函数,客户,连接建立,listenfd connfd,服务器(父进程),服务器 (子进程),(a)调用accept()前,(b)调用accept()后,(c) 调用fork()后,(d),主服务器进程和子服务器进程实现并发,连接请求,父进程关闭连接套接字 子进程关闭侦听套接字,5.2多进程服务器(2),进程概念 进程是程序在一个数据集上以并发方式运行的一次过程。 进程是程序的动态执行。 有生存期。 由程序、数据、进程控制块构成。 一个进程可包括多个程序,一个程序也可对应多个进程。 每个进程有唯一的进程标识符pid。 用ps命令看当前系统运行的进程。,5.2多进程服务器(3),系统调用 创建进程fork() 原型:#include #include pid_t fork(void); 功能:创建一个新进程,父子进程共享代码段。 返回值:子进程中为0,父进程中为子进程ID, 出错为-1。 注:用getpid()可以获得当前进程标识符,用getppid()可以获得父进程标识符;通过分支语句控制父子进程执行不同代码(如C/S模式中)。 典型代码: pid_t pid; if(pid=fork()0) /*parent process*/ else if(pid=0) /*child process*/ ;exit(0); else cout“fork error!n”;exit(0);,系统中已经有太多的进程或某用户的进程总数超过了系统限制,5.2多进程服务器(4),例1:利用fork()系统调用创建子进程pp1.cpp 例2:父子进程享用共同的代码pp2.cpp 说明:调用fork()后, 父进程/子进程执行顺序不确定 程序pp1运行结果 程序pp2运行结果,Just 1 process now. Calling fork() Im the child. Program end. Im the parent. Program end.,pid0=294 i=0 j=0 pid=297 ppid=295 (4) i=0 j=297 pid=295 ppid=294 (2) i=295 j=296 pid=294 ppid=197 (1) i=295 j=0 pid=296 ppid=294 (3),i=fork(),j=fork(),j=fork(),5.2多进程服务器(5),终止进程exit()、wait()和waitpid 1.exit() 原型:#include void exit(int status); 功能:终止进程,常用于关闭子进程打开的描述符,并返回状态,父进程用wait()、waitpid()获得该状态。 返回值:无 注:子进程中使用exit()不能完全释放子进程所占用的资源,进程控制块PCB中还保留它的信息,子进程变为“僵尸”进程,ps能看到这样的进程,直到父进程调用wait()后完全释放子进程资源。,5.2多进程服务器(6),父进程可能先于子进程终止,子进程也可能先于父进程终止。 2.wait() 原型:#include #include pid_t wait(int *stat_loc); 功能:在有子进程终止时,系统使用中断信号SIGCHLD通知父进程有子进程终止,此时才能调用wait() 。wait()允许父进程取得子进程的状态信息,并挂起,等待子进程终止。 返回值:正常,返回子进程进程号,否则-1;同时stat_loc返回子进程返值。 典型代码: pid_t pid; int child_status; if(pid=fork()0) /*parent process*/;wait(,5.2多进程服务器(7),原型:#include #include pid_t waitpid(pid_t pid,int *stat_loc,int options); 功能:允许父进程取得指定子进程的状态信息,其中:pid是所等待的子进程号,-1时表示第一个子进程; options表示等待的方式,如WNOHANG表示无子进程结束时返回; 当pid=-1, options=0时,等价于wait()。 返回值:正常,返回子进程号,否则-1;stat_loc返回子进程返值 注:若有若干客户同时关闭连接,这些服务进程的父进程同时收到若干SIGCHLD中断信号,由于系统对中断信号无队列设置,导致某些中断信号被丢弃,仍产生“僵尸”进程。waitpid()可以控制无子进程结束时返回。 典型代码: clear_child(int signo) int stat,pid; while(pid=waitpid(-1,5.2多进程服务器(8),多进程并发服务器 方法 用前面所学方法在服务器和客户端建立连接。 服务器调用fork()产生子进程。 若子进程,关闭监听套接字(保留连接套接字),处理客户请求,最后关闭连接套接字,用exit()退出进程。 若父进程,关闭连接套接字(保留监听套接字,套接字描述符的引用计数减1),等待另一客户的连接。,5.2多进程服务器(9),典型代码 #include #include #include #include main() ; listenfd=socket(); bind(listenfd,); listen(listenfd,); while(1) connfd=accept(); if(pid=fork()0)/*parent process*/;close(connfd); ; continue; else if (pid=0) /*child process*/;close(listenfd); ; exit(0); else cout“fork errorn”; exit(0); ,5.2多进程服务器(10),实例1 源程序名:procserver.c和procclient.c 功能描述:服务器等候客户连接请求,连接成功后显示客户地址,接收客户名字并显示,循环接收客户字符串并显示,反序处理后发回客户端,服务器可同时处理多客户请求。而客户端先与服务器连接,接收键盘输入的客户名字发给服务器,循环接收用户输入的字符串发给服务器,接收服务器发回的信息并显示,直到D客户关闭连接并退出。 运行程序: 服务器:$procserver You got a connection from 127.0.0.1.clients name is client1. Received client(client1) message:1234 You got a connection from 127.0.0.1.clients name is client2. Received client(client2) message:abcd,5.2多进程服务器(11),客户1:$procclient 127.0.0.1 Connected to server. Input name:client1 Input string to server:1234 Server Message:4321 客户2:$procclient 127.0.0.1 Connected to server. Input name:client2 Input string to server:abcd Server Message:dcba,5.2多进程服务器(12),实例2 源程序名:multi.h mulprosrv.cpp mulprocli.cpp 功能描述:服务器等候客户连接请求,连接成功后显示客户地址和端口号,循环接收客户的两个整数,相加后发回客户端。服务器可同时处理多客户请求。 客户端首先与服务器连接,循环接收用户输入的两个整数发给服务器,接收服务器发回的两数和的信息并显示,询问是否继续(1或0),0返回。 运行程序: 服务器:$./mulprosrv one client is request service from 127.0.0.1 at PORT 32769(客户1) in process 1231:12+23=35 in process 1231:6+8=14 one client is request service from 202.113.29.19 at PORT 32770(客户2) in process 2050:2+3=5 client 127.0.0.1 closed child process 1231 cleaned return 0,5.2多进程服务器(13),客户1: $./mulprocli 127.0.0.1 connect to host 127.0.0.1 enter an interger:12 enter another interger:23 12+23=35 would you like to continue(1:continue,0:end):1 enter an interger:6 enter another interger:8 6+8=14 would you like to continue(1:continue,0:end):0 客户2:$./mulprocli 202.113.29.19 .,5.3多线程服务器(1),线程基础 线程能提高代码响应和性能,线程由系统内核按时间片进行管理,在单处理器系统中,系统内核使用时间分片模拟线程的并发执行,在多处理器系统中,如同多个进程,线程也可以并发执行。 同一进程中的线程共享如下内容: 全局变量 数据 虚拟内存 打开的文件描述符 当前工作目录 用户及用户组ID 每个线程具有独立的: 线程ID errno变量 优先级,5.3多线程服务器(2),POSIX的多线程库 1. pthread_create() 原型:#include int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void * (* start_func)(void *),void *arg); 功能:程序运行时系统产生主线程,该函数产生其它线程,其中: thread:指向线程ID的指针,pthread_t就是unsigned int。 attr:指向线程属性的指针,NULL为默认值。 start_func:指向线程执行的函数(通用函数指针)。 arg:为函数start_func传递参数,若传递多个参数,须封装在一个结构中。 返回值:成功返回0,否则返回非零的错误代码。 注: fork()创建的子进程与其父进程使用同一个执行点,而pthread_create()中的参数start_func指明新线程要执行的函数,5.3多线程服务器(3),典型代码: #include pthread_t thread; struct ARG int fd; struct sockaddr_in addr; arg; void *start_routine(void *arg); void main() if(pthread_create( / pthread_create()返回非零,表明创建线程失败 ,5.3多线程服务器(4),2. pthread_exit() 原型:#include void pthread_exit(void *value_ptr); 功能:终止当前线程, 若value_ptr非空则指向线程退出状态。一般用法取NULL 。 返回值:无,若在线程中调用exit(),将终止该进程所有线程. 典型代码:(常用在线程执行的函数中) void *start_routine(void *arg) pthread_exit(NULL); ,5.3多线程服务器(5),多线程并发服务器 方法 用前面所学方法在服务器和客户端建立连接 服务器调用pthread_create()产生新线程 若主线程,等待另一客户的连接请求 若新线程,处理客户请求 给新线程传递参数 普通方法:使用公共变量传参 通过分配arg的空间传递参数 注意 编译时要和线程库libpthread.a或libpthread.so相连接,如: g+ thrserver.cpp o thrserver -lpthread,静态链接库,动态链接库,5.3多线程服务器(6),1.普通方法 void *start_routine(void *arg); struct ARG int connfd; int other; ; void main() struct ARG arg; int listenfd,connfd; pthread_t thread; /*connfd=accept()*/ arg.connfd=connfd; arg.other=5; if(pthread_create( ,void *start_routine(void *arg) struct ARG info; info.connfd=(struct ARG *)arg)-connfd; info.other=(struct ARG *)arg)-other; close(info.connfd); pthread_exit(NULL); 说明: 变量arg是所有线程共用的,传递的参数为指针,假设新线程A正处理客户A请求,主线程又接受另一客户B的连接,主线程将修改变量arg内容,故只能处理一个客户,无法同时处理多个客户(不安全)。,5.3多线程服务器(7),5.3多线程服务器(8),2.通过动态分配arg空间传递参数 void *start_routine(void *arg); struct ARG int connfd; int other; void main() int listenfd,connfd; ARG *arg; pthread_t thread; /*connfd=accept()*/ arg=new ARG; arg-connfd=connfd; arg-other=5; if(pthread_create( ,void *start_routine(void *arg) ARG info; info.connfd=(ARG *)arg)-connfd; info.other=(ARG *)arg)-other; ; close(info.connfd); delete arg; pthread_exit(NULL); 说明: 创建新线程前,为新线程动态分配存储arg的空间,传递参数给新线程,新线程使用后释放arg。,5.3多线程服务器(9),5.3多线程服务器(10),实例1 源程序名:thrserver.cpp和procclient.cpp (方法2传参) 功能描述:与5.2中实例1功能相同 编译过程:$g+ thrserver.cpp o thrserver -lpthread 运行程序: 服务器:$thrserver You got a connection from 127.0.0.1.clients name is wen. Received client(wen) message:1234 You got a connection from 127.0.0.1.clients name is zhang. Received client(zhang) message:abcd 客户1:$procclient 127.0.0.1 Connected to server. Input name:wen Input string to server:1234 Server Message:4321 客户2:$procclient 127.0.0.1 ,5.3多线程服务器(11),实例2 源程序名: multhrsrv.cpp和mulprocli.cpp (方法1传参) 功能描述:与5.2实例2中功能相同,所不同的是服务器采用多线程服务器提供并发服务。 运行程序: 服务器:$./multhrsrv Process Idis 423 one client is request service from 127.0.0.1 at PORT 32769(客户1) thread 1231 will process the request in thread 1231:12+23=35 in thread 1231:6+8=14 one client is request service from 127.0.0.1 at PORT 32770(客户2) thread 2050 will process the request in thread 2050:2+3=5 client 127.0.0.1 closed and thread 1231 TERMINATE client 127.0.0.1 closed and thread 2050 TERMINATE,5.3多线程服务器(12),线程安全 由于同一进程中的所有线程共享相同的存储空间,如果多个线程修改相同的内存区域可能会出现错误,这就是线程安全问题。 实例3: 源程序名:thrserver1.cpp和procclient.cpp 功能描述:修改4.3中实例1,增加一个函数savedata(),希望将每个线程接收的所有信息保存,线程终止时显示,但结果与想象不符。原因是调用了非线程安全的函数savedata(),该函数中包含了一个静态变量(多线程共享变量) 结论:在多线程环境中应避免使用静态变量,而采用线程专用数据(TSD)取代静态变量,它是线程私有的,是定义线程私有数据的唯一方法。,5.3多线程服务器(12),运行程序: 服务器: $./thrserver1 You got a connect from 127.0.0.1.clients name is wen. Received client(wen) message:1234 You got a connect from 127.0.0.1.clients name is zhang. Received client(zhang) message:abc Received client(wen) message:5678 Client(wen) closed connection.users data:1234 Received client(zhang) message:def Client(zhang) closed connection.users data: 客户1: $procclient 127.0.0.1 Connected to server. Input name:wen Input string to server:1234 /依次输入1234、5678和bye Server Message:4321 客户2: $procclient 127.0.0.1 /依次输入abc、def和bye,5.3多线程服务器(13),与TSD有关的函数 pthread_key_create() 原型:#include int pthread_key_create(pthread_key_t *key, void(*destructor)(void *value); 功能:在进程内部分配一个标志TSD(线程专用数据)的关键字,创建进程时为NULL,每个线程可以为关键字绑定一个值,该值对绑定的线程是唯一的(创建线程时为NULL),一般创建第一个新线程时使用。其中: key:指向创建的关键字。 destructor:为析构函数,若它非空,key也非空,则线程退出时调用析构函数。 返回值:正常返回0,否则返回错误码。,5.3多线程服务器(14),pthread_setspecific() 原型:#include int pthread_setspecific(pthread_key_t *key,void *value) 功能:为TSD关键字绑定一个与本线程相关的值。其中value为相关值。 返回值:正常返回0,否则返回错误码。 pthread_getspecific() 原型:#include void *pthread_getspecific(pthread_key_t *key) 功能:获得与调用线程相关的关键字所绑定的值。 返回值:正常返回与调用线程相关的关键字所绑定的值,若未绑定返回NULL。,5.3多线程服务器(15),典型代码: (某线程处理函数中) pthread_key_t key; pthread_key_create( ,5.3多线程服务器(16),pthread_once() 原型:#include void *pthread_once( pthread_once_t *once_control, void (*init_routine)(void) ); 功能:初始化动态包,当设置为PTHREAD_ONCE_INIT时,所指向的函数在同一进程中只被调用一次。其中: once_control=PTHREAD_ONCE_INIT init_routine:初始化时所指向的函数。 返回值:正常返回0,否则错误码。,5.3多线程服务器(17),典型代码: static pthread_key_t key; static pthread_once_t once=PTHREAD_ONCE_INIT; static void destructor(void *ptr) delete ptr; static void getkey_once( ) pthread_key_create( ,5.3多线程服务器(18),实例4: 源程序名:thrserver2.cpp和procclient.cpp 功能描述:修改4.3中实例3,采用线程专用数据(TSD)取代静态变量实现了实例3希望实现的功能。 实例5: 源程序名:thrserver3.cpp和procclient.cpp 功能描述:修改4.3中实例3,另一种常用的方法是通过使用函数的参变量取代静态变量,但需要改变函数的原型以增加相应的参变量,同样实现了实例4的功能(自己看)。,5.4 I/O多路复用服务器(1),多路复用的基本原理 当服务器同时处理两个输入流时(如stdio和socket),常常使进程进入阻塞状态。即如果在输入流A中阻塞,则无法处理输入流B中到达的数据,反之亦然。 生活中的例子:一个人需分别在两个不同车站接两个人,若先去A站接人,A站人没到,等待,直到接到A站人再去B站接另一人,此时B站人已等候多时,效率低。 解决办法:谁先到就给接站的人打电话,然后接人。 这种思想就是多路复用的原理。,5.4 I/O多路复用服务器(2),实现机制 服务器采用I/O多路复用技术,由系统内核缓冲I/O数据,当某I/O准备好后,系统通知应用程序该I/O可读或可写,应用程序马上完成相应的I/O操作,这一过程由select()系统调用实现。 优势 与多进程和多线程技术相比, I/O多路复用技术系统开销小,不必创建进程/线程,也不必维护这些进程/线程。 主要应用 客户程序需同时处理交互式输入及与服务器间的网络连接。 客户端需同时对多个网络连接作出响应。 TCP服务器需同时处理处于监听状态和多个连接状态的套接字。 服务器需处理多个网络协议的套接字。 服务器需同时处理不同的网络服务和协议。,5.4 I/O多路复用服务器(3),涉及的数据结构、带参数的宏及函数 宏名 #define FD_SETSIZE 256 (在sys/types.h中) 数据结构 fd_set是一个结构(整型数组),存放描述符集合(每个描述符占1位)。该数据类型对用户不透明,需要通过宏进行操作。 带参数的宏 FD_ZERO(fd_set *fdset) 初始化描述符集合,每位置为零 FD_SET(int fd, fd_set *fdset) 建立描述符fd与fdset的联系 FD_CLR (int fd, fd_set *fdset) 撤消描述符fd与fdset的联系 FD_ISSET (int fd, fd_set *fdset) 检查与fdset联系的描述符fd是否可读写,返回非0表示可读写(集合中相应位是否被设置) 注:通常为读写分别建立描述符集合,如: fd_set read_socks,write_socks; FD_ZERO(,5.4 I/O多路复用服务器(4),函数select() 原型:#include int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout); 说明:nfds: select()函数监视描述符值的最大值,一般设为要监视描述符值的最大值加1(描述符从0开始).最大描述符上限定义为FD_SETSIZE,而事实上目前的UNIX版本不作限制(仅受内存量和管理性限制) readfds: select()监视的可读描述符集合. writefds: select()监视的可写描述符集合. errorfds: select()监视的异常描述符集合. timeout: select()函数的超时结束时间. 返回值: 成功返回总位数,对应已准备好的描述符,否则返回-1,并置errno码。 注: readfds, writefds, errorfds中任意一个(或全部)可以为NULL,当全部为NULL时,相当于sleep(),5.4 I/O多路复用服务器(5),编写下列代码: fd_set readset,writeset; FD_ZERO(,fd0,fd3,fd1,readset,writeset,fd2,5.4 I/O多路复用服务器(6),select()函数实现I/O多路复用的步骤 清空描述符集合: FD_ZERO(,5.4 I/O多路复用服务器(7),实例6: 源程序名:selectserver.cpp和procclient.cpp 功能描述:单线程并发服务器实例,实现功能与例4、例5类似,编程比多进程、多线程并发服务器复杂,若多线程并发服务器I/O多路复用更复杂。 运行结果: 服务器: $./selectserver Client(wen) closed connection.users data:12345678 Client(zhang) closed connection.users data:abcdef 客户1: $procclient 127.0.0.1 /依次输入1234、5678和bye 客户2: $procclient 127.0.0.1 /依次输入abc、def和bye,5.4 I/O多路复用服务器(8),实例7: 源程序名:chatserver.cpp和chatclient.cpp 功能描述:采用I/O多路复用实现聊天室程序 运行程序: 服务器: $./chatserver 1234 broadmsg: zhang:hello broadmsg: wang:Hi,zhang 客户1: $./chatclient 202.113.29.19 1234 zhang zhang entered chat room hello (输入的) zhang:hello (广播的) wang:Hi,zhang (广播的) 客户2: $./chatclient 202.113.29.19 1234 wang wang entered chat room Hi,zhang(输入的) wang:Hi,zhang(广播的),5.4 I/O多路复用服务器(8),说明: 先运行服务器程序 客户1进入聊天室,并输入hello,服务器进行广播 此时,客户2、客户3依次进入聊天室 若某客户D退出聊天室,则 本客户显示:close chat connection closed 其它客户显示:* is leaving now! 若非正常关闭,其它客户显示:* is dropped!,5.5 非阻塞socket(1),进程阻塞 一个进程中同时处理多路输入输出流时,常出现进程阻塞 TCP socket编程中导致阻塞的系统调用 服务器端的accept(),若没有连接请求则进程被阻塞,有连接请求到达时恢复执行. 从socket读出数据的read()、recv()系统调用,若读取缓冲区无数据可读(对方发送数据尚未到达),则进程被阻塞. 向socket写入数据的write()、sent()系统调用,若写入缓冲区已满(TCP还没来得及送出去),则进程被阻塞. cin在接收到回车输入前,进程被阻塞. UDP socket编程中的recvfrom()和sendto()也导致阻塞,5.5 非阻塞socket(2),非阻塞socket基本原理 前面介绍的socket都是阻塞的(默认方式),即当输入输出条件没有准备好,对该socket的操作会导致进程的阻塞。 将socket设为非阻塞,在程序中通过轮询来检查socket是否准备好进行输入和输出(不用select) 设置非阻塞的系统调用 #include int fcntl(int fd,int cmd); int fcntl(int fd,int cmd,long arg);

温馨提示

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

评论

0/150

提交评论