欢迎来到淘文阁 - 分享文档赚钱的网站! | 帮助中心 好文档才是您的得力助手!
淘文阁 - 分享文档赚钱的网站
全部分类
  • 研究报告>
  • 管理文献>
  • 标准材料>
  • 技术资料>
  • 教育专区>
  • 应用文书>
  • 生活休闲>
  • 考试试题>
  • pptx模板>
  • 工商注册>
  • 期刊短文>
  • 图片设计>
  • ImageVerifierCode 换一换

    【精品】Linux网络编程 并发服务器精品ppt课件.ppt

    • 资源ID:86272881       资源大小:856KB        全文页数:78页
    • 资源格式: PPT        下载积分:15金币
    快捷下载 游客一键下载
    会员登录下载
    微信登录下载
    三方登录下载: 微信开放平台登录   QQ登录  
    二维码
    微信扫一扫登录
    下载资源需要15金币
    邮箱/手机:
    温馨提示:
    快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。
    如填写123,账号就是123,密码也是123。
    支付方式: 支付宝    微信支付   
    验证码:   换一换

     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    【精品】Linux网络编程 并发服务器精品ppt课件.ppt

    Linux网络编程并发服务器2第六单元 并发服务器6.1服务器的分类6.2多进程并发服务器6.3多线程服务器3服务器分类按连接类型分类面向连接的服务器(如TCP)面向无连接的服务器(如UDP)按处理方式分类迭代服务器并发服务器4绑定地址监听连接接收连接处理连接断开连接接收请求处理请求返回响应TCP迭代服务器绑定地址监听连接接收连接创建子进程关闭连接套接字处理连接关闭连接套接字终止子进程关闭监听套接字服务器主进程服务器子进程TCP并发服务器迭代服务器vs.并发服务器5迭代服务器vs.并发服务器迭代服务器一次响应一个客户。并发服务器同时响应多个客户。多进程多线程I/O多路复用:单进程,select函数对监听套接字描述符、各个客户端套接字描述符进行监视。用一个新进程/线程去响应一个客户端。8第六单元 并发服务器6.1服务器的分类6.2多进程并发服务器6.3多线程服务器96.2多进程并发服务器6.2.1进程基础6.2.2进程创建6.2.3进程终止6.2.4多进程并发服务器10进程:可以认为是一个程序的一次运行。程序每次运行都是一个进程。一个动态实体,是独立的任务。它拥有独立的地址空间、执行堆栈、文件描述符等。各个进程之间既相互独立又相互影响独立:未经允许,进程之间无法访问,一个进程的崩溃不会造成其他进程的崩溃。影响:进程间可以通过某些机制进行通信,如IPC。“进程”基本概念111每个进程都有一个非负整数作为唯一的进程ID,也称为PID。pstree命令可查看进程家族关系。init是第一个用户进程,PID为1除init外,每个进程都只有一个父进程(PPID),可以有一个或多个子进程。“进程”基本概念212父进程与子进程1程序执行后会产生一个进程,默认情况下只有一个进程。当程序代码中包含fork/vfork创建进程函数时,进程将创建一个新进程,称为子进程,原进程成为父进程。如果程序代码不对父进程和子进程要执行的代码相区分的话,父进程继续执行,子进程将默认执行fork/vfork以后的代码。对于子进程这是毫无意义的,所以一般都会对父子进程要执行的代码进行区分(这里注意程序代码还是同一个文件,只是用if结构进行区分)13父进程与子进程2进程之间应该是独立的。一个程序中刚创建的子进程也需要有自己的资源。Linux支持三种资源拷贝方式:共享:父子进程共享通用资源,相互影响,vfork。直接拷贝:父子进程各有一份相同的资源,费时间。写时复制(copyonwrite):虚拟内存拷贝过程费时,所以只有两个进程中的任一个试图修改某内存页时,才进行对该内存页的拷贝,否则将一直共享,fork。服务器按照处理方式可分为几类?并发服务器有几种方式?三种资源拷贝方式是什么?156.2多进程并发服务器6.2.1进程基础6.2.2进程创建6.2.3进程终止6.2.4多进程并发服务器16#include#includepid_tfork(void)返回:父进程中返回子进程PID,子进程返回0,出错返回-1。创建进程fork17fork后,子进程和父进程继续执行fork()函数后的指令,至于先后次序,要根据内核使用的进程调度算法,一般来说是新进程优先。父子进程对资源采用写时复制(copyonwrite)方式。父进程中调用fork之前打开的所有描述字在函数fork返回之后子进程会得到一个副本。fork后,父子进程均需要将自己不使用的描述字关闭。创建进程fork18#include#includepid_tvfork(void);该系统调用基本上与fork相同,在BSD3.0中开始出现,主要为解决fork进行复制时昂贵的开销。两者的基本区别在于当使用vfork()创建新进程时,父进程将被暂时阻塞,而子进程则可以借用父进程的地址空间,直到子进程退出,至此父进程才继续执行。创建进程vfork19进程练习fork调用举例pid_tid;id=fork();if(id0)perror(“fork”);exit(1);elseif(id=0)/子进程代码exit(0);else/父进程代码其他代码;说明:fork之前都是父进程执行的代码,调用fork成功后,父子进程都将继续执行调用fork以后的所有代码,直到exit或main函数return。左边例子中,执行次序:父进程:1赋值id2对if-else-else语句选择执行3其他代码子进程:1fork成功后,赋值id2对if-else-else语句选择执行,由于有exit(0),所以子进程退出,否则子进程将继续执行其他代码。期间遇到exit或main函数return都会各自退出进程20进程练习/home/linuxnet/6/fork.cpid_tid;id=fork();if(id0)perror(fork);exit(1);elseif(id=0)printf(hello%dn,getpid();exit(0);else printf(“hello%dn,getpid();printf(%dprintthissentencen,getpid();说明:创建子进程后,子进程和父进程都是独立的进程,两者执行的先后次序与系统的进程调度策略有关。216.2多进程并发服务器6.2.1进程基础6.2.2进程创建6.2.3进程终止6.2.4多进程并发服务器22进程的终止存在两个可能:父进程先于子进程终止此时子进程称为孤儿进程,系统会安排init进程领养该孤儿进程。子进程先于父进程终止子进程终止时,系统内核为子进程保留一定的状态信息(进程ID、终止状态、CPU时间等)并向其父进程发送SIGCHLD信号;缺省情况下,父进程忽略该信号,或者提供一个该信号发生时即被调用的函数。父进程可以调用wait或waitpid函数时,获取这些信息。如果父进程不及时调用wait则子进程成为僵尸进程。终止进程23终止进程(续)#includevoidexit(intstatus);本函数终止调用进程。关闭所有子进程打开的描述符,向父进程发送SIGCHLD信号,并向父进程返回终止状态。24#include#includepid_twait(int*stat_loc);参数:stat_loc存储子进程的终止状态(一个整数);返回:成功被终止子进程的PID成功;出错-1。如果没有终止的子进程,但是有一个或多个正在执行的子进程,则该函数将堵塞,直到有一个子进程终止或者wait被信号中断时,wait返回并释放子进程所有资源。获取子进程终止信息25使用wait()函数可能会出现一个问题SIGCHLD服务器父进程服务器父进程服务器子进程服务器子进程服务器子进程服务器子进程服务器子进程服务器子进程客户客户FINFINFINSIGCHLDSIGCHLD由于由于Linux信号不排队,在信号不排队,在SIGCHLD信号同时到来后,信号同时到来后,服务器信号处理程序中调用了服务器信号处理程序中调用了wait函数,其只执行一次,函数,其只执行一次,这样将留下这样将留下2个僵尸进程。可以使用个僵尸进程。可以使用waitpid函数解决这个函数解决这个问题。问题。获取子进程终止信息26pid_twaitpid(pid_tpid,int*stat_loc,intoptions);可以指定等待某个或具有某些特征的进程。pid参数意义如下:-1:要求知道任何一个子进程的返回状态(等待第一个终止的子进程);0:要求知道进程号为pid的子进程的状态;0)printf(“child%dterminatedn”,pid);想想如果仅是将wait进行循环调用结果会如何?获取子进程终止信息练习编程:创建子进程,在子进程中输出Imchild,父进程等待子进程结束后中输出Imparent。296.2多进程并发服务器6.2.1进程基础6.2.2进程创建6.2.3进程终止6.2.4多进程并发服务器30多进程并发服务器状态图服务器客户connect()函数listenfd客户/服务器状态图(调用accept函数返回前)连接请求31多进程并发服务器状态图(cont.)服务器客户connect()函数listenfd客户/服务器状态图(调用accept函数返回后)connfd连接建立32多进程并发服务器状态图(cont.)服务器(父进程)客户connect()函数listenfd客户/服务器状态图(调用fork函数后)connfd连接建立服务器(子进程)listenfdconnfdfork()函数33多进程并发服务器状态图(cont.)服务器(父进程)客户connect()函数listenfd客户/服务器状态图(父进程关闭连接套接字,子进程关闭监听套接字)连接建立服务器(子进程)connfd34服务器等待接收客户的连接请求一旦连接成功则显示客户地址,接着接收客户端的名称并显示;然后接收来自该客户的字符串,每当收到一个字符串时,显示该字符串,并将字符串按照恺撒密码的加密方式(K=3)进行加密,再将加密后的字符发回客户端;之后,继续等待接收该客户的信息,直到客户关闭连接。要求服务器具有同时处理多个客户请求的能力。客户端首先与相应的服务器建立连接;接着接收用户输入的客户端名称,并将其发送给服务器;然后继续接收用户输入的字符串,再将字符串发送给服务器,同时接收服务器发回的加密后的字符串并显示。之后,继续等待用户输入字符串,直到用户输入Ctrl+D,客户关闭连接并退出。多进程并发服务器实例35编程创建/home/linuxnet/6目录mpserver.cP58client.cP60makefile见右编译make(边写边make)运行每个程序占用一个终端服务器:./mpserver客户端:./client127.0.0.1根据提示输入信息多运行几个客户端SMP=mpserverC=clientall:$(SMP)$(C)$(SMP):$(SMP).cgcc$-o$(C):$(C).cgcc$-o$clean:rm-f$(SMP)$(C)36服务器思路main函数socket/bind/listen-监听套接字循环accept-连接套接字fork创建子进程,子进程负责处理与客户端通信process_cli父进程继续下次循环,接受下一个客户端连接process_cli函数显示客户端IP,recv接收客户端name并显示,循环接收客户端信息将其中的字母进行+3后发回给客户端关闭连接注意:fork后父子进程要分别将自己不使用的描述符关闭。37客户端思路main函数socket/connect(IP设置方法参照以前例子)调用process函数处理与服务器通信close断开连接process函数从键盘读name并发送给服务器循环调用getMessage从键盘读信息并发送给服务器,直至接收到Ctrl+D退出循环接收服务器信息并显示getMessage函数提示信息,从键盘输入信息并返回,该信息可从参数sendline或返回值获得。voidprocess(FILE*fp,intsockfd)/从键盘读name并发送给服务器charsendlineMAXDATASIZE,recvlineMAXDATASIZE;intnum;printf(Connectedtoserver.n);printf(Inputclientsname:);if(fgets(sendline,MAXDATASIZE,fp)=NULL)printf(nExit.n);return;send(sockfd,sendline,strlen(sendline),0);循环:1.调用getMessage从键盘读信息并发送给服务器,直至接收到Ctrl+D退出循环2.接收服务器信息并显示printf(nExit.n);getMessage函数:从键盘读信息char*getMessage(char*sendline,intlen,FILE*fp)printf(Inputstringtoserver:);return(fgets(sendline,MAXDATASIZE,fp);循环1.调用getMessage从键盘读信息并发送给服务器,直至接收到Ctrl+D退出循环2.接收服务器信息并显示while(getMessage(sendline,MAXDATASIZE,fp)!=NULL)send(sockfd,sendline,strlen(sendline),0);if(num=recv(sockfd,recvline,MAXDATASIZE,0)=0)printf(Serverterminated.n);return;recvlinenum=0;printf(ServerMessage:%sn,recvline);41第六单元 并发服务器6.1服务器的分类6.2多进程并发服务器6.3多线程服务器426.3多线程服务器6.3.1线程基础6.3.2线程基础函数6.3.3给新线程传递参数6.3.4多线程并发服务器6.3.5线程安全函数43虽然多进程并发服务器模式很多年来都使用得很好,但使用fork生成子进程存在一些问题。首先,fork占用大量的资源,内存映像要从父进程拷贝到子进程,所有描述符要在子进程中复制等。虽然当前采用写时拷贝(copy-on-write)技术,将真正的拷贝推迟到子进程有写操作时,但fork仍然需要占用大量资源。其次,fork子进程后,需要用进程间通信(IPC)在父子进程间传递信息。由于子进程从一开始就有父进程数据空间及所有描述符的拷贝,所以fork之前的信息容易传递,但是从子进程返回信息给父进程就需要做很多工作。多进程服务器的问题44线程是进程内的独立执行实体和调度单元,又称为“轻量级”进程(lightwightprocess),创建线程比进程快10100倍。一个进程内的所有线程共享相同的内存空间、全局变量等信息(这种机制又带来了同步问题),所以同进程内的一个线程崩溃可能会影响其他线程。同进程内的线程还共享以下信息:进程指令,大多数数据,打开的文件描述字,信号处理程序和信号处置,当前工作目录,用户ID和组ID线程私有信息线程ID、寄存器集合(包括程序计数器和栈指针)、栈(用于存放局部变量)、error、信号掩码、优先级“线程”基本概念45线程相关说明46线程和进程的关系一个进程可以创建多个线程,进程是拥有资源的单位,线程是使用资源的单位。同一进程中的线程共享进程程序段、数据段信息。线程的好处“时”“空”开销小:占用空间,切换时间方便通信:共享数据段提高程序响应:将耗时长的操作放在一个线程内,可以同时进行其他操作。多CPU系统会使线程尽量分散到不同的CPU上运行。47多进程与多线程的优缺点进程优点:编程、调试简单,可靠性较高。缺点:创建、销毁、切换速度慢,内存、资源占用大。线程优点:创建、销毁、切换速度快,内存、资源占用小。缺点:编程、调试复杂,可靠性较差。486.3多线程服务器6.3.1线程基础6.3.2线程基础函数6.3.3给新线程传递参数6.3.4多线程并发服务器6.3.5线程安全函数49线程相关函数#include创建线程:pthread_create挂起当前线程:pthread_join设置线程可分离:pthread_detach结束当前线程:pthread_exit50线程的创建pthread函数intpthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*),void*arg);参数:thread:指向线程标识符的指针,attr:设置线程属性,可设为NULL表示默认属性start_routine:线程运行函数的起始地址,注意原型arg:传给运行函数的参数,若传多个用结构体“打包”。返回值:成功返回0,失败返回errno,不置errno。51线程的创建举例n举例:(1)首先准备好要运行函数的形式和内容(2)调用pthread_create函数创建线程void*print_msg(void*arg)/线程运行函数操作mainpthread_ttid;intret;ret=pthread_create(&tid,NULL,print_msg,NULL);if(ret!=0)printf(Createpthreaderror!n);exit(1);注意线程函数的参数和返回值的类型!52等待线程终止函数pthread_join用来挂起当前线程等待指定线程结束。intpthread_join(pthread_tth,void*thread_return);参数th:被等待的线程标识符,thread_return:自定义指针,存储被等待线程的返回值。返回值:成功返回0,错误返回-1(置errno)说明这是阻塞函数,调用后将一直等到被等线程结束为止。当函数返回时,被等待线程的资源被收回。该函数类似与waitpid函数,但必须指定等待线程的ID,该函数不能等待任意一个线程结束(如wait);被等待线程必须是当前进程的成员,并且不是分离的线程和守护线程。53void*print_msg(void*arg)/线程运行函数操作mainpthread_ttid;intret;ret=pthread_create(&tid,NULL,print_msg,NULL);/其他操作pthread_join(tid,NULL);/等待线程tid结束/继续其他操作等待线程终止举例54线程分为两类可联合的(joinable)(默认)分离的(detached)当可联合的线程终止时,其线程id和退出状态将保留,直到另外一个线程调用pthread_join。分离的线程则像守护进程,当它终止时,释放所有资源,其他线程不能等待它终止。intpthread_detach(pthread_ttid)该函数将指定的线程变为脱离的。如设置当前线程pthread_detach(pthread_self();其中pthread_self()获取当前线程id设置线程分离属性55线程退出#includevoidpthread_exit(void*status);指针status:指向线程的退出状态。不能指向一个局部变量,因为线程终止时其所有的局部变量将被撤销;还有其他两种方法可使线程终止启动线程的函数(pthread_create的第3个参数)返回。其返回值便是线程的终止状态;如果进程的main函数返回,或者当前进程中任一线程调用了exit()函数,将终止该进程中所有线程。566.3多线程服务器6.3.1线程基础6.3.2线程基础函数6.3.3给新线程传递参数6.3.4多线程并发服务器6.3.5线程安全函数57由于同一个进程内的所有线程共享内存和变量,因此在传递参数时需作特殊处理,下面参考如下几种方法:1传递参数的普通方法2通过分配arg的空间来传递参数(掌握)给新线程传递参数581传递参数的普通方法由于线程创建函数第四个参数只允许传递一个参数,因此当需要传递多个数据时,应首先将这些数据封装在一个结构体中intpthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*),void*arg);591传递参数的普通方法void*start_routine(void*arg);struct ARG int connfd;int other;int main()ARG arg;While(1)if(connfd=accept(sockfd,NULL,NULL)=-1)arg.connfd=connfd;pthread_create(&tid,NULL,start_routine,(void*)&arg);void*start_routine(void*arg)ARG info;info.connfd=(ARG*)arg)-connfd;info.other=(ARG*)arg)-other;601传递参数的普通方法的问题这种方法有问题,对一个客户可以工作,但多个客户则可能出现问题。由于传递的是arg的地址,所以各个线程所使用的实际上是同一个arg变量,实际上arg中保存了服务器与每个客户端通信的套接字描述符,如果新线程正处理A客户请求时,主线程又接受B的连接从而修改了arg,这样A从arg获得的信息就是B的信息了。612通过分配arg的空间来传递主线程首先为每个新线程分配存储arg的空间,再将arg传递给新线程使用,新线程使用完后要释放该空间。intmain(void)ARG*arg;intconnfd;while(1)if(connfd=accept(sockfd,NULL,NULL)=-1)arg=(structARG*)malloc(sizeof(structARG);arg-connfd=connfd;pthread_create(&tid,NULL,start_routine,(void*)arg);626.3多线程服务器6.3.1线程基础6.3.2线程基础函数6.3.3给新线程传递参数6.3.4多线程并发服务器6.3.5线程安全函数63与6.2多进程并发服务器功能相同服务器等待接收客户的连接请求,一旦连接成功则显示客户地址,接着接收客户端的名称并显示;然后接收来自该客户的字符串,每当收到一个字符串时,显示该字符串,并将字符串按照恺撒密码的加密方式(K=3)进行加密,再将加密后的字符发回客户端;之后,继续等待接收该客户的信息,直到客户关闭连接。要求服务器具有同时处理多个客户请求的能力。客户端首先与相应的服务器建立连接;接着接收用户输入的客户端名称,并将其发送给服务器;然后继续接收用户输入的字符串,再将字符串发送给服务器,同时接收服务器发回的加密后的字符串并显示。之后,继续等待用户输入字符串,直到用户输入Ctrl+D,客户关闭连接并退出。多线程并发服务器实例64编程创建/home/linuxnet/6目录mtserver.cP68client.c同P60makefile见右编译make(边写边make)运行每个程序占用一个终端服务器:./mtserver客户端:./client127.0.0.1根据提示输入信息多运行几个客户端#在makefile中添加内容SMT=mtserverall:$(SMP)$(C)$(SMT)$(SMT):$(SMT).cgcc$-o$clean:rm行最后加$(SMT)65服务器思路main函数socket/bind/listen-监听套接字循环accept-连接套接字创建线程,将连接套接字描述符和客户端地址传入线程主线程继续下次循环,接受下一个客户端连接process_cli函数与多进程并发服务器相同function函数线程执行函数,调用process_cli函数,将连接套接字描述符和客户端地址作为参数。释放main函数中malloc的空间线程退出666.3多线程服务器6.3.1线程基础6.3.2线程基础函数6.3.3给新线程传递参数6.3.4多线程并发服务器6.3.5线程安全函数67线程安全问题是一个非常复杂的问题。简单地说,就是多个线程在操作共享数据特别是静态变量时出现的混乱情况,这种情况可能导致不可预测的后果。示例:P75多线程并发服务器加入了保存用户聊天数据的函数savedata(line112)。该函数使用了静态变量index导致了混乱的结果。线程安全问题67voidprocess_cli(intconnfd,structsockaddr_inclient)69intnum;70charcli_data1000;/.接收到用户数据后86savedata(recvbuf,num,cli_data);112voidsavedata(char*recvbuf,intlen,char*cli_data)114staticintindex=0;inti=0;while(ilen-1)cli_dataindex+=recvbufi;i+;cli_dataindex=0;12268错误原因示意图690123456789101112131415161718192021helloabcdbyeclient1的私有数据charcli_data10000123456789101112131415161718192021123abcdefgclient2的私有数据charcli_data1000公有变量staticindex,两个线程共同增长70解决线程安全问题的方法主要有:使用线程专用数据(TSD)。使用函数参变量实现线程安全性线程安全解决方案71从上例可以看出,在多线程环境中,应避免使用静态变量。在Linux环境中,用线程专用数据TSD(Thread-SpecificData)取代静态变量。它类似于全局数据,只不过它是线程私有的,是以线程为界限的。TSD是定义线程私有全局数据的唯一方法。每个TSD由进程内唯一的关键字(key)来标志,用这个关键字,线程可以存取线程私有的数据。线程专用数据:TSDTSD使用过程适用情况各线程使用一个公共数据,但是对该数据都有自己的值,这种数据称为线程专有数据。使用方法1在第一个线程执行时内产生TSD关键字,保证所有线程范围内只执行一次2如果线程专有数据与TSD关键字没有绑定,则第一次要进行绑定3使用线程专有数据时先根据TSD关键字进行获取,再进行操作。7273TSD:第1步pthread_once(&once,creatkey_once);产生TSD关键字,只在第一个新线程中执行。L128once:只执行一次L20creatkey_once函数:产生TSD关键字L25-28调用phtread_key_create函数L27key:在进程内部分配一个标志TSD的关键字L19destructor:和关键字关联的析构函数,每个线程退出时用L21-2474TSD:第2、3步data=(structST_DATA*)pthread_getspecific(key)要使用线程专有数据前先获取与线程绑定TSD关键字有关的数据L129如果返回值为NULL表明没有与TSD关键字绑定数据,则绑定线程专有数据,并赋初值。L131-133pthread_setspecific(key,data);将线程专有数据data与TSD关键字key绑定75另外一种常用的方法是:通过使用函数的参变量来取代静态变量。P82示例L74,76定义data结构体并赋初值。每个线程一个data。L91调用savedata_r函数,将data地址作为参数传递L117savedata_r函数中的L122对data.index进行增加。函数参变量实现安全性76尽管TSD实现略微有些复杂,但却是将一个非线程安全函数转化为线程安全常用的方法。第二种方法虽然简单,但需要改变函数的原型,同时,线程中函数的调用者还需要为这些变量分配相应的空间并初始化,如每个线程都会定义个data变量。两种方法比较77第六单元小结服务器分类,区别多进程并发服务器进程相关函数多进程并发服务器实现方法多线程并发服务器线程相关函数多线程并发服务器实现方法理解线程安全的概念和解决方案谢谢

    注意事项

    本文(【精品】Linux网络编程 并发服务器精品ppt课件.ppt)为本站会员(1595****071)主动上传,淘文阁 - 分享文档赚钱的网站仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知淘文阁 - 分享文档赚钱的网站(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    关于淘文阁 - 版权申诉 - 用户使用规则 - 积分规则 - 联系我们

    本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

    工信部备案号:黑ICP备15003705号 © 2020-2023 www.taowenge.com 淘文阁 

    收起
    展开