UNIX中的套接字网络编程接口课件_第1页
UNIX中的套接字网络编程接口课件_第2页
UNIX中的套接字网络编程接口课件_第3页
UNIX中的套接字网络编程接口课件_第4页
UNIX中的套接字网络编程接口课件_第5页
已阅读5页,还剩86页未读 继续免费阅读

下载本文档

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

文档简介

UNIX中的套接字網路編程介面

2.1.1問題的提出站在應用程式實現的角度,應用程式如何方便地使用協議棧軟體進行通信呢?如果能在應用程式與協議棧軟體之間提供一個軟體介面,就可以方便客戶與伺服器軟體的編程。2.1UNIX套接字網路編程介面的產生與發展

套接字應用程式編程介面是網路應用程式通過網路協議棧進行通信時所使用的介面,即應用程式與協議棧軟體之間的介面,簡稱套接字編程介面(SocketAPI)。它定義了應用程式與協議棧軟體進行交互時可以使用的一組操作,決定了應用程式使用協議棧的方式、應用程式所能實現的功能、以及開發具有這些功能的程式的難度。

加州大學伯克利(Berkley)分校開發並推廣了一個包括TCP/IP互聯協議的UNIX,稱為BSDUNIX(BerkeleySoftwareDistributionUNIX)操作系統,套接字編程介面是這個操作系統的一個部分。後來的許多操作系統並沒有另外搞一套其他的編程介面,而是選擇了對於套接字編程介面的支持。由於這個套接字規範最早是由Berkeley大學開發的,一般將它稱為BerkeleySockets規範。2.1.2套接字編程介面的起源與應用

要想實現套接字編程介面,可以採用兩種實現方式,一種是在操作系統的內核中增加相應的軟體來實現,一種是通過開發操作系統之外的函數庫來實現。

2.1.3套接字編程介面的兩種實現方式

UNIX操作系統對檔和所有其他的輸入/輸出設備採用一種統一的的操作模式,就是“打開-讀-寫-關閉”(open-read-write-close)的I/O模式。當TCP/IP協議被集成到UNIX內核中的時候,相當於在UNIX系統中引入了一種新型的I/O操作,就是應用程式通過網路協議棧來交換數據。

2.1.4套接字通信與UNIX操作系統的輸入/輸出

在UNIX系統的實現中,套接字是完全與其他I/O集成在一起的。操作系統和應用程式都將套接字編程介面也看作一種輸入/輸出機制。但是,用戶進程與網路協議的交互作用實際要比用戶進程與傳統的I/O設備相互作用要複雜得多。

其次,使用套接字的應用程式必須說明許多細節。僅僅提供open、read、write、close四個過程遠遠不夠。為避免單個套接字函數參數過多,套接字編程介面的設計者定義了多個函數。2.2套接字編程的基本概念

套介面是對網路中不同主機上應用進程之間進行雙向通信的端點的抽象,一個套介面就是網路上進程通信的一端,提供了應用層進程利用網路協議棧交換數據的機制。

圖2.1電插座與電話插座的作用2.2.1什麼是套接字(SOCKET)

我們應當從多個層面來理解套接字這個概念的內涵。從套接字所處的地位來講,套接字上聯應用進程,下聯網路協議棧,是應用程式通過網路協議棧進行通信的介面,是應用程式與網路協議棧進行交互的介面。圖2.2應用進程、套介面、網路協議棧及操作系統的關係

從實現的角度來講,非常複雜。套接字是一個複雜的軟體機構,包含了一定的數據結構,包含許多選項,由操作系統內核管理。從使用的角度來講,非常簡單。對於套接字的操作形成了一種網路應用程式的編程介面(API)。本書把這一套操作套接字的編程介面函數稱作套接字編程介面,套接字是它的操作對象。總之,套接字是網路通信的基石。

2.2.2套接字的特點1.通信域

