多线程网络编程课件_第1页
多线程网络编程课件_第2页
多线程网络编程课件_第3页
多线程网络编程课件_第4页
多线程网络编程课件_第5页
已阅读5页,还剩46页未读 继续免费阅读

下载本文档

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

文档简介

多线程网络编程R&DIP夏建嵬2007/0810/13/20221AsiaInfoTechnologies(China),Ltd.课程目标理解应用系统日志机制,熟练使用日志接口掌握socket编程基础,能理解socket封装类掌握posix多线程编程基础能轻松快速构建稳定的、可扩展的多线程服务器10/13/20222AsiaInfoTechnologies(China),Ltd.课程内容应用系统的日志机制介绍深入Posix编程深入Socket编程构建多线程网络服务器10/13/20223AsiaInfoTechnologies(China),Ltd.Socket编程TCP/IP协议概述TCP编程UDP编程10/13/20224AsiaInfoTechnologies(China),Ltd.TCP/IP协议概述-协议分层10/13/20225AsiaInfoTechnologies(China),Ltd.TCP/IP协议概述-主机间的连接10/13/20226AsiaInfoTechnologies(China),Ltd.TCP/IP协议概述-基本概念字节顺序:机器结构或网络硬件的差异小端(Little-Endian):最低存储地址包含了整数的最低位置。大端(Big-Endian):最低存储地址包含了整数的最高位置。网络标准字节顺序:首先发送整数中的最高有效字节(大端方式)问题:字符数组需要进行转换吗?IP地址的分类10/13/20227AsiaInfoTechnologies(China),Ltd.网络掩码

为了更充分地利用宝贵的IP地址资源,子网的概念被引入。为此,IP地址的主机部分又被分为子网部分和主机部分。IP地址=网络地址+子网地址+主机地址网络掩码(32位):某位为1,代表IP地址中的对应位为网络地址;某位为0,代表IP地址中的对应位为主机地址。路由器在处理子网的IP数据包时,将数据包的目标地址和各个子网的网络掩码进行“与”操作,然后将结果与子网的地址进行比较。TCP/IP协议概述-基本概念10/13/20228AsiaInfoTechnologies(China),Ltd.TCP/IP协议概述-基本概念不同体系结构的编程模型ILP32模型:32位系统上使用,意即Integer-Long-Pointer都是32位。LP64模型:64位系统上使用,意即Long-Pointer都是64位。端口号

TCP/UDP协议使用16位的端口号来区分不同的进程。RFC1700包含了由IANA定义的端口列表:知名(well-known)端口:0~1023,由IANA分配和控制。已注册(register)端口:1024~49151,在IANA注册但不受其控制。动态(dynamic)端口:49152~65535,临时端口,可随意使用。10/13/20229AsiaInfoTechnologies(China),Ltd.TCP/IP协议概述-IP协议IP协议概述

IP(InternetProtocol)协议是网络层协议,主要功能及特点:不可靠的数据报传输服务。路由选择功能。无连接服务。最大努力传送(best-effortdelivery)特性。IP报文的格式10/13/202210AsiaInfoTechnologies(China),Ltd.TCP/IP协议概述-套接字地址结构通用套接字地址结构struct

