Linux聊天室程序设计_第1页
Linux聊天室程序设计_第2页
Linux聊天室程序设计_第3页
Linux聊天室程序设计_第4页
Linux聊天室程序设计_第5页
已阅读5页,还剩18页未读 继续免费阅读

下载本文档

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

文档简介

Linux 聊 天室程序设计 学 院: 姓 名: 班 级: 学 号: 指导老师:目录实验基本介绍-1第1章 实验分析及关键技术-11.1 实验分析-11.2 TCP通信-21.3 多线程-21.4 数据类型及用法-31.5网络套接字-41.6地址及顺序处理-41.7连接函数的说明-5第2章 Linux网络聊天室socket 编程实现过程-72.1 聊天室Socket编程连接过程-82.2 客户端主界面及聊天功能展示-8第3章 总结-10程序代码 -11参考文献-21基于Linux网络聊天室的设计与实现实验基本介绍本系统实现了在Linux网络聊天室的基本功能。在Linux下编写并调试服务器端程序和客户端程序,实现了客户、服务器之间的连接接和通信。可以在单机上开辟两个窗口分别运行客户、服务器端的程序,或者两台主机链接分别作为客户和服务器的方式。总体来说,此设计以嵌入式C语言为开发语言,使用网络套接字socket建立连接,并运用多线程实现数据的交换,程序经gcc调试成功,可以在单机网络聊天中使用。第一章 实现分析及关键技术21.1.实现分析1.1.1功能分析本系统是要在Linux系统下实现一个聊天室系统,主要有服务器和客户端。它具备的基本功能包括群聊、私聊等。1、服务器端:负责处理用户发来的各种信息,管理用户的动作(用户注册、群发信息、和特定用户私聊等)2、客户端:主要是可以发送信息给特定用户或群等功能。1.1.2设计分析本聊天系统的为c/s形式,服务器主要就是处理客户的输入信息。首先要存储客户的个人资料;当然,服务器还有自己的动态数据处理。客户状态分为链接客户、非链接客户,我们采用结构体存储链接客户的信息,以链表式来记录链接客户。用户输入自己的名字,同时并发服务器要一直在为连接用户创建线程,系统设计的总框图13,看图3.1 系统设计总框图客户端 socket 服务器图3.1 系统设计总框图1.1.3技术分析基于以上的功能分析和数据分析,首先通信Socket套接字是实现c/s通信的接口,且使用tcp协议。传输数据时,客户端需要创建两个线程分别监听键盘(即用户输入信息)和socket接口(即传入信息)。服务器则要给每个链接的客户创建一个线程来进行信息的传输,即实现并发服务器,正如图3.1所示。并发服务器是指在一个时刻可以处理多个客户请求的服务器。这只是概念上和表面的并发,并不一定是真正同时处理多个请求,如果服务器具有多个处理器,则是实际的并发,但对于大多数服务器来说,是利用操作系统的某种机能,比如异步i/o、多进程、多线程等技术来实现的,只是从客户的角度看上去看仿佛服务器并发地与多个客户通讯。linux 提供了两种形式的并发,进程和线程。因此常见的并发服务器即是基于进程的并发和基于线程的并发两种类型。线程并发服务器是使用就涉及到线程同步问题,mutex(互斥量)则能解决这个问题。当然最主要的是socket函数调用。1.2 TCP通信Socket 是建立在传输层协议(主要是TCP 和UDP)上的一种套接字规范, Socket接口是TCP/ IP 网络最为通用的API , 也是在Internet上进行应用开发最通用API14 ,socket 屏蔽了底层通信软件和具体操作系统的差异,使得任何两台安装了TCP 协议软件和实现了Socket 规范的计算机之间的通信成为可能。TCP是传输控制协议的简称,它是提供一条全双工的、可靠的信道。TCP提供面向连接的服务,在传送数据之前必须先建立连接,数据传送结束后要释放连接。TCP不提供广播和多播服务。由于TCP要提供可靠的、面向连接的运输服务,所以不可避免地增加了许多系统开销,比如确认、流量控制、计时器以及连接管理等都需要占用许多系统的时空资源。 两个计算机之间如果使用TCP通信,其连接过程需要三次握手实现15,TCP对话通过三次握手来初始化的。三次握手的目的是使数据段的发送和接收同步,并建立虚连接。下面描述了这三次握手的简单过程。1、初始化主机通过一个同步标志置位的数据段发出会话请求。2、接收主机通过发回具有以下项目的数据段表示回复:同步标志置位、即将发送的数据段的起始字节的顺序号、应答并带有将收到的下一个数据段的字节顺序号。3、请求主机再回送一个数据段,并带有确认顺序号和确认号。如图3.2所示图3.2 tcp三次握手1.3多线程(pthread)1.3.1线程pthread在网络通信中,端口地址是以进程为单位进程分配的,而一个进程与外界的消息发送与接收必须通过分配给它的同一个端口进行。因此,不能通过创建进程方式来解决上诉问题,因为两个进程会分别对应两个不同的端口,而发送和接收必须使用同一端口。线程不是资源分配的单位,所以如果使用两个线程不会对线程分配新的端口。因此,本实验需要使用两个线程去分别完成发送和接收信息的任务,这两个线程共享其进程拥有的统一个端口地址。由于创建进程的进程本身会作为一个线程来调度,所以只需要再创建一个线程专门负责接收信息就可以了.因此,对于从每个客户端发来的请求,服务器端都要创建相应的线程去接收并处理;同理,对于客户端而言,也要创建一个线程去读取服务器端发来的信息。1.4数据类型及用法1、各个函数类型pthread_t:线程句柄 pthread_create():创建一个线程 pthread_exit():终止当前线程 pthread_join():阻塞当前的线程,直到另外一个线程运行结束pthread_attr_detach():获取脱离状态的属性2、线程创建与结束(1)pthread_t线程的标识符类型,pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义: typedef unsigned long int pthread_t; (2)pthread_create thread_create用来创建一个线程,它的原型为: extern int pthread_create _P (pthread_t *_thread, _const pthread_attr_t *_attr,void *(*_start_routine) (void *), void *_arg); 第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我们的 函数thread不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。对线程属性的设定和修改我们将在下一 节阐述。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线程, 例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一 行代码。 (3)pthread_join 函数pthread_join用来等待一个线程的结束。函数原型为: extern int pthread_join _P (pthread_t _th, void *_thread_return); 第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。 (4)pthread_exit 线程除了正常执行结束外,还可以通过函数pthread_exit来结束它,pthread_exit的函数原型为: extern void pthread_exit _P (void *_retval) _attribute_ (_noreturn_); 唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给 thread_return。最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线 程则返回错误代码ESRCH。void thread(node *p)pthread_create(&tidi,NULL,(void*)thread,ptr);1.5网络套接字(socket)1.5.1概述1、连接步骤Socket接口上TCP/IP网络应用程序接口(API),它提供了许多函数,本系统就使用它们来开发TCP/IP网络聊天室程序17。使用Socket接口进行网络通信的过程,简要步骤如下:(1)建立一个Socket:(2)按要求配置socket,将socket连接到远程主机或给socket指定以各本地协议端口。(3)按要求通过socket发送和接受数据。(4)关闭此socket。这是通过Socket实现点对点通信需要掌握的4个编程要点。2、socket定义18在 Linux 中的网络编程是通过socket接口来进行的。人们常说的socket接口是一种特殊的I/O,它也是一种文件描述符。每一个socket都用一个半相关描述协议,本地地址、本地端口来表示;一个完整的套接字则用一个相关描述协议,本地地址、本地端口、远程地址、远程端口。socket也有一个类似于打开文件的函数调用,该函数返回一个整型的socket描述符,随后的连接建立、数据传输等操作都是通过socket来实现的。1.6地址及顺序处理1.6.1 地址结构相关处理(1)数据结构介绍下面首先介绍两个重要的数据类型:sockaddr 和sockaddr_in,这两个结构类型都是用来保存socket信息的,如下所示:struct sockaddr unsigned short sa_family; /*地址族*/char sa_data14; /*14字节的协议地址,包含该socket的IP地址和端口号。*/;struct sockaddr_in short int sa_family; /*地址族*/unsigned short int sin_port; /*端口号*/struct in_addr sin_addr; /*IP地址*/unsigned char sin_zero8; /*填充0 以保持与struct sockaddr同样大小*/;这两个数据类型是等效的,可以相互转化,通常sockaddr_in数据类型使用更为方便。在建立socketadd或sockaddr_in后,就可以对该socket 进行适当的操作了。1.6.2 地址格式转化(1)函数说明通常用户在表达地址时采用的是点分十进制表示的数值(或者是以冒号分开的十进制IPv6 地址),而在通常使用的socket 编程中所使用的则是二进制值,这就需要将这两个数值进行转换。这里在IPv4 中用到的函数有inet_aton、inet_addr 和inet_ntoa,而IPv4 和IPv6 兼容的函数有inet_pton 和inet_ntop。由于IPv6 是下一代互联网的标准协议,因此本系统使用IPv4 为例协议。这里inet_pton 函数是将点分十进制地址映射为二进制地址,而inet_ntop 是将二进制地址映射为点分十进制地址。(2)函数格式表 3.3 列出了inet_pton函数的语法要点。表 3.3 inet_pton函数语法要点所需头文件#include 函数原型int inet_pton(int family,const char *strptr, void *addrptr)函数传入值FamilyAF_INET:IPv4协议AF_INET6:IPv6协议strptr:要转化的值addrptr:转化后的地址函数返回值成功:0出错:1表 3.4 列出了inet_ntop函数的语法要点。表3.4 inet_ntop 函数语法要点所需头文件#include 函数原型int inet_ntop(int family, void *addrptr, char *strptr, size_t len)函数传入值familyAF_INET6:IPv6协议AF_INET:IPv4协议addrptr:转化后的地址strptr:要转化的值Len:转化后值的大小函数返回值成功:0出错:11.7连接函数的说明1.7.1 连接的流程图在客户/服务器模型中,多个相互通信的计算机都作为客户端,与网络服务器进行连接,并通过服务器进行信息的传递。所以多个客户端之间的通信就变为了客户端与服务端的通信。所以,采用客户/服务器模型进行网络聊天需要分别编写服务器端和客户端的程序,服务器和客户端之间相互通信的同步关系和各自的程序流程如实验图3.3所示。图3.3 使用TCP协议socket编程流程图1.7.2 函数格式(1)Socket()作用:socket函数为客户机或服务器创建一个sokcet格式:int socket(int family,int type,int protocol); 参数说明:Family:表示地址族,可以去AF_UNLX和AF_INT。其中,AF_UNLX只能够用于单一的UNIX系统进程间通信;AF_INT是针对Internet的,因而可以允许在远程主机之间通信,实验中使用AF_INT。Type:网络程序所采用的通信协议,可以取SOCK_STREAM或SOCK_DGRAM。其中,SOCK_STREAM表明使用的是TCP协议,这样提供按顺序的、可靠的、双向、面向连接的比特流;SOCKE_DGRAM表明使用的是UDP协议,这样只会提供定长、不可靠、无连接的通信。(2)bind( )格式: int bind(int sockfd,struct sockaddr *addr,int addrlen); 参数说明: Sockfd:socket的文件描述符号。 Sockaddr:表示名字所用的一个数据结构,用来保存地址(包括IP地址和端口)。 Addrlen:设置结构大小长度。(3) listen()格式: int listen(int sockfd, int backlog); 作用:监听连接信号,和accepted函数合同。参数说明:Sockfd:表示socket调用返回的文件描述符。Backlog:表示接入队列允许的连接数目,大多数系统允许20个,也可以子定义510个。(4) accept() 格式: Int accept (int sockfd, void *addr, int *addrlen)。 作用:与listen函数合用,监听信息、接收客户端请求。 参数说明: Sockfd:表示socket的文件描述符。 Addr:表示指向局部的数据结构struct sockaddr-in的指针。 Addrlen:表示地址的长度。(5) connect()格式: int connect( int sockfd , struct sockaddr *serv_addr , int addrlen)。作用:在面向连接的系统中客户及连接服务器时使用,connect必须在bind后使用。 参数作用: Sockfd:表示socket的文件描述符。 Serv-addr:表示村访目的端口和ip地址(套接字)的数据结构。(6) send() 和 recv()格式1:Int send (int sockfd, const vod *msg,int len, int flags)。功能:发送信息。格式2:Int recv (int sockfd , void *buf,int len, usigned int flags)。作用:用于流式socket、数据报socket内部之间的通信。(7) close( )格式: Close( int sockfd)。第二章 Linux网络聊天室socket 编程实现过程32.1聊天室Socket编程连接的过程2.1.1监听连接 利用socket、bind、listen建立连接,步骤是:(1)先用socket函数初始化socket,创建新的sock_fd。sock_fd=socket(AF_INET,SOCK_STREAM,0);(2)此步骤涉及到IP地址及其处理过程。参数说明:inet_addr 函数 INADDR_ANY该函数把由小数点分开的十进制IP地址转为unsinged long 类型,而在实验中所使用的为INADDR_ANY,使用利用自已的IP地址自动填充。(3)利用bind函数绑定端口和IP地址。sin.sin_family=AF_INET; /*将地址族类型设定好 */sin.sin_port=htons(port); /* 将端口给其赋值*/inet_pton(AF_INET,ip_buf,&pin.sin_addr); /*用连接地址自动填充ip*/rst = connect(sock_fd,(void*)&pin,sizeof(pin);/*sock_fd 是分配的socket名字,pin则便是分配好的端口与IP,用bind绑定*/。 (4)利用listen监听请求 listen(listen_fd,T_MAX);2.1.2 发送请求(1)获取主机信息。(2)初始化socket端口。(3)利用connect函数将自己的IP地址等信息发送到主机,等待主机调用accept函数来接受请求。2.1.3 主机接收请求,进行数据通信(1)主机利用accept接收请求。(2)创建子进程,显示欢迎信息;(3)接收返回信息,显示连接成功,并退出连接;(4)关闭客户端口socket以上的过程总结为服务器端应用程序在一个端口监听对服务的请求,也就是说,服务线程一直处于休眠状态,直到有一个客户对这个服务提出了连接请求,此时服务线程被“唤醒”并为客户提供服务,即对客户的请求做出适当的反应18 。接着自己继续监听,新建的线程执行完毕,自动关闭连接,释放资源。这样就实现了socket通讯,如图4.1所示。 服务器 客户端图4.1 单个客户端socket连接2.2客户端主界面及聊天功能展示2.2.1 用GCC对客户端和服务器端的.c文件进行编辑如图2.2.2 进入服务器端主界面客户端正常启动后,在命令行输入./ser,后点击回车,进入客户端页面,如图2.2.3 进入客户端服务器正常启动后,客户在命令行输入./client,后点击回车,进入后输入服务器的ip及端口号,才能和服务器建立链接。2.2.4 输入自己的用户姓名,即可进行聊天,可实现群聊和私人聊天2.2.5群聊在用户界面,每用用户加入,都会有提示.输入聊天内容,在服务器内聊天的用户都能看到,如图2.2.6 私聊在选项中选择2,进行私人聊天.输入想要进行私聊的用户的姓名,即可实现私聊,如图 第三章 总结本文是Linux下socket网络聊天室系统设计的分析和实现过程,此次设计中主要以下的几点知识,要注意掌握。3.1 Linux发展迅速,目前已是嵌入式行业的热点。主要基于它的内核小、效率高、兼容性好和稳定性强等优点。Linux下c语言编程被广泛使用,同时Linux还提供了GCC编译器和GDB调试器,来编译和调试.c文件。但当需要编译的文件较多时,Linux又使用了makefile文件来按照依赖关系及规则来编译文件。3.2服务器与客户端在实现多任务时使用了多线程。每个线程有自己的任务,并互相独立。Posix线程是一个POSIX标准线程.该标准定义内部API创建和操纵线程。对于多个并发的任务需要创建多个线程去实现。3.3 Socket套接字建立的c/s通信模式,Socket接口上TCP/IP网络应用程序接口(API),它提供了许多函数(socket、bind、listen、rev、read等函数),本系统就使用它们来开发TCP/IP网络聊天室程序。本设计中有几个难题经过思考已被解决。首先,网络聊天系统使用多线程就涉及到线程同步问题,需要用到互斥量mutex。互斥对象是系统内核对象, 各线程都可以拥有它, 谁拥有谁就能执行; 执行完毕, 用 Release Mutex 函数释放拥有权, 以让其他等待的线程使用。其次,Linux下一个完整的套接字则用一个相关描述协议,本地地址、本地端口、远程地址、远程端口。这里的端口和远程地址需要客户在建立的开始输入ip和端口号。此设计的预期分析的几个功能已基本都实现,但还有些功能不全,例如某个用户总是发送垃圾信息,则应该屏蔽他的信息发送请求,即屏蔽功能。又如,建立群组织功能等,本设计仍需改善及功能加强。 程序代码:服务器端:/*server.c*/#include#include#include#include/使用read()函数需要加的头文件#include#include#include#include#include#define MAX_LINE 10240#define MAXDATASIZE 128#define MAXMEM 10#define NAMELEN 20int connfdMAXMEM;char nameMAXMEMNAMELEN; void chart_personal(char buff,int n)int len,i;int flag;char chMAX_LINE;char ch1MAXDATASIZE;char buff2MAX_LINE;while(1)len=read(connfdn,ch, MAX_LINE);/读名字flag=0;if(strcmp(ch,quit)=0)return ;for(i=0;iMAXMEM;i+) /执行他一次 if(connfdi!=-1&i!=n&strcmp(ch,namei)=0) len=read(connfdn,ch1, MAX_LINE);/读内容 printf(%s) 发送 (%s) 内容 is:%sn,namen,ch,ch1); strcpy(buff2,buff); strcat(buff2,ch1); write(connfdi,buff2,strlen(buff2)+1); flag=1; memset(ch,0,strlen(ch)-1);/清空 memset(ch1,0,strlen(ch1)-1);/清空 break; if(flag=0)printf(您输入的用户不存在,清重新输入n);write(connfdn,*er,6);elsebreak;void chart_together(char buff,int n)int len,i;char buff2MAX_LINE;char chMAX_LINE;while(1)len=read(connfdn,ch, MAX_LINE);printf(-n);if(strcmp(ch,quit)=0)printf(%s 退出群聊n,namen); memset(ch,0,strlen(ch)-1);/清空 break; strcpy(buff2,buff);strcat(buff2,ch);printf(%s send all root content is:%sn,buff,ch); for(i=0;i0) namenlen=0; strcpy(buff,*); strcat(buff,namen); strcat(buff,:); printf(root:%s join inn,buff); while(1) if(len=read(connfdn,buff1, MAX_LINE)0) /读取用户信息 if(strcmp(buff1,1)=0) chart_together(buff,n); else if(strcmp(buff1,2)=0)chart_personal(buff,n); else if(strcmp(buff1,3)=0)/下载 down_file(n); else if(strcmp(buff1,4)=0)/上传 up_file(n); else if(strcmp(buff1,*quit)=0) printf(%s has exitn,buff); write(connfdn,buff1,strlen(buff1)+1); close(connfdn); connfdn=-1; close(connfdn); continue; else printf(buff1=%sn,buff1); printf(输入有误,重新输入n); memset(buff1,0,strlen(buff1)-1);/清空 int main(void)struct sockaddr_in sin;/设置套接字地址结构struct sockaddr_in cin;pthread_t thread; int lfd;int i;socklen_t len;/去得客户端地址的大小char bufMAX_LINE;/存储传送内容的缓冲区char addr_pINET_ADDRSTRLEN;/存储客户端地址的缓冲区int port=8003;int n;/读写字节数int opt=1;/套接字选项bzero(&sin,sizeof(sin);/清空地址结构sin.sin_family=AF_INET;/使用IPV4通信域sin.sin_addr.s_addr=INADDR_ANY;/服务器可以接受任意地址sin.sin_port=htons(port);/端口号转换为网络字节16位if(lfd=socket(AF_INET,SOCK_STREAM,0)=-1)/判断是否绑定成功perror(fail to creat socket);/打印错误信息exit(1);setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt);if(bind(lfd,(struct sockaddr*)&sin,sizeof(sin)=-1)/判断是否绑定成功perror(fail to bind);exit(1);if(listen(lfd,10)=-1)/进行侦听队列长度的绑定perror(fail to listen);exit(1);printf(waiting.n);for(i=0;iMAXMEM;i+) connfdi=-1; while(1) len=sizeof(cin); for(i=0;iMAXMEM;i+) if(connfdi=-1) break; if(connfdi=accept(lfd,(struct sockaddr*)&cin,&len)=-1)/判断接收客户端连接是否成功 perror(fail to accept);exit(1); pthread_create(&thread,NULL,(void*)(&rcv_snd),(void*)i); if(close(lfd)=-1)perror(fail to close);exit(1);return 0;客户端:/*client.c*/#include#include#include#include/1文件/运用函数opean(),create()所需要的头文件#include/2#include/socket编程#include#include/编写linux下的线程需要包含头文件pthread.h#include/3#define MAX_LINE 10240#define MAXDATASIZE 128int ma=0,mb;char filenameMAX_LINE;void up_file(char ch,int connfd)int open_fd1,read_count,write_count;char ch1MAX_LINE;/打开的文件名open_fd1 = open(ch, O_RDONLY);/打开文件为只读方式if (read_count = read(open_fd1, ch1, sizeof(ch) 0) /如果文件读取成功,则返回读取的字节数,当返回-1时该读取函数有错误发生 write_count = write(connfd, ch1, read_count);/函数操作成功会返回写入的字节数,当出错的时候则返回-1if (write_count 0)if(strncmp(recvline,p,3)!=0)/输入的不是字符,是文件if(i=1)printf(请输入你要保存名字:);scanf(%s,filename);open_fd=creat(filename,0700);open_fd = open(filename, O_WRONLY|O_APPEND);if (retval 0) w = write(open_fd, recvline, retval);else if(strcmp(recvline,*quit)=0)printf(退出n);break;else if(strcmp(recvline,*er)=0)ma=1;i=0;printf(输入的用户名有误重新输入n);memset(recvline,0,strlen(recvline)-1);/清空continue;elsei=0;printf(%sn,recvline);memset(recvline,0,strlen(recvline)-1);/清空i+;close(open_fd);void *snd_person(void *sfd)int n;int i=0;char kMAX_LINE;int connfd = *(int *)sfd);char strMAX_LINE ;while(1)if(i=0)scanf(%s,str);n=write(connfd,str,strlen(str)+1);/发送字符串,该串包含0if(n=-1)perror(write error);exit(1);memset(str,0,strlen(str)-1);/清空 else printf(tt欢迎进入聊天室!n);printf(n);printf(1.群聊n);printf(2.私人聊天n);printf(quit:退出n);printf(n);printf(请输入你的选择:)scanf(%s,k);n=write(connfd,k,strlen(str)+2);/发送字符串,该串包含0if(strcmp(k,1)=0)chart_together(connfd);

温馨提示

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

评论

0/150

提交评论