接字存在於通信域中,通信域是為了處理一般的進程通過套接字通信而引入的一種抽象概念,套接字通常只和同一域中的套接字交換數據。如果數據交換要穿越域的邊界,就一定要執行某種解釋程式。現在,僅僅針對Internet域,並且使用Internet協議族(即TCP/IP協議族)來通信。2.套接字具有三種類型每一個正被使用的套接字都有它確定的類型,只有相同類型的套接字才能相互通信。(1)數據報套接字(DatagramSOCKET)數據報套接字提供無連接的不保證可靠的獨立的數據報傳輸服務。在Internet通信域中,數據報套接字使用UDP數據報協議形成的進程間通路,具有UDP協議為上層所提供的服務的所有特點。圖2.3在Internet通信域中,數據報套接字基於UDP協議

(2)流式套接字(StreamSOCKET)流式套接字提供雙向的、有序的、無重複的、無記錄邊界的可靠的數據流傳輸服務。在Internet通信域中,流式套接字使用TCP協議形成的進程間通路,具有TCP協議為上層所提供的服務的所有特點,在使用流式套接字傳輸數據之前,必須在數據的發送端和接收端之間建立連接,如圖2.4所示。

圖2.4在Internet通信域中,流式套接字基於TCP協議(3)原始式套接字(RAWSOCKET)

原始式套接字允許對較低層次的協議,如IP、ICMP直接訪問,用於檢驗新的協議的實現。3.套接字由應用層的通信進程創建,並為其服務就是說,每一個套接字都有一個相關的應用進程,操作該套接字的代碼是該進程的組成部分。4.使用確定的IP地址和傳輸層端口號

往往在生成套接字的描述符後,要將套接字與電腦上的特定的IP地址和傳輸層端口號相關聯,這個過程稱為綁定。一個套介面要使用一個確定的三元組網絡地址資訊,才能使它在網路中唯一地被標識。(1)不管是採用對等模式或者客戶機/伺服器模式,通信雙方的應用程式都需要開發。(2)雙方所交換數據的結構和交換數據的順序有特定的要求,不符合現在成熟的應用層協議,甚至需要自己去開發應用層協議,自己設計最適合的數據結構和資訊交換規程。

2.2.3套接字的應用場合2.2.4套接字使用的數據類型和相關的問題1.三種表示套接字地址的結構在套接字編程介面中,專門定義了三種結構型的數據類型,用來存儲協議相關的網路地址,在套接字編程介面的函數調用中要用到它們。(1)sockaddr結構,針對各種通信域的套接字,存儲它們的地址資訊。struct