sockaddr{ unsignedshortsa_family; //地址类型,AF_INET charsa_data[14]; //协议地址};TCP/IP协议相关的地址结构//IP地址结构struct

in_addr{ __u32s_addr; //IP地址,网络字节顺序};//TCP/IP协议的套接字地址struct

sockaddr_in{ shortinsa_family; //地址类型,为AF_INET unsignedshortint

sin_port; //端口号,网络字节顺序

struct

in_addr

sin_addr; //IP地址

//填充字节,最好初始化为0};10/13/202211AsiaInfoTechnologies(China),Ltd.TCP/IP协议概述-基础socket函数介绍字节转换相关函数//h:hostn:networkl:longs:shortuint32_thtonl(uint32_thostlong);uint16_thtons(uint16_thostshort);uint32_tntohl(uint32_tnetlong);uint16_tntohs(uint16_tnetshort);地址转换相关函数//a:asciin:networkint

inet_aton(constchar*cp,struct

in_addr*inp);char*inet_ntoa(struct

in_addrin);in_addr_t

inet_addr(constchar*cp);in_addr_t

inet_network(constchar*cp);10/13/202212AsiaInfoTechnologies(China),Ltd.gethostbyname函数//首先查本地hosts文件,然后查DNSstruct

hostent*gethostbyname(constchar*name);int

gethostbyname_r(constchar*name,struct

hostent*ret,char*buf,size_t

buflen,

struct

hostent**result,int*h_errnop);//hostent结构struct

hostent{ char*h_name; //正式的主机名

char**h_aliases; //别名列表

int

h_addrtype; //地址类型

int

h_length; //地址长度

char**h_addr_list; //IP地址列表

}#defineh_addrh_addr_list[0]TCP/IP协议概述-基础socket函数介绍//gethostbyname_r使用实例struct

hostent*result;struct

hostent

hentry;charhdata[4096];int

hlen=sizeof(hdata);int

herr=0;memset(hdata,0,sizeof(hdata));intret;ret=gethostbyname_r(m_cnAddr.c_str(),&hentry,hdata,hlen,&result,&herr);if(0!=ret){ //错误处理并返回}memcpy(&ipaddr,hentry.h_addr_list[0],hentry.h_length);10/13/202213AsiaInfoTechnologies(China),Ltd.gethostbyaddr函数struct

hostent*gethostbyaddr(constchar*addr,int

len,intfamily);struct

hostent*gethostbyaddr_r(constchar*addr,intlength,inttype,struct

hostent*result,char*buffer,int

buflen,int*h_errnop);注:1、addr参数是指向in_addr类型的指针。

2、Linux没有提供gethostbyaddr_r(),上面的API是Solaris提供的。获得或设置套接字选项int

getsockopt(ints,intlevel,int

optname,void*optval,socklen_t*optlen);int

setsockopt(ints,intlevel,int

optname,constvoid*optval,socklen_t

optlen);

选项的层次SOL_SOCKET :通用套接字选项。IPPROTO_IP :IP选项。IPPROTO_TCP :TCP选项。TCP/IP协议概述-基础socket函数介绍10/13/202214AsiaInfoTechnologies(China),Ltd.常用选项说明SO_ERROR选项

获得并清除套接字错误。例如:

int

sock_err=0;

int

sock_err_len=sizeof(sock_err);//注意:别忘了

int

sockopt_ret=getsockopt(socketFD(),SOL_SOCKET,SO_ERROR, (void*)&sock_err,(socklen_t*)&sock_err_len);SO_LINGER选项延迟关闭选项,用于指定close()函数对面向连接的协议如何操作。默认情下,close()函数调用后会立即返回,让TCP协议处理发送缓冲区中的剩余数据和连接关闭操作。LINGER选项结构为:structlinger{

int

l_onoff; //允许/禁止延迟操作,默认为0(禁止)

int

l_linger; //延迟时间(秒)};TCP/IP协议概述-基础socket函数介绍10/13/202215AsiaInfoTechnologies(China),Ltd.LINGER结构说明:l_onoff为0禁止close()延迟操作,l_linger成员被忽略。close()按默认方式工作。l_onoff非0,l_linger为0调用close()后夭折该连接,即丢弃发送缓冲区中的未发送数据,并向对端发送RST报文之后关闭连接,这样可以避免TIME_WAIT状态。但会造成数据丢失/数据混乱/连接非正常关闭。l_onoff非0,l_linger非0调用close()后,其返回将会延迟l_linger指定的秒数。SO_RCVBUF和SO_SNDBUF选项SO_RCVBUF选项用于设置套接字接收缓冲区大小,而SO_SNDBUF选项用于设置套接字发送缓冲区大小。加大缓冲区大小可以改善网络处理性能,但缓冲区大小都有个上限限制。注意:对于TCP连接而言,修改缓冲区大小必须在连接建立之前,即connect()或listen()前对于UDP连接而言,没有实际的发送缓冲区,该选项表示能够发送的、最大UDP报文的大小。TCP/IP协议概述-基础socket函数介绍10/13/202216AsiaInfoTechnologies(China),Ltd.SO_REUSEADDR选项该选项用于解决重复绑定问题,后面详细介绍。需要注意的是:该选项必须在绑定操作之前设置。例如://settheSO_REUSEADDRoption,sockfd为打开的socket描述符int

optval=1;if(0!=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))){//错误处理}……if(0!=bind(sockfd,…){}多路监听函数select()函数select()可用于同时检测多个描述符是否就绪。当某个描述符就绪时,函数select()成功返回,否则,它将阻塞直到超时(如果设置)。TCP/IP协议概述-基础socket函数介绍10/13/202217AsiaInfoTechnologies(China),L

select(intn,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,struct

timeval*timeout);该函数能同时检测三个描述符集合:读集合(readfds),写集合(writefds)和异常集合(exceptfds)。读就绪的条件接收缓冲区的数据量不小于套接字的接收下限。连接的读通道被关闭,即收到FIN报文。监听套接字的完成队列不为空,即表示有已经建立的连接。调用函数connect()以非阻塞方式进行连接的过程中出现错误。写就绪的条件发送缓冲区的可用空间不小于套接字的发送下限。连接的写通道被关闭。注:向已经关闭写通道的连接上写数据会产生SIGPIPE信号。非阻塞式套接字的TCP连接建立成功。调用函数connect()以非阻塞方式进行连接过程中出现错误。异常就绪的条件套接字上有带外数据未接收。套接字仍在带外标记的范围内,即带外数据还在正常的字节流中未被读取。TCP/IP协议概述-基础socket函数介绍10/13/202218AsiaInfoTechnologies(China),Ltd.描述符集合相关的宏:FD_CLR(int

fd,fd_set*set); //将某个描述符从指定的描述符集中删除FD_ISSET(int

fd,fd_set*set); //检查某个描述符是否被设置(就绪)FD_SET(int

fd,fd_set*set); //将某个描述符加入到指定的描述符集中FD_ZERO(fd_set*set); //清空整个描述符集

使用函数select():

fd_set

wset;

FD_ZERO(&wset);

FD_SET(the_sock_fd,&wset); while(没有读完){ //重新计算超时时间rel

intret=select(the_sock_fd+1,NULL,&wset,NULL,&rel); if(0!=ret){ //错误处理

} //读剩余字节数

}TCP/IP协议概述-基础socket函数介绍10/13/202219AsiaInfoTechnologies(China),Ltd.Socket编程TCP/IP协议概述TCP编程UDP编程10/13/202220AsiaInfoTechnologies(China),Ltd.TCP编程-协议概述TCP协议概述

TCP(TransmissionControlProtocol)协议,是基于IP协议的传输控制协议,提供可靠的、面向连接的服务。主要特征:可靠性:通过确认和超时重传来保证的。面向无结构的字节流:数据之间没有界限。面向连接:通信之前,必须建立连接;通信结束,必须关闭连接。全双工:通信双方都可以同时发送和接收数据。提供流量控制机制:通过滑动窗口实现。TCP报文的格式10/13/202221AsiaInfoTechnologies(China),Ltd.TCP编程-建立连接建议TCP连接

TCP协议采用三次握手方式来建立一条可靠的连接。在这个过程中,连接双方完成各自初始序列号的交换。10/13/202222AsiaInfoTechnologies(China),Ltd.TCP编程-关闭连接关闭TCP连接由于TCP连接是全双工连接,因此,需要同时关闭读通道和写通道。10/13/202223AsiaInfoTechnologies(China),Ltd.TCP编程-基本函数介绍函数socket()int

socket(intdomain,inttype,intprotocol);【参数说明】domain [in]协议簇,TCP/IP为AF_INET。type [in]套接字类型。TCP:SOCK_STREM;UDP:SOCK_DGRAM。protocol [in]使用的协议,默认为0。对于TCP/UDP而言,设为0即可。【功能】该函数在内核中创建一个套接字结构,并返回一个标识该结构的描述符。连接相关的所有信息都保存在该套接字结构中。函数bind()int

bind(int

sockfd,struct

sockaddr*my_addr,socklen_t

addrlen);【功能】该函数将套接字绑定在到本地地址和端口。10/13/202224AsiaInfoTechnologies(China),Ltd.TCP编程-基本函数介绍【说明】对于服务端程序而言,bind()函数必须被调用,以绑定自己的公认端口号,而IP地址可以设定为通用地址INADDR_ANY或某个本地IP地址。对于客户端程序而言,一般不需要指定套接字地址(本地IP地址和端口),因为系统会自动为套接字选择一个未用的端口和本地IP地址,因此,没必要调用bind()函数:IP地址可以指定设为某个本地IP地址或INADDR_ANY。端口可以设为非零值或0,其中,0表示由系统指定端口号。建议:一般情况下,客户端程序请尽量不要使用固定端口。(为什么?)函数listen()intlisten(intsockfd,intbacklog);【功能】将一个套接字转换为监听套接字(listeningsocket),TCP状态也由CLOSED转换为LISTEN状态。其中,backlog设置请求队列的最大长度。10/13/202225AsiaInfoTechnologies(China),Ltd.TCP编程-基本函数介绍【说明】每个监听套接字都会维护两个队列:未完成连接队列和已完成连接队列。未完成连接包含那些“没有完成三次握手”的连接,而已完成连接队列则包含那些“已完成三次握手,但未被accept”的连接。函数accept()int

accept(ints,struct

sockaddr*addr,socklen_t*addrlen);【功能】从监听套接字的完成连接队列中接收一个连接。如果没有已经建立的连接,该函数会被阻塞。10/13/202226AsiaInfoTechnologies(China),Ltd.TCP编程-基本函数介绍函数connect()int

connect(int

sockfd,conststruct

sockaddr*serv_addr,socklen_t

addrlen);【功能】客户端调用该函数发起三次握手过程,和服务端建立连接。【例子】struct

sockaddr_in

addrin;memset(&addrin,0,sizeof(addrin);addrin.sin_family=AF_INET; addrin.sin_port=8080; //服务端监听端口addrin.sin_addr.s_addr=inet_addr(“5”); //服务端地址if(0!=connect(sockfd,(struct

sockaddr*)&addrin,sizeof(addrin))){//连接失败,错误处理}10/13/202227AsiaInfoTechnologies(China),Ltd.TCP编程-基本函数介绍函数read()/write()ssize_t

read(int

sockfd,void*buf,size_tcount);ssize_t

write(int

fd,constvoid*buf,size_tcount);【功能】函数read()将套接字接收缓冲区(内核)中的数据拷贝到用户缓冲区,真正的网络数据的接收操作是由内核完成的。函数write()负责将发送数据拷贝到套接字发送缓冲区(并不表示数据已经发送到对方了)。【说明】关于函数read()的返回值:=0 :收到FIN,对端关闭了连接。>0 :有数据到达,返回值为收到的字节数。<0 :如果errno是EINTR,表明是被信号中断的,不影响后续的读操作;如果是其它错误,需要进行处理。关于函数write()的返回值:<0 :如果errno是EINTR,表明是被信号中断的,不影响后续的写操作;其它错误,需要进行处理。>0 :拷贝的字节数,说明发送缓冲区还有空间。=0:很奇怪,一般当作错误处理。(???)高效的读/写操作方法:多路监听select()+阻塞式的读/写。10/13/202228AsiaInfoTechnologies(China),Ltd.TCP编程-基本函数介绍函数close()

int

close(int

fd);【功能】关闭套接字描述符。选项SO_LINGER可以控制close()的关闭操作。函数shutdown()int

shutdown(intsocket,inthow);【功能】关闭TCP连接。其中how指定了关闭方式:=0 :关闭读通道,之后的所有读操作都将返回0。在调用shutdown()时,如果套接字接收缓冲区还有数据,它们将会被丢弃;读通道被关闭之后,TCP协议会对对端发送的所有数据返回确认,但数据会被丢弃。=1 :关闭写通道,之后所有的写操作都将产生SIGPIPE信号(注意处理,如果忽略该信号,函数会返回EPIPE)。在关闭写通道时,套接字发送缓冲区中所有没有发送的数据将会被发送,然后发送FIN。=2 :关闭读通道和写通道。【说明】shutdown()和close()区别:前者针对TCP连接,而后者是针对的套接字描述符。10/13/202229AsiaInfoTechnologies(China),Ltd.TCP编程-简单事务处理利用shutdown()函数可以实现简单的事务处理(transaction)。这里所说的事务指的是一次简单的报文交换过程:请求发给服务器,然后服务器返回应答给客户端。10/13/202230AsiaInfoTechnologies(China),Ltd.TCP编程-地址重复绑定的问题案例:快速重启服务器失败

描述:监听服务器已经关闭,但该服务器接收的连接还未完全关闭(通信当中或TIME_WAIT状态),这时重启服务器会报如下错误:Addressalreadyinuse(EADDRINUSE)原因:监听套接字和accepted的套接字共享同样的本地地址和端口,因此,重启后试图绑定活动连接的端口是不允许的。解决方案:在绑定操作之前设置SO_REUSEADDR选项。10/13/202231AsiaInfoTechnologies(China),Ltd.TCP编程-客户端和服务端总结10/13/202232AsiaInfoTechnologies(China),Ltd.TCP编程-非阻塞式的主动连接初始化套接字

。更改套接字为非阻塞方式。fcntl(sockfd,F_GETFL,0); //获得描述符标志fcntl(sockfd,F_SETFL,flags); //设置描述符标志调用connect()连接TCP服务器。正常情况下,函数connect()会立即返回,不会等待三次握手完成。这时,connect()会返回-1,且errno为EINPROGRESS或EWOULDBLOCK。调用select()函数监听该套接字的读操作或写操作是否就绪。如果select()出错或超时,表明连接失败。通过SO_ERROR选项,获得套接字错误。如果套接字发生错误,表明连接不成功。恢复套接字的原始标志。10/13/202233AsiaInfoTechnologies(China),Ltd.TCP编程-读写操作对于TCP套接字,一般采取如下方式:多路监听select()+阻塞式读/写。这样,我们既可以充分利用阻塞式高效的特点,又可以通过select()来严格控制读/写操作的超时。函数CSocket::selectSocket()intCSocket::selectSocket(my_uint32_t&result,my_uint32_ttimeout);【参数说明】result [in/out]想要监听的类型/就绪的类型。类型可以是:可读/可写/异常就绪timeout [in]监听超时时间。【功能】对CSocket对应的套接字进行监听(可读/可写/异常就绪),监听的结果也保存在参数result中。【返回值】1 :监听成功,可读、可写或异常就绪。0 :监听超时。-1 :监听出错。10/13/202234AsiaInfoTechnologies(China),Ltd.TCP编程-读写操作函数CTCPPeer::readSocket()CTCPPeer::readSocket(char*buf,int

len,int&rbytes);【参数说明】buf [in]数据缓冲区指针。len [in]缓冲区长度。rbytes [in/out]要读的字节数/实际读取的字节数。【功能】在指定的时间内读取指定长度的数据到缓冲区,超时时间在初始化CTCPPeer对象时设置。【返回值】1 :读数据成功。0 :对端关闭了连接,rbytes保存已读的字节数。<0 :读数据失败,如果超时,返回ERR_SELTIMEOUT(-1986)。【讨论】1、如果返回ERR_SELTIMEOUT,且返回的rbytes不为0,该如何处理?如果rbytes为0,又该如何处理?10/13/202235AsiaInfoTechnologies(China),Ltd.TCP编程-读写操作函数CTCPPeer::writeSocket()CTCPPeer::readSocket(char*buf,int

len,int&wbytes);【参数说明】buf [in]数据缓冲区指针。len [in]缓冲区长度。wbytes [in/out]要发送的字节数/实际发送的字节数。【功能】在指定的时间内发送指定长度的数据,超时时间在初始化CTCPPeer对象时设置。【返回值】1 :发送数据成功。0 :对端关闭了连接,wbytes保存已发送的字节数。<0 :发送数据失败,如果超时,返回ERR_SELTIMEOUT(-1986)。【讨论】1、如果返回ERR_SELTIMEOUT,且返回的wbytes不为0,该如何处理?如果wbytes为0,又该如何处理?10/13/202236AsiaInfoTechnologies(China),Ltd.TCP编程-例子查错//头文件略去int