sockaddr{unsignedshortsa_family;//地址家族charsa_data;//協議地址}

(2)sockaddr_in結構,專門針對Internet通信域,存儲套接字相關的網路地址資訊,例如IP地址,傳輸層端口號等資訊。struct

sockaddr_in{shortintsin_family;//地址家族unsignedshortintsin_port;//端口號struct

in_addr

sin_addr;//IP地址unsignedcharsin_zero[8];//全為0}

(3)in_addr結構,專門用來存儲IP地址。Struct

in_addr{Unsignedlongs_addrl;}(4)這些數據結構的一般用法:首先,定義一個Sockaddr_in的結構實例,並將它清零。比如:struct

sockaddr_in

myad;memset(&myad,0,sizeof(structsockaddr_in));

然後,為這個結構賦值,比如:myad.sin_family=AF_INET;

myad.sin_port=htons(8080);

myad.sin_addr.s_addr=htonl(INADDR-ANY);第三步:在函數調用中使用時,將這個結構強制轉換為sockaddr類型。如:accept(listenfd,(sockaddr*)(&myad),&addrlen);

2.本機位元組順序和網路位元組順序在具體電腦中的多位元組數據的存儲順序,稱為本機位元組順序。多位元組數據在網路協議報頭中的存儲順序,稱為網路位元組順序。

網路應用程式要在不同的電腦中運行,本機位元組順序是不同的,但是,網路位元組順序是一定的。所以,應用程式在編程的時候,在把IP地址和端口號裝入套接字的時候,應當把它們從本機位元組順序轉換為網路位元組順序;相反,在本機輸出時,應將它們從網路位元組順序轉換為本機位元組順序。

套接字編程介面特為解決這個問題設置了四個函數:htons()短整數本機順序轉換為網路順序,用於端口號。htonl()長整數本機順序轉換為網路順序,用於IP地址。ntohs()短整數網路順序轉換為本機順序,用於端口號。ntohl()長整數網路順序轉化為本機順序,用於IP地址。這四個函數將被轉換的數值作為函數的參數,函數返回值是轉換後的結果。

3.點分十進位的IP地址的轉換在因特網中,IP地址常常用點分十進位的表示方法,但在套接字中,IP地址是無符號的長整型數,套接字編程介面設置了兩個函數,專門用於兩種形式的IP地址的轉換。(1)inet-addr函數

unsignedlonginet-addr(constchar*cp)入口參數cp:點分十進位形式的IP地址。返回值:網路位元組順序的IP地址,是無符號的長整數,(2)inet_ntoa函數char*inet_ntoa(struct

in_addrin)入口參數in:包含長整型IP地址的in_addr

結構變數,返回值:指向點分十進位IP地址的字串的指針。

通常,我們使用功能變數名稱來標識站點,可以將文字型的主機功能變數名稱直接轉換成IP地址,struct

hostent*

gethodtbyname(constchar*name);入口參數:是站點的主機功能變數名稱字串,返回值:是指向hostent

結構的指針,hostent結構包含主機名,主機別名數組,返回地址的類型(一般是AF-INET),地址長度的位元組數,已符合網路位元組順序的主機網路地址等。

4.功能變數名稱服務

2.3面向連接的套接字編程2.3.1套接字的工作過程2.3.2UNIX套接字編程介面的系統調用1.創建套接字SOCKET()SOCKET過程創建一個套接字並返回一個整型描述符:intSOCKET(int

Protofamily,intType,intProtocol);

2.綁定套接字到指定的地址BIND()

intBIND(int

Sockfd,struct

sockaddr*

My_addr,int

Addrlen);

3.啟動監聽Listen()

intLISTEN(int

Sockfd,int

Queuesize);舉例:LISTEN(Sockfe,10);

圖2.6監聽套接字使用緩衝區接納多個客戶端的連接請求

4.接收連接請求ACCEPT()intACCEPT(int

Sockfd,struct

sockaddr*

Addr,int*

addrlen);舉例:int

clientfd;//定義回應套接字描述符變數int

addrler=sizeof(sockaddr);//獲得套接字地址結構長度。struct

sockaddr_in

cltsockaddr;//定義用於返回客戶端地址的結構。clientfd=ACCEPT(listenfd,(sockaddr*)(&cltsockaddr),&addrlen);//接收客戶連接請求

5.請求建立連接CONNECT()intCONNECT(int

Sockfd,struct

sockaddr*

Service_addr,int

Addrlen);舉例:if(CONNECT(sockfd,(struct

sockaddr*)(&serv_addr),sizeof(struct

sockaddr))<0){報錯,並退出}

6.讀/寫套接字READ()和WRITE()intREAD(int

sockfd,void*buffer,int

len);intWRITE(int

sockfd,void*buffer,int

len)

7.向套接字發送SEND()和從套接字接收RECV()

intSEND(int

sockfd,char*buf,int

len,intflags);intRECV(int

sockfd,char*buf,int

len,intflags);

8.關閉套接字CLOSE()

intCLOSE(int

sockfd);2.3.3面向連接的套接字編程實例1.實例的功能伺服器對來訪的客戶計數,並向客戶報告這個計數值。客戶建立與伺服器的一個連接並等待它的輸出。每當連接請求到達時,伺服器生成一個可列印的ASCII串資訊,將它在連接上發回,然後關閉連接。客戶顯示收到的資訊,然後退出。

例如,對於伺服器接收的第10次客戶連接請求,該客戶將收到並列印如下資訊:Thisserverhasbeencontacted10times.2.實例程式的命令行參數實例是UNIX環境下的C程式,客戶和服務器程式在編譯後,均以命令行的方式執行。伺服器程式執行時可以帶一個命令行參數,是用來接受請求的監聽套接字的協議端口號。這個參數是可選的。如果不指定端口號,代碼將使用程式內定的缺省端口號5188。

客戶程式執行時可以帶兩個命令行參數:一個是伺服器所在電腦的主機名,另一個是伺服器監聽的協議端口號。這兩個參數都是可選的。如果沒有指定協議端口號,客戶使用程式內定的缺省值5188。如果一個參數也沒有,客戶使用缺省端口和主機名localhost,localhost是映射到客戶所運行的電腦的一個別名。允許客戶與本地機上的伺服器通信,對調試是很有用的。3.客戶程式代碼/*----------------------------------------------------*程式:client.c*目的:創建一個套接字,通過網路連接一個伺服器,並列印來自伺服器的資訊*語法:client[host[port]]*host-運行伺服器的電腦的名字*port-伺服器監聽套接字所用協議端口號*注意:兩個參數都是可選的。如果未指定主機名,客戶使用localhost;如果未指定端口號,*客戶將使用PROTOPORT中給定的缺省協議端口號*----------------------------------------------------*/

#include<sys/types.h>#include<sys/socket.h>/*UNIX下,套接字的相關包含檔。*/#include<netinet/in.h>#include<arpa/inet.h>#include<netdb.h>#include<stdio.h>#include<string.h>

#definePROTOPORT5188/*缺省協議端口號*/externint

errno;charlocalhost=“localhost”;/*缺省主機名*/

main(argc,argv)int

argc;char*argv[];{struct

hostent

*ptrh;/*指向主機列表中一個條目的指針*/struct

sockaddr_in

servaddr;/*存放伺服器端網路地址的結構*/int

sockfd;/*客戶端的套接字描述符*/int port;/*伺服器端套接字協議端口號*/char*host;/*伺服器主機名指針*/intn;/*讀取的字元數*/charbuf[1000];/*緩衝區,接收伺服器發來的數據*/

memset((char*)&servaddr,0,sizeof(servaddr));/*清空sockaddr結構*/servaddr.sin_family=AF_INET;/*設置為因特網協議族*/

/*檢查命令行參數,如果有,就抽取端口號。否則使用內定的缺省值*/if(argc>2){port=atoi(argv[2]);/*如果指定了協議端口,就轉換成整數*/}else{port=PROTOPORT;/*否則,使用缺省端口號*/}if(port>0)/*如果端口號是合法的數值,就將它裝入網路地址結構*/

servaddr.sin_port=htons((u_short)port);else{/*否則,列印錯誤資訊並退出*/fprintf(stderr,”badportnumber%s\n”,argv[2]);exit(1);}

/*檢查主機參數並指定主機名*/if(argc>1){host=argv[1];/*如果指定了主機名參數,就使用它*/}else{host=localhost;/*否則,使用缺省值*/}

/*將主機名轉換成相應的IP地址並複製到servaddr

結構中*/ptrh=gethostbyname(host);/*從伺服器主機名得到相應的IP地址*/if((char*)ptrh==null){/*檢查主機名的有效性,無效則退出*/fprintf(stderr,”invalidhost:%s\n”,host);exit(1);}memcpy(&servaddr.sin_addr,ptrh->h_addr,ptrh->h_length);

/*創建一個套接字*/sockfd=SOCKET(AF_INET,SOCK_STREAM,0);if(sockfd<0){fprintf(stderr,”socketcreationfailed\n”);exit(1);}

/*請求連接到伺服器*/if(connect(sockfd,(struct

sockaddr*)&servaddr,sizeof(servaddr))<0){fprintf(stderr,”connectfailed\n”);/*連接請求被拒絕,報錯並退出*/exit(1);}

/*從套接字反復讀數據,並輸出到用戶螢幕上*/n=recv(sockfd,buf,sizeof(buf),0);while(n>0){write(1,buf,n);n=recv(sockfd,buf,sizeof(buf),0);}

/*關閉套接字*/closesocket(sockfd);

/*終止客戶程式*/exit(0);}

4.伺服器實例代碼/*------------------------------------------*程式:server.c*目的:分配一個套接字,然後反復執行如下幾步:*(1)等待客戶的下一個連接*(2)發送一個短消息給客戶*(3)關閉與客戶的連接*(4)轉向(1)步*命令行語法:server[port]*port–伺服器端監聽套接字使用的協議端口號*注意:端口號可選。如果未指定端口號,伺服器使用PROTOPORT中指定的缺省*端口號*-----------------------------------------------*/

#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<netdb.h>#include<stdio.h>#include<string.h>#definePROTOPORT5188/*監聽套接字的缺省協議端口號*/#define QLEN6/*監聽套接字的請求佇列大小*/intvisits=0;/*對於客戶連接的計數*/

main(argc,argc)int

argc;char*argv[];{struct

hostent

*ptrh;/*指向主機列表中一個條目的指針*/struct

sockaddr_in

servaddr;/*存放伺服器網路地址的結構*/struct

sockaddr_in

clientaddr;/*存放客戶網路地址的結構*/int

listenfd; /*監聽套接字描述符*/int

clientfd; /*回應套接字描述符*/intport;/*協議端口號*/int

alen;/*地址長度*/charbuf[1000];/*供伺服器發送字串所用的緩衝區*/

memset((char*)&servaddr,0,sizeof(servaddr));/*清空sockaddr結構*/servaddr.sin_family=AF_INET;/*設置為因特網協議族*/servaddr.sin_addr.s_addr=INADDR_ANY;/*設置本地IP地址*/

/*檢查命令行參數,如果指定了,就是用該端口號,否則使用缺省端口號*/if(argc>1){port=atoi(argv[1]);/*如果指定了端口號,就將它轉換成整數*/}else{port=PROTOPORT;/*否則,使用缺省端口號*/}

if(port>0)/*測試端口號是否合法*/servaddr.sin_port=htons((u_short)port);else{/*列印錯誤資訊並退出*/fprintf(stderr,”badport number%s\n”,argv[1]);exit(1);}

/*創建一個用於監聽的流式套接字*/listenfd=SOCKET(AF_INET,SOCK_STREAM,0);if(listenfd<0){fprintf(stderr,“socketcreationfailed\n”);exit(1);}

/*將本地地址綁定到監聽套接字*/if(bind(listenfd,(struct

sockaddr*)&servaddr,sizeof(servaddr))<0){fprintf(stderr,”bindfailed\n”);exit(1);}

/*開始監聽,並指定監聽套接字請求佇列的長度*/if(listen(listenfd,QLEN)<0){fprintf(stderr,”listenfiled\n”);exit(1);}

/*伺服器主迴圈—接受和處理來自客戶端的連接請求*/while(1){alen=sizeof(clientaddr);/*接受客戶端連接請求,並生成回應套接字*/if((clientfd=accept(listenfd,(struct

sockaddr*)&clientaddr,&alen))<0){fprintf(stderr,“acceptfailed\n”);exit(1);}visits++;/*累加訪問的客戶數*/sprintf(buf,“thisserverhasbeencontacted%dtime\n”,visits);send(clientfd,buf,strlen(buf),0);/*向客戶端發送資訊*/closesocket(clientfd);/*關閉回應套接字*/}}

關於阻塞的問題

圖2.7伺服器進程因調用ACCEPT()而被阻塞

2.3.4進程的阻塞問題和對策

1.什麼是阻塞阻塞是指一個進程執行了一個函數或者系統調用,該函數由於某種原因不能立即完成,因而不能返回調用它的進程,導致進程受控於這個函數而處於等待的狀態,進程的這種狀態稱為阻塞。圖2.8RECV()函數的兩種執行方式

2.能引起阻塞的套接字調用在Berkeley套接字網路編程介面的模型中,套接字的默認行為是阻塞的,具體地說,在一定情況下,有多個操作套接字的系統調用會引起進程阻塞。(1)ACCEPT()(2)READ()、RECV()和READFORM()(3)WRITE()、SEND()和SENDTO()(4)CONNECT()(5)SELECT()(6)CLOSESOCKET()

3.阻塞工作模式帶來的問題採用阻塞工作模式的單進程伺服器是不能很好地同時為多個客戶服務的。圖2.9是一個例子。

圖2.9採用阻塞工作模式的伺服器不能很好地為多個客戶服務

4.一種解決方案

利用UNIX操作系統的FORK()系統調用,編制多進程併發執行的伺服器程式。可以創建子進程。對於每一個客戶端,用一個專門的進程為它服務,通過進程的併發執行,來實現對多個客戶的併發服務。基本的編程框架是:父進程代碼If((pid=FORK())==0){…….子進程代碼........}elseif(pid<0){報錯資訊}父進程代碼

舉例:#include<sys/types.h>#include<sys/socket.h>#include<stdio.h>#include<arpa/inet.h>

voidmain(int

argc,char**argv){int

listenfd,clientfd,pid;struct

sockaddr_in

ssockaddr,csockaddr;charbuffer[1024];int

addrlen,n;

/*創建監聽套接字*/listenfd=socket(AF_INET,SOCK_STREAM,0);if(listenfd<0){fprintf(stderr,"socketerror!\n");exit(1);}

/*為監聽套接字綁定網路地址*/memset(&ssockaddr,0,sizeof(structsockaddr_in));ssockaddr.sin_family=AF_INET;ssockaddr.sin_addr.s_addr=htonl(INADDR_ANY);ssockaddr.sin_port=htons(8080);if(bind(listenfd,&ssockaddr,sizeof(struct

sockaddr_in))<0){fprintf(stderr,"binderror!\n");exit(2);}

/*啟動套接字的監聽*/listen(listenfd,5);addrlen=sizeof(sockaddr);

/*伺服器進入迴圈,接受並處理來自不同客戶端的連接請求*/while(1){clientfd=accept(listenfd,(sockaddr*)(&csockaddr),&addrlen);/*accept調用返回時,表明有客戶端請求連接,創建子進程處理連接*/If((pid=FORK())==0){/*顯示客戶端的網路地址*/printf("ClientAddr:%s%d\n",inet_ntoa(csockaddr.sin_addr),ntohs(csockaddr.sin_port));/*讀取客戶端發送來的數據,在將它們返回到客戶端*/while((n=read(clientfd,buffer,1024))>0){buffer[n]=0;printf("ClientSend:%s",buffer);write(clientfd,buffer,n);}if(n<0){fprintf(stderr,"readerror!\n");exit(3);}

/*通信完畢,關閉與這個客戶連接的套接字*/printf("clent%sclosed!\n",inet_ntoa(csockaddr.sin_addr));close(clientfd);exit(1);}elseif(pid<0)printf("forkfailed!\n");close(clientfd);}close(listenfd);/*關閉監聽套接字*/}

2.4無連接的套接字編程2.4.1無連接的套接字編程的兩種模式使用數據報套接字開發網路應用程式,既可以採用客戶/伺服器模式,也可以採用對等模式。圖2.10對等模式的數據報套接字的編程模型

1.對等模式

2.客戶/伺服器模式

圖2.11C/S模式的數據報套接字的編程模型

2.4.2兩個專用的系統調用1.發送數據報SENDTO()intSENDTO(int

sockfd,constvoid*msg,int

len,unsignedintflags,struct

sockaddr*to,int

tolen);

2.接收數據報RECVFROM()intRECVFROM(int

sockfd,void*buf,int

len,unsignedintflags,struct

sockaddr*from,int*

fromlen)

2.4.3數據報套接字的對等模式編程實例聊天程式#include<sys/types.h>#include<unistd.h>#include<error.h>#include<sys/socket.h>#include<arpa/inet.h>#include<stdio.h>

/*中斷處理過程*/voidint_proc(int

signo){}

voidmain(int

argc,char**argv){struct

sockaddr_in

daddr,saddr,cmpaddr;int

sockfd;inttimer=3;charbuffer[1024];int

addrlen,n;

/*判斷用戶輸入的命令行是否正確,如果有錯,提示用法*/if(argc!=5){printf("用法:%s目的IP目的端口源IP源端口\n",argv[0]);exit(0);}

/*設定中斷處理函數,並設置時間限制*/signal(SIGALRM,int_proc);alarm(timer);

/*建立數據報套接字*/sockfd=socket(AF_INET,SOCK_DGRAM,0);if

温馨提示

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

评论

0/150

提交评论