myServer(){

struct

sockaddr_in

addr;

int

addr_len; charbuf[102400];

int

sockfd;

int

fd=socket(PF_INET,SOCK_DGRAM,0);

if(fd<0)return-1;

addr.sin_family=AF_INET;

addr.sin_port=1000;

addr.sin_addr.s_addr=INADDR_ANY;

if(bind(fd,(struct

sockaddr*)&addr,sizeof(addr)==-1)return-1;

if(sockfd=accept(fd,(struct

sockaddr*)&addr,&addrlen)==-1)return-1;

intn;

while((n=read(fd,buf,sizeof(buf))>0)write(sockfd,buf,n);

close(fd); return0;}10/13/202237AsiaInfoTechnologies(China),Ltd.TCP编程-角色划分在TCP编程中,涉及到三种角色:被动连接角色、主动连接角色和通信角色。被动连接角色指的是监听服务端(TCPServer),主动连接角色指的是客户端(TCPClient),而通信角色则是由前两者生成的、用于数据传输的连接,我们常称之为对等实体(TCPPeer)。10/13/202238AsiaInfoTechnologies(China),Ltd.TCP编程-TCP类的组织10/13/202239AsiaInfoTechnologies(China),Ltd.TCP编程-TCP常连接池在实时性要求较高的应用中,为了节省建立TCP花费的时间,经常采用TCP常连接池。常连接池中的所有连接都是在启动之初建立的,需要的时候从池中取一条连接,用完后放回池中。初始化清除例程无效连接检查和修复例程取连接放回连接10/13/202240AsiaInfoTechnologies(China),Ltd.TCP编程-问题解答问题解答10/13/202241AsiaInfoTechnologies(China),Ltd.Socket编程TCP/IP协议概述TCP编程UDP编程10/13/202242AsiaInfoTechnologies(China),Ltd.UDP编程-协议概述UDP协议特征不可靠性:数据报文可能丢失或损坏。非面向连接:客户端和服务端没有固定的通道。乱序性:先发送的报文不一定会先被收到。无流控机制。面向记录的消息:消息要么全部被收到,要么什么也收不到。响应快:IP协议层具有尽最大努力传送的特点,同时,没有连接和可靠性开销。UDP应用需要自己处理报文的丢失、乱序等问题。UDP报文的格式10/13/202243AsiaInfoTechnologies(China),Ltd.UDP编程-协议概述UDP协议应用场合网络数据多为消息,通信方式为发送-接收一次模型。拥有大量的客户端。无数据安全性要求。网络负担重,但响应速度要求较高,如视频点播。10/13/202244AsiaInfoTechnologies(China),Ltd.UDP编程-重要函数介绍函数recvfrom()/sendto()ssize_t

recvfrom(ints,void*buf,size_t

len,intflags,struct

sockaddr*from,socklen_t*fromlen);ssize_t

sendto(ints,constvoid*buf,size_t

len,intflags,conststruct

sockaddr*to,socklen_t

tolen);【参数说明】s [in]套接字描述符。buf [in]发送或接收缓冲区。len [in]发送或接收消息的长度。flags [in]发送或接收标志,对UDP意义不大,可设为0。from [in]发送方地址。fromlen [in]发送方地址长度。to [in]接收方地址。tolen [in]接收方地址长度。【功能】recvfrom()函数接收UDP报文,从系统缓冲区拷贝到用户缓冲区中。而sendto则将UDP报文从用户缓冲区拷贝到系统缓冲区。10/13/202245AsiaInfoTechnologies(China),Ltd.UDP编程-重要函数介绍【说明】关于返回值recvfrom()/sendto()的返回值有以下两种情况,注意与TCP的区别:>=0 :接收/发送的字节数,0表示是空报文,是UDP协议允许的。<0 :出错,需要进行处理。关于SO_SNDBUF选项SO_SNDBUF选项可以设置UDP发送缓冲区的大小,但对UDP而言,它指的是接被发送UDP报文的最大长度。如果发送的UDP报文大于这个值,sendto()会返回EMSGSIZE。由于UDP没有实际的发送缓冲区,因此,可以认为UDP报文的sendto()函数是几乎不会阻塞的。

温馨提示

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

评论

0/150

提交评论