Linux程序設計6-第六章-Linux網(wǎng)絡程序設計_第1頁
Linux程序設計6-第六章-Linux網(wǎng)絡程序設計_第2頁
Linux程序設計6-第六章-Linux網(wǎng)絡程序設計_第3頁
Linux程序設計6-第六章-Linux網(wǎng)絡程序設計_第4頁
Linux程序設計6-第六章-Linux網(wǎng)絡程序設計_第5頁
已閱讀5頁,還剩107頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第六章-Linux網(wǎng)絡程序設計基本要求1、了解TCP/IP基礎知識,什么是socket,socket編程,遠程過程調用。2、掌握Linux平臺數(shù)據(jù)結構的傳送方法。TCP/IP協(xié)議概述OSI參考模型與TCP/IP參考模型應用層表示層會話層傳輸層網(wǎng)絡層數(shù)據(jù)鏈路層物理層應用層傳輸層網(wǎng)絡層網(wǎng)絡接口層OSI參考模型TCP/IP參考模型OSI參考模型與TCP/IP參考模型對應關系TCP/IP協(xié)議族TCP/IP實際上一個一起工作的通信家族,為網(wǎng)際數(shù)據(jù)通信提供通路。為討論方便可將TCP/IP協(xié)議組大體上分為三部分:1.Internet協(xié)議(IP)2.傳輸控制協(xié)議(TCP)和用戶數(shù)據(jù)報文協(xié)議(UDP)3.處于TCP和UDP之上的一組協(xié)議專門開發(fā)的應用程序。它們包括:TELNET,文件傳送協(xié)議(FTP),域名服務(DNS)和簡單的郵件傳送程序(SMTP)等許多協(xié)議。網(wǎng)絡層第一部分也稱為網(wǎng)絡層。包括Internet協(xié)議(IP)、網(wǎng)際控制報文協(xié)議(ICMP)和地址識別協(xié)議(ARP).Internet協(xié)議(IP)。該協(xié)議被設計成互聯(lián)分組交換通信網(wǎng),以形成一個網(wǎng)際通信環(huán)境。它負責在源主機和目的地主機之間傳輸來自其較高層軟件的稱為數(shù)據(jù)報文的數(shù)據(jù)塊,它在源和目的地之間提供非連接型傳遞服務。

網(wǎng)際控制報文協(xié)議(ICMP)。它實際上不是IP層部分,但直接同IP層一起工作,報告網(wǎng)絡上的某些出錯情況。允許網(wǎng)際路由器傳輸差錯信息或測試報文。地址識別協(xié)議(ARP)。ARP實際上不是網(wǎng)絡層部分,它處于IP和數(shù)據(jù)鏈路層之間,它是在32位IP地址和48位局域網(wǎng)物理地址之間執(zhí)行翻譯的協(xié)議。傳輸層協(xié)議第二部分是傳輸層協(xié)議。包括傳輸控制協(xié)議和用戶數(shù)據(jù)報文協(xié)議。

傳輸控制協(xié)議(TCP)。由于IP提供非連接型傳遞服務,因此TCP應為應用程序存取網(wǎng)絡創(chuàng)造了條件,使用可靠的面向連接的傳輸層服務。該協(xié)議為建立網(wǎng)際上用戶進程之間的對話負責。此外,還確保兩個以上進程之間的可靠通信。它所提供的功能如下。1.監(jiān)聽輸入對話建立請求。2.請求另一網(wǎng)絡站點對話。3.可靠的發(fā)送和接收數(shù)據(jù)。4.適度的關閉對話。應用程序部分用戶數(shù)據(jù)報文協(xié)議(UDP)。UDP提供不可靠的非連接型傳輸層服務,它允許在源和目的地站點之間傳送數(shù)據(jù),而不必在傳送數(shù)據(jù)之前建立對話。此外,該協(xié)議還不使用TCP使用的端對端差錯校驗。當使用UDP時,傳輸層功能全都發(fā)揮,而開銷卻比較低。它主要用于那些不要求TCP協(xié)議的非連接型的應用程序。例如,名字服務、網(wǎng)絡管理、視頻點播和網(wǎng)絡會議等。最后是應用程序部分。這部分包括Telnet,文件傳送協(xié)議(FTP和TFTP),簡單的文件傳送協(xié)議(SMTP)和域名服務(DNS)等協(xié)議。TCP/IP使用了主干網(wǎng)絡,能連接各種主機和LAN的多級分層結構,局部用戶能方便的聯(lián)網(wǎng),不致影響到整個網(wǎng)絡系統(tǒng)。此外這種結構還有利于局部用戶控制操作和管理。TCP/IP具有兩個主要功能。第一是IP在網(wǎng)絡之間(有時在個別網(wǎng)絡內部)提供路由選擇。第二是TCP將TP傳遞的數(shù)據(jù)傳送的接收主機那的適當?shù)奶幚聿考?。Internet協(xié)議(IP)IP主要有以下四個主要功能:(1)數(shù)據(jù)傳送(2)尋址(3)路由選擇(4)數(shù)據(jù)報文的分段IP功能IP的主要目的是為數(shù)據(jù)輸入/輸出網(wǎng)絡提供基本算法,為高層協(xié)議提供無連接的傳送服務。這意味著在IP將數(shù)據(jù)遞交給接收站點以前不在傳輸站點和接收站點之間建立對話(虛擬鏈路)。它只是封裝和傳遞數(shù)據(jù),但不向發(fā)送者或接收者報告包的狀態(tài),不處理所遇到的故障。IP協(xié)議不注意包內的數(shù)據(jù)類型,它所知道的一切是必須將某些稱為IP幀頭的控制協(xié)議加到高層協(xié)議(TCP或者UDP)所接受的數(shù)據(jù)上。IP地址IP地址為32位地址,一般以4個字節(jié)表示。每個字節(jié)的數(shù)字又用十進制表示,即每個字節(jié)的數(shù)的范圍是0~255,且每個數(shù)字之間用點隔開,例如:12,這種記錄方法稱為“點-分”十進制記號法。IP地址的結構如下所示:網(wǎng)絡類型網(wǎng)絡ID主機IDIP地址的分類Internet地址可分成5類:A、B、C三類由InterNIC(Internet網(wǎng)絡信息中心)在全球范圍內統(tǒng)一分配,D、E類為特殊地址。

01 78 310網(wǎng)絡地址ID主機地址IDA類IP地址10網(wǎng)絡地址ID主機地址ID0121516 31B類IP地址110網(wǎng)絡地址ID主機地址ID01232324 31C類IP地址1110廣播地址ID01234 31D類IP地址11110保留用于將來和試驗使用012345 31E類IP地址IP地址說明A類網(wǎng)絡地址有128個(支持127)個網(wǎng)絡,占有最左邊的一個字節(jié)(8位)。高位(0)表示識別這種地址的類型。B類地址使用左邊兩個8位用來網(wǎng)絡尋址。兩個高位(10)用于識別這種地址的類型,其余的14位用作網(wǎng)絡地址,右邊的兩個字節(jié)(16位)用作網(wǎng)絡節(jié)點。C類地址是最常見的Internet地址。三個高位(110)用于地址類型識別,左邊三個字節(jié)的其余21位用于尋址。C類地址支持1046個網(wǎng)絡,每個網(wǎng)絡可多達256端點。D類地址是相當新的。它的識別頭是1110,用于組播,例如用于路由器修改。E類地址為時延保留,其識別頭是11110。傳輸控制協(xié)議(TCP)TCP(傳輸控制協(xié)議TransmissionControlProtocol)是重要的傳輸層協(xié)議,傳輸層軟件TCP的目的是允許數(shù)據(jù)同網(wǎng)絡上的另外站點進行可靠的交換。它能提供端口編號的譯碼,以識別主機的應用程序,而且完成數(shù)據(jù)的可靠傳輸。TCP協(xié)議具有嚴格的內裝差錯檢驗算法確保數(shù)據(jù)的完整性。TCP是面向字節(jié)的順序協(xié)議,這意味著包內的每個字節(jié)被分配一個順序編號,并分配給每包一個順序編號。TCP頭信息用戶數(shù)據(jù)報文協(xié)議UDP(用戶數(shù)據(jù)報協(xié)議UserDatagramProtocol)也是TCP/IP的傳輸層協(xié)議,它是無連接的,不可靠的傳輸服務。當接收數(shù)據(jù)時它不向發(fā)送方提供確認信息,它不提供輸入包的順序,如果出現(xiàn)丟失包或重份包的情況,也不會向發(fā)送方發(fā)出差錯報文。UDP的主要作用是分配和管理端口編號,以正確無誤的識別運行在網(wǎng)絡站點上的個別應用程序。由于它執(zhí)行功能時具有較低的開銷,因而執(zhí)行速度比TCP快。它多半用于不需要可靠傳輸?shù)膽贸绦?,例如網(wǎng)絡視頻點播和視頻會議等。UDP頭信息TCP/IP協(xié)議分組服務控制數(shù)據(jù)的協(xié)議TCP以連接為基礎,即兩臺電腦必須先建立一個連接,然后才能傳輸數(shù)據(jù)。事實上,發(fā)送和接受的電腦必須一直互相通訊和聯(lián)系。UDP是一個無連接服務,數(shù)據(jù)可以直接發(fā)送而不必在兩臺電腦之間建立一個網(wǎng)絡連接。它和有連接的TCP相比,占用帶寬少,但是無法確認數(shù)據(jù)是否真正到達了客戶端,而客戶端收到的數(shù)據(jù)也不知道是否還是原來的發(fā)送順序。數(shù)據(jù)路由協(xié)議路由協(xié)議分析數(shù)據(jù)包的地址并且決定傳輸數(shù)據(jù)到目的電腦最佳路線。他們也可以把大的數(shù)據(jù)分成幾部分,并且在目的地再把他們組合起來。IP處理實際上傳輸數(shù)據(jù)。ICMP(網(wǎng)絡控制信息協(xié)議InternetControlMessageProtocol)處理IP的狀態(tài)信息,比如能影響路由決策的數(shù)據(jù)錯誤或改變。RIP(路由信息協(xié)議RoutingInformationProtocol)它是幾個決定信息傳輸?shù)淖罴崖酚陕肪€協(xié)議中的一個。OSPF(OpenShortestPathFirst)一個用來決定路由的協(xié)議。ARP(地址解析協(xié)議AddressResolutionProtocol)確定網(wǎng)絡上一臺電腦的數(shù)字地址。DNS(域名系統(tǒng)DomainNameSystem)從機器的名字確定一個機器的數(shù)字地址。RARP(反向地址解析協(xié)議ReverseAddressResolutionProtocol)確定網(wǎng)絡上一臺計算機的地址,和ARP正好相反。用戶服務BOOTP(啟動協(xié)議BootProtocol)由網(wǎng)絡服務器上取得啟動信息,然后將本地的網(wǎng)絡計算機啟動。FTP(文件傳輸協(xié)議FileTransferProtocol)通過國際互連網(wǎng)從一臺計算機上傳輸一個或多個文件到另外一臺計算機。TELNET(遠程登陸)允許一個遠程登陸,使用者可以從網(wǎng)絡上的一臺機器通過TELNET連線到另一臺機器,就像使用者直接在本地操作一樣。EGP(外部網(wǎng)關協(xié)議ExteriorGatewayProtocol)為外部網(wǎng)絡傳輸路由信息。GGP(網(wǎng)關到網(wǎng)關協(xié)議Gateway-to-GatewayProtocol)在網(wǎng)關和網(wǎng)關之間傳輸路由協(xié)議。IGP(內部網(wǎng)關協(xié)議InteriorGatewayProtocol)在內部網(wǎng)絡傳輸路由信息。4.其他協(xié)議(也為網(wǎng)絡提供了重要的服務)NFS(網(wǎng)絡文件系統(tǒng)NetworkFileSystem)允許將一臺機器的目錄被另一臺機器上的用戶安裝(Mount)到自己的機器上,就像是對本地文件系統(tǒng)進行操作一樣進行各式各樣的操作。NIS(網(wǎng)絡信息服務NetworkInformationService)對整個網(wǎng)絡用戶的用戶名、密碼進行統(tǒng)一管理,簡化在NIS服務下整個網(wǎng)絡登陸的用戶名/密碼檢查。RPC(遠程過程調用RemoteProcedureCall)通過它可以允許遠程的應用程序通過簡單的、有效的手段聯(lián)系本地的應用程序,反之也是。SMTP(簡單郵件傳輸協(xié)議SimpleMailTransferProtocol)一個專門為電子郵件在多臺機器中傳輸?shù)膮f(xié)議,平時發(fā)郵件的SMTP服務器提供的必然服務。SNMP(簡單網(wǎng)絡管理協(xié)議SimpleNetworkManagementProtocol)這是一項為超級用戶準備的服務,超級用戶可以通過它來進行簡單的網(wǎng)絡管理。端口TCP和UDP協(xié)議是以IP協(xié)議為基礎的傳輸,為了方便多種應用程序,區(qū)分不同應用程序的數(shù)據(jù)和狀態(tài),引入了端口的概念。端口是一個16位的整數(shù)類型值,通常稱這個值為端口號。如果是服務程序,則需要對某個端口進行綁定,這樣某個客戶端可以方位本主機上的此端口來與應用程序進行通信。由于IP地址只能對主機進行區(qū)分,而加上端口號就可以對區(qū)分此主機上的應用程序。實際上,IP地址和端口號的組合,可以確定在網(wǎng)絡上的一個程序通路,端口號實際上是操作系統(tǒng)標識應用程序的一種方法。主機字節(jié)序和網(wǎng)絡字節(jié)序在使用網(wǎng)絡進行程序設計中會碰到的一個問題是字節(jié)序的問題,這在基于單機或者同類型機器進行開發(fā)的過程中很少遇到。由于網(wǎng)絡的特點是將Internet上不同的網(wǎng)絡設備和主機進行連接和通信,這決定了使用網(wǎng)絡進行開發(fā)的程序的特點就是要兼容各種類型的設備,其中的數(shù)據(jù)在不同的設備上要有唯一的含義。字節(jié)序的問題是上述情況下的典型問題。字節(jié)序的含義字節(jié)序的問題是由于CPU對整數(shù)在內存中的存放方式造成的。字節(jié)多于一個字節(jié)的數(shù)據(jù)類型在內存中的存放順序叫主機字節(jié)序。最常見的字節(jié)序有兩種,小端字節(jié)序和大端字節(jié)序:小端字節(jié)序,即LittleEndian(簡稱LE),將數(shù)據(jù)的最低字節(jié)放在內存的起始位置。小端字節(jié)序的特點是內存地址較低的位存放數(shù)據(jù)的低位,內存地址高的位存放數(shù)據(jù)的高位,與思維的習慣。采用低字節(jié)序的CPU有x86架構的intel系列產品。大端字節(jié)序,即BigEndian(簡稱BE),將數(shù)據(jù)的高字節(jié)放在內存的起始位置。大端字節(jié)序的特點是內存中低字節(jié)位置存放數(shù)據(jù)的高位字節(jié),內存中的高位字節(jié)存放數(shù)據(jù)的較低字節(jié)數(shù)據(jù),與思維習慣不一致,但是與實際數(shù)據(jù)的表達方式是一致的。采用大端字節(jié)序的CPU有PowerPC的UNIX系統(tǒng)。網(wǎng)絡字節(jié)序的轉換網(wǎng)絡的字節(jié)序標準規(guī)定為大端字節(jié)序,不同平臺上會對主機字節(jié)序進行轉化,成為網(wǎng)絡字節(jié)序后再進行傳送,到主機后再轉化為主機字節(jié)序,數(shù)據(jù)的傳輸就不會產生傳輸造成的問題了。同一個數(shù)據(jù)在不同的平臺上可以使用網(wǎng)絡字節(jié)序的轉換函數(shù)來實現(xiàn)。什么是套接字(SOCKET)套接口是對網(wǎng)絡中不同主機上應用進程之間進行雙向通信的端點的抽象,一個套接口就是網(wǎng)絡上進程通信的一端,提供了應用層進程利用網(wǎng)絡協(xié)議棧交換數(shù)據(jù)的機制。Socket的功能Socket的英文原意就是“孔”或“插座”,將電話系統(tǒng)與面向連接的Socket機制相比,有著驚人相似的地方。以一個國家級的電話網(wǎng)為例。電話的通話雙方相當于相互通信的兩個進程;通話雙方所在的地區(qū)(享有一個全局唯一的區(qū)號)相當于一個網(wǎng)絡,區(qū)號是它的網(wǎng)絡地址;區(qū)內的一個單位的交換機相當于一臺主機,主機分配給每個用戶的局內號碼相當于Socket號.任何用戶在通話之前,首先要占有一部電話機,相當于申請一個Socket號;同時要知道對方的電話號碼,相當于對方有一個Socket。然后向對方撥號呼叫,相當于發(fā)出連接請求(假如對方不在同一區(qū)內,還要撥對方區(qū)號,相當于給出網(wǎng)絡地址)。對方假如在場并空閑(相當于通信的另一主機開機且可以接受連接請求),拿起電話話筒,雙方就可以正式通話,相當于連接成功。雙方通話的過程,是向電話機發(fā)出信號和從電話機接受信號的過程,相當于向Socket發(fā)送數(shù)據(jù)和從Socket接受數(shù)據(jù)。通話結束后,一方掛起電話機,相當于關閉Socket,撤消連接。套接字基礎從套接字所處的地位來講,套接字上聯(lián)應用進程,下聯(lián)網(wǎng)絡協(xié)議棧,是應用程序通過網(wǎng)絡協(xié)議棧進行通信的接口,是應用程序與網(wǎng)絡協(xié)議棧進行交互的接口。套接字基礎(續(xù))從實現(xiàn)的角度來講,非常復雜。套接字是一個復雜的軟件機構,包含了一定的數(shù)據(jù)結構,包含許多選項,由操作系統(tǒng)內核管理。從使用的角度來講,非常簡單。對于套接字的操作形成了一種網(wǎng)絡應用程序的編程接口(API)。操作套接字的編程接口函數(shù)稱作套接字編程接口,套接字是它的操作對象。總之,套接字是網(wǎng)絡通信的基石。常用的socket流式套接字:它提供基于TCP協(xié)議的雙向、可靠、有序且不重復的無記錄邊界的數(shù)據(jù)流。數(shù)據(jù)報套接字:它提供基于UDP協(xié)議的雙向數(shù)據(jù)流,但不一定可靠、有序和不重復。原始套接字:它提供網(wǎng)絡下層通信協(xié)議的直接訪問。一般用于開發(fā)新的網(wǎng)絡層協(xié)議,如新的IP協(xié)議等。UDPTCP三種表示套接字地址的結構(1).structsockaddr這個結構用來存儲套接字地址。數(shù)據(jù)定義:structsockaddr{unsignedshortsa_family;/*address族,AF_xxx*/charsa_data[14];/*14bytes的協(xié)議地址*/};sa_family一般來說,都是“AF_INET”。sa_data包含了一些遠程電腦的地址、端口和套接字的數(shù)目,它里面的數(shù)據(jù)是雜溶在一切的。三種表示套接字地址的結構(續(xù))(2).structsockaddr_in為了處理structsockaddr,程序員建立了另外一個相似的結構structsockaddr_in:structsockaddr_in(“in”

代表“Internet”)structsockaddr_in{shortintsin_family;/*Internet地址族*/unsignedshortintsin_port;/*端口號*/structin_addrsin_addr;/*Internet地址*/unsignedcharsin_zero[8];/*添0(和structsockaddr一樣大?。?/};這個結構提供了方便的手段來訪問socketaddress(structsockaddr)結構中的每一個元素。三種表示套接字地址的結構(續(xù))(3).structin_addr專門用來存儲IP地址,其定義如下:/*因特網(wǎng)地址(astructureforhistoricalreasons)*/structin_addr{unsignedlongs_addr;};如果你聲明了一個“

ina”

作為一個structsockaddr_in的結構,

那么“ina.sin_addr.s_addr”就是4個字節(jié)的IP地址(按網(wǎng)絡字節(jié)順序排放)。需要注意的是,即使你的系統(tǒng)仍然使用聯(lián)合而不是結構來表示structin_addr,你仍然可以用上面的方法得到4個字節(jié)的IP地址.三種表示套接字地址的結構(續(xù))(4).這些數(shù)據(jù)結構的一般用法:首先,定義一個Sockaddr_in的結構實例,并將它清零。比如:structsockaddr_inmyad;memset(&myad,0,sizeof(structsockaddr_in));然后,為這個結構賦值,比如:myad.sin_family=AF_INET;myad.sin_port=htons(8080);myad.sin_addr.s_addr=htonl(INADDR-ANY);第三步:在函數(shù)調用中使用時,將這個結構強制轉換為sockaddr類型。如:accept(listenfd,(sockaddr*)(&myad),&addrlen);本機字節(jié)順序和網(wǎng)絡字節(jié)順序在具體計算機中的多字節(jié)數(shù)據(jù)的存儲順序,稱為本機字節(jié)順序。多字節(jié)數(shù)據(jù)在網(wǎng)絡協(xié)議報頭中的存儲順序,稱為網(wǎng)絡字節(jié)順序。在網(wǎng)絡上面有著許多類型的機器,這些機器在表示數(shù)據(jù)的字節(jié)順序是不同的,比如i386芯片是低字節(jié)在內存地址的低端,高字節(jié)在高端,而alpha芯片卻相反.為了統(tǒng)一起來,在Linux下面,有專門的字節(jié)轉換函數(shù).unsignedlonginthtonl(unsignedlonginthostlong)

unsignedshortinthtons(unisgnedshortinthostshort)

unsignedlongintntohl(unsignedlongintnetlong)

unsignedshortintntohs(unsignedshortintnetshort)在這四個轉換函數(shù)中,h代表host,n代表network.s代表shortl代表long第一個函數(shù)的意義是將本機器上的long數(shù)據(jù)轉化為網(wǎng)絡上的long.其他幾個函數(shù)的意義也差不多.點分十進制的IP地址的轉換(1)inet_aton將strptr所指的字符串轉換成32位的網(wǎng)絡字節(jié)序二進制值。

#include<arpa/inet.h> intinet_aton(constchar*strptr,structin_addr*addrptr);(2)inet_addr功能同上,返回地址。int_addr_tinet_addr(constchar*strptr);(3)inet_ntoa將32位網(wǎng)絡字節(jié)序二進制地址轉換成點分十進制的串。char*inet_ntoa(stuctin_addrinaddr);域名服務在網(wǎng)絡上標志一臺機器可以用IP或者是用域名.那么我們怎么去進行轉換呢?

struct

hostent

*gethostbyname(const

char

*hostname)可以將機器名(如

)轉換為一個結構指針.在這個結構里面儲存了域名的信息

struct

hostent

*gethostbyaddr(const

char

*addr,int

len,int

type)可以將一個32位的IP地址(C0A80001)轉換為結構指針.

這兩個函數(shù)失敗時返回NULL

且設置h_errno錯誤變量,調用h_strerror()可以得到詳細的出錯信息

struct

hostent的定義:

struct

hostent{

char

*h_name;

/*

主機的正式名稱

*/

char

*h_aliases;

/*

主機的別名

*/

int

h_addrtype;

/*

主機的地址類型

AF_INET*/

int

h_length;

/*

主機的地址長度

對于IP4

是4字節(jié)32位*/

char

**h_addr_list;

/*

主機的IP地址列表

*/

}

#define

h_addr

h_addr_list[0]

/*

主機的第一個IP地址*/套接字的工作過程初等網(wǎng)絡函數(shù)1socket#include<sys/socket.h>intsocket(intdomain,inttype,intprotocol)socket為網(wǎng)絡通訊做基本的準備.成功時返回文件描述符,失敗時返回-1。通過errno可知道出錯的詳細情況.參數(shù)說明:domain:說明我們網(wǎng)絡程序所在的主機采用的通訊協(xié)族(AF_UNIX和AF_INET等)。AF_UNIX只能夠用于單一的Unix系統(tǒng)進程間通信,而AF_INET是針對Internet的,因而可以允許在遠程主機之間通信當我們

mansocket時發(fā)現(xiàn)

domain可選項是

PF_*而不是AF_*,因為glibc是posix的實現(xiàn)

所以用PF代替了AF,不過我們都可以使用的.type:我們網(wǎng)絡程序所采用的通訊協(xié)議。SOCK_STREAM表明我們用的是TCP協(xié)議,這樣會提供按順序的,可靠,雙向,面向連接的比特流.SOCK_DGRAM表明我們用的是UDP協(xié)議,這樣只會提供定長的,不可靠,無連接的通信。

SOCK_RAW原始套接字,用來直接訪問IP協(xié)議。protocol:由于我們指定了type,所以這個地方我們一般只要用0來代替就可以了。初等網(wǎng)絡函數(shù)(續(xù))2bindintbind(intsockfd,structsockaddr*my_addr,intaddrlen)bind將本地的端口同socket返回的文件描述符捆綁在一起.成功是返回0,失敗的情況和socket一樣。參數(shù)說明sockfd:是由socket調用返回的文件描述符.addrlen:是sockaddr結構的長度.my_addr:是一個指向sockaddr的指針.構造套接字地址舉例intlistenfd;structsockaddr_inserver_addr;listenfd=socket(AF_INET,SOCK_STREAM,0);bzero(&server_addr,sizeof(server_addr));server_addr.sin_family=AF_INET;server_addr.sin_port=htons(80);inet_pton(AF_INET,“44”,&server_addr.sin_addr);bind(listenfd,&server_addr,sizeof(structsockaddr_in))初等網(wǎng)絡函數(shù)(續(xù))3listenintlisten(intsockfd,intbacklog)listen函數(shù)將bind的文件描述符變?yōu)楸O(jiān)聽套接字。返回的情況和bind一樣。參數(shù)說明sockfd:是bind后的文件描述符.。backlog:設置請求排隊的最大長度.當有多個客戶端程序和服務端相連時,使用這個表示可以介紹的排隊長度。初等網(wǎng)絡函數(shù)(續(xù))4acceptintaccept(intsockfd,structsockaddr*addr,int*addrlen)accept成功時返回最后的服務器端的文件描述符,這個時候服務器端可以向該描述符寫信息了。失敗時返回-1。參數(shù)說明sockfd:是listen后的文件描述符.。addr,addrlen是用來給客戶端的程序填寫的,服務器端只要傳遞指針就可以了.。bind,listen和accept是服務器端用的函數(shù),accept調用時,服務器端的程序會一直阻塞到有一個

客戶程序發(fā)出了連接,

accept成功時返回最后的服務器端的文件描述符。初等網(wǎng)絡函數(shù)(續(xù))5connectintconnect(intsockfd,structsockaddr*serv_addr,intaddrlen)connect函數(shù)是客戶端用來同服務端連接的。成功時返回0,sockfd是同服務端通訊的文件描述符

。失敗時返回-1。參數(shù)說明sockfd:socket返回的文件描述符.serv_addr:儲存了服務器端的連接信息.其中sin_add是服務端的地址

addrlen:serv_addr的長度

完整的讀寫函數(shù)一旦我們建立了連接,我們的下一步就是進行通信了.在Linux下面把我們前面建立的通道

看成是文件描述符,這樣服務器端和客戶端進行通信時候,只要往文件描述符里面讀寫東西了.就象我們往文件讀寫一樣。1寫函數(shù)writessize_twrite(intfd,constvoid*buf,size_tnbytes)

write函數(shù)將buf中的nbytes字節(jié)內容寫入文件描述符fd.成功時返回寫的字節(jié)數(shù).失敗時返回-1.并設置errno變量.在網(wǎng)絡程序中,當我們向套接字文件描述符寫時有倆種可能.1)write的返回值大于0,表示寫了部分或者是全部的數(shù)據(jù).2)返回的值小于0,此時出現(xiàn)了錯誤.我們要根據(jù)錯誤類型來處理.如果錯誤為EINTR表示在寫的時候出現(xiàn)了中斷錯誤.如果為EPIPE表示網(wǎng)絡連接出現(xiàn)了問題(對方已經關閉了連接).寫實例intmy_write(intfd,void*buffer,intlength)

{intbytes_left;

intwritten_bytes;

char*ptr;

ptr=buffer;

bytes_left=length;

while(bytes_left>0){/*開始寫*/

written_bytes=write(fd,ptr,bytes_left);

if(written_bytes<=0){/*出錯了*/

if(errno==EINTR)/*中斷錯誤我們繼續(xù)寫*/

written_bytes=0;

else/*其他錯誤沒有辦法,只好撤退了*/

return(-1);

}

bytes_left-=written_bytes;

ptr+=written_bytes;/*從剩下的地方繼續(xù)寫*/

}

return(0);

}完整的讀寫函數(shù)(續(xù))2讀函數(shù)readssize_tread(intfd,void*buf,size_tnbyte)read函數(shù)是負責從fd中讀取內容.當讀成功時,read返回實際所讀的字節(jié)數(shù),如果返回的值是0表示已經讀到文件的結束了,小于0表示出現(xiàn)了錯誤.讀實例如果錯誤為EINTR說明讀是由中斷引起的,如果是ECONNREST表示網(wǎng)絡連接出了問題.和上面一樣,我們也寫一個自己的讀函數(shù).intmy_read(intfd,void*buffer,intlength)

{

intbytes_left;

intbytes_read;

char*ptr;

bytes_left=length;

while(bytes_left>0)

{

bytes_read=read(fd,ptr,bytes_read);

if(bytes_read<0)

{if(errno==EINTR)

bytes_read=0;

else

return(-1);

}

else

if(bytes_read==0)break;

bytes_left-=bytes_read;

ptr+=bytes_read;

}

return(length-bytes_left);

}完整的讀寫函數(shù)(續(xù))3數(shù)據(jù)的傳遞

有了上面的兩個函數(shù),我們就可以向客戶端或者是服務端傳遞數(shù)據(jù)了.比如我們要傳遞一個結構.可以使用如下方式

:/*客戶端向服務端寫*/

structmy_structmy_struct_client;

write(fd,(void*)&my_struct_client,sizeof(structmy_struct);/*服務端的讀*/

charbuffer[sizeof(structmy_struct)];

struct*my_struct_server;

read(fd,(void*)buffer,sizeof(structmy_struct));

my_struct_server=(structmy_struct*)buffer;在網(wǎng)絡上傳遞數(shù)據(jù)時我們一般都是把數(shù)據(jù)轉化為char類型的數(shù)據(jù)傳遞.接收的時候也是一樣的。注意的是我們沒有必要在網(wǎng)絡上傳遞指針(因為傳遞指針是沒有任何意義的,我們必須傳遞指針所指向的內容)。面向連接的傳輸層套接字舉例服務器端程序/*******服務器程序(server.c)********/

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<netdb.h>

#include<sys/types.h>

#include<netinet/in.h>

#include<sys/socket.h>

intmain(intargc,char*argv[])

{intsockfd,new_fd;

structsockaddr_inserver_addr;

structsockaddr_inclient_addr;

intsin_size,portnumber;

charhello[]="Hello!AreYouFine?\n";

if(argc!=2){

fprintf(stderr,"Usage:%sportnumber\a\n",argv[0]);

exit(1);

}

if((portnumber=atoi(argv[1]))<0){

fprintf(stderr,"Usage:%sportnumber\a\n",argv[0]);

exit(1);

}/*服務器端開始建立socket描述符*/if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)

{fprintf(stderr,"Socketerror:%s\n\a",strerror(errno));

exit(1);

}/*服務器端填充sockaddr結構*/

bzero(&server_addr,sizeof(structsockaddr_in));server_addr.sin_family=AF_INET;server_addr.sin_addr.s_addr=htonl(INADDR_ANY);server_addr.sin_port=htons(portnumber);/*捆綁sockfd描述符*/

if(bind(sockfd,(structsockaddr*)(&server_addr),sizeof(structsockaddr))==-1){fprintf(stderr,"Binderror:%s\n\a",strerror(errno));

exit(1);

}/*監(jiān)聽sockfd描述符*/

if(listen(sockfd,5)==-1)

{fprintf(stderr,"Listenerror:%s\n\a",strerror(errno));

exit(1);

}while(1){/*服務器阻塞,直到客戶程序建立連接*/

sin_size=sizeof(structsockaddr_in);

if((new_fd=accept(sockfd,(structsockaddr*)(&client_addr),&sin_size))==-1){

fprintf(stderr,"Accepterror:%s\n\a",strerror(errno));

exit(1);

}

fprintf(stderr,“Servergetconnectionfrom%s\n”,inet_ntoa(client_addr.sin_addr));

if(write(new_fd,hello,strlen(hello))==-1){

fprintf(stderr,“WriteError:%s\n”,strerror(errno));

exit(1);

}/*這個通訊已經結束*/

close(new_fd);/*循環(huán)下一個*/

}

close(sockfd);

exit(0);

}面向連接的傳輸層套接字舉例客戶端程序/*******客戶端程序client.c**********/

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<netdb.h>

#include<sys/types.h>

#include<netinet/in.h>

#include<sys/socket.h>

intmain(intargc,char*argv[])

{intsockfd;

charbuffer[1024];

structsockaddr_inserver_addr;

structhostent*host;

intportnumber,nbytes;

if(argc!=3){

fprintf(stderr,"Usage:%shostnameportnumber\a\n",argv[0]);

exit(1);

}

if((host=gethostbyname(argv[1]))==NULL)

{fprintf(stderr,"Gethostnameerror\n");

exit(1);

}

if((portnumber=atoi(argv[2]))<0)

{fprintf(stderr,"Usage:%shostnameportnumber\a\n",argv[0]);

exit(1);

}/*客戶程序開始建立sockfd描述符*/

if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){

fprintf(stderr,"SocketError:%s\a\n",strerror(errno));

exit(1);}/*客戶程序填充服務端的資料*/

bzero(&server_addr,sizeof(server_addr));

server_addr.sin_family=AF_INET;

server_addr.sin_port=htons(portnumber);

server_addr.sin_addr=*((structin_addr*)host->h_addr);/*客戶程序發(fā)起連接請求*/

if(connect(sockfd,(structsockaddr*)(&server_addr),sizeof(structsockaddr))==-1)

{fprintf(stderr,"ConnectError:%s\a\n",strerror(errno));

exit(1);}/*連接成功了*/

if((nbytes=read(sockfd,buffer,1024))==-1){

fprintf(stderr,"ReadError:%s\n",strerror(errno));

exit(1);

}

buffer[nbytes]='\0';

printf("Ihavereceived:%s\n",buffer);/*結束通訊*/

close(sockfd);

exit(0);

}無連接的套接字編程使用數(shù)據(jù)報套接字開發(fā)網(wǎng)絡應用程序,既可以采用客戶/服務器模式,也可以采用對等模式??蛻?服務器模式UDP通信機制在服務器端服務器先使用AF_INET協(xié)議族創(chuàng)建UDP數(shù)據(jù)報類型的套接字,該socket類型為SOCK_DGRAM;然后服務器調用bind函數(shù),給此UDP套接字綁定一個端口;調用recvfrom函數(shù)在指定的端口上等待客戶端發(fā)送來的UDP數(shù)據(jù)報。在客戶端先通過socket函數(shù)創(chuàng)建一個數(shù)據(jù)報套接字;然后由操作系統(tǒng)為這個套接字分配端口號;此后客戶端就可以使用sendto函數(shù)向一個指定的地址發(fā)送一個UDP套接字。在服務器端收到套接字后,從recvfrom中返回,在對數(shù)據(jù)進行處理之后,再用sendto函數(shù)處理的結果返回客戶端。UDP與TCP的比較UDP服務器通常是非連接的因而UDP服務器進程不需要象TCP服務器那樣在傾聽套接字上接收新建的連接;UDP只需要在綁定的端口上等待客戶機發(fā)送來的UDP數(shù)據(jù)報,并對其進行處理和響應。一個TCP服務器進程只有在完成了對某客戶機的服務后,才能為其他的客戶機提供服務;UDP客戶機并不獨占服務器,UDP服務器只是接收數(shù)據(jù)報,處理并返回結果。UDP的應用場合UDP支持廣播和多播。如果要使用廣播和多播,必須使用UDP套接字。UDP套接字沒有連接的建立和終止過程。UDP只需要兩個分組來交換一個請求和應答。UDP不適合海量數(shù)據(jù)的傳輸。無連接的傳輸層套接字

兩個常用的函數(shù)

#include<sys/socket.h>intrecvfrom(intsockfd,void*buf,intlen,unsignedintflags,structsockaddr*fromint*fromlen)intsendto(intsockfd,constvoid*msg,intlen,unsignedintflags,structsockaddr*tointtolen)sockfd,buf,len的意義和read,write一樣,分別表示套接字描述符,發(fā)送或接收的緩沖區(qū)及大小recvfrom負責從sockfd接收數(shù)據(jù),如果from不是NULL,那么在from里面存儲了信息來源的情況,如果對信息的來源不感興趣,可以將from和fromlen設置為NULL.sendto負責向to發(fā)送信息.此時在to里面存儲了收信息方的詳細資料.UDP實例一個實例程序編譯好后,運行時需指定雙方所使用的IP地址和端口號,第一個參數(shù)是對方的IP地址,然后是對方的端口號,之后是本端的IP地址和端口號。例如在test1的終端輸入:a.out004321685678同時在test2上輸入:a.out685678004321然后雙方都可以發(fā)送信息給對方了/*firststepinudpprogramming*/#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<stdio.h>#defineBUFLEN255intmain(intargc,char**argv){ structsockaddr_inpeeraddr,localaddr; intsockfd; charrecmsg[BUFLEN+1]; intsocklen,n; if(argc!=5){ printf("%s<destIPaddress><destport><sourceIPaddress><sourceport>\n",argv[0]); exit(0); }sockfd=socket(AF_INET,SOCK_DGRAM,0); if(sockfd<0){ fprintf(stderr,"socketcreatingerrorinudptalk.c!\n"); exit(1); } socklen=sizeof(structsockaddr_in); memset(&peeraddr,0,socklen); peeraddr.sin_family=AF_INET; peeraddr.sin_port=htons(atoi(argv[2])); if(inet_pton(AF_INET,argv[1],&peeraddr.sin_addr)<=0){ printf("WrongdestIPaddress!\n"); exit(0); }

memset(&localaddr,0,socklen); localaddr.sin_family=AF_INET; localaddr.sin_port=htons(atoi(argv[4]));

if(inet_pton(AF_INET,argv[3],&localaddr.sin_addr)<=0){ printf("WrongsourceIPaddress!\n"); exit(0); }

if(bind(sockfd,&localaddr,socklen)<0){ fprintf(stderr,"bindlocaladdresserrorinudptalk.c!\n"); exit(2); } if(fgets(recmsg,BUFLEN,stdin)==NULL)exit(0);

if(sendto(sockfd,recmsg,strlen(recmsg),0,&peeraddr,socklen)<0){ fprintf(stderr,"sendtoerrorinudptalk.c!\n"); perror(""); exit(3);

}for(;;){ n=recvfrom(sockfd,recmsg,BUFLEN,0,&peeraddr,&socklen); if(n<0){ fprintf(stderr,"recvfromerrorinudptalk.c\n"); perror(""); exit(4); } else{ recmsg[n]=0; printf("peer:%s",recmsg); }

if(fgets(recmsg,BUFLEN,stdin)==NULL) exit(0); if(sendto(sockfd,recmsg,strlen(recmsg),0,&peeraddr,socklen)<0){ fprintf(stderr,"sendtoerrorinudptalk.c!\n"); perror(""); exit(3); }}}對第一個UDP例程的思考思考:1.只有來自對方IP和端口號的數(shù)據(jù)報才予以處理,如何過濾掉其它數(shù)據(jù)報?2.雙方發(fā)送和接收交替進行,只要有一方發(fā)出數(shù)據(jù)報,對方阻塞的狀態(tài)就能消除。如果一個數(shù)據(jù)報丟失,通信雙方都在recvfrom中阻塞,永遠等待。超時機制的設置?對第一個UDP例程的思考解決第一個問題:接收數(shù)據(jù)后,加入對數(shù)據(jù)報地址的檢驗:

if(memcmp(recvaddr,perraddr,socklen)!=0)

continue;這樣就將來自其他地址的數(shù)據(jù)報拒之門外了。對第一個UDP例程的思考解決第二個問題:前面提到的sock選項SO_RCVTIMEO就可以完成阻塞超時的設置。在程序中加入:#include<sys/time.h>Structtimevalrecto;Inttolen=sizeof(structtimeval);…/*建立socket等操作*/rcvto.tv_sec=3;/*這兩句對rcvto定時為3秒0毫秒*/rccvto.tv_usec=0;Setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&rcvto,tolen);getsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&rcvto,&tolen);Printf(“receivetimeoutsettedis%ldsecond%ldmillisecond\n”,rcvto.tv_sec,rcvto.tv_usec);如果顯示Receivetimeoutsettedis3second0millisecond,那么就設置成功了;如果結果是Receivetimeoutsettedis0second0millisecond,就意味著超時時間為無窮大。設置了SO_RCVTIMEO,那么在發(fā)生超時后,recvfrom將返回,返回值為-1,同時errno被置為EWOULDBLOCK。高級套接字函數(shù)1、recv和sendrecv和send函數(shù)提供了和read和write差不多的功能。不過它們提供了第四個參數(shù)來控制讀寫操作。intrecv(intsockfd,void*buf,intlen,intflags)intsend(intsockfd,void*buf,intlen,intflags)前面的三個參數(shù)和read,write一樣,第四個參數(shù)可以是0或者是以下的組合:MSG_DONTROUTE不查找路由表MSG_OOB接受或者發(fā)送帶外數(shù)據(jù)MSG_PEEK查看數(shù)據(jù),并不從系統(tǒng)緩沖區(qū)移走數(shù)據(jù)MSG_WAITALL等待所有數(shù)據(jù)MSG_DONTROUTE:是send函數(shù)使用的標志。這個標志告訴IP協(xié)議,目的主機在本地網(wǎng)絡上面,沒有必要查找路由表。這個標志一般用網(wǎng)絡診斷和路由程序里面。

MSG_OOB:表示可以接收和發(fā)送帶外的數(shù)據(jù)。關于帶外數(shù)據(jù)我們以后會解釋的。MSG_PEEK:是recv函數(shù)的使用標志。表示只是從系統(tǒng)緩沖區(qū)中讀取內容,而不清楚系統(tǒng)緩沖區(qū)的內容。這樣下次讀的時候,仍然是一樣的內容。一般在有多個進程讀寫數(shù)據(jù)時可以使用這個標志。MSG_WAITALL:是recv函數(shù)的使用標志。表示等到所有的信息到達時才返回。使用這個標志的時候recv回一直阻塞,直到指定的條件滿足,或者是發(fā)生了錯誤。

1)當讀到了指定的字節(jié)時,函數(shù)正常返回.返回值等于len。

2)當讀到了文件的結尾時,函數(shù)正常返回,返回值小于len。3)當操作發(fā)生錯誤時,返回-1,且設置錯誤為相應的錯誤號(errno)。如果flags為0,則和read,write一樣的操作。高級套接字函數(shù)(續(xù))recvmsg和sendmsgrecvmsg和sendmsg可以實現(xiàn)前面所有的讀寫函數(shù)的功能。

intrecvmsg(intsockfd,structmsghdr*msg,intflags)

intsendmsg(intsockfd,structmsghdr*msg,intflags)structmsghdr{

void*msg_name;

intmsg_namelen;

structiovec*msg_iov;

intmsg_iovlen;

void*msg_control;

intmsg_controllen;

intmsg_flags;

}structiovec{

void*iov_base;/*緩沖區(qū)開始的地址*/

size_tiov_len;/*緩沖區(qū)的長度*/

}msg_name和msg_namelen當套接字是非面向連接時(UDP),它們存儲接收和發(fā)送方的地址信息。msg_name實際上是一個指向structsockaddr的指針,msg_name是結構的長度。當套接字是面向連接時,這兩個值應設為NULLmsg_iov和msg_iovlen指出接受和發(fā)送的緩沖區(qū)內容。msg_iov是一個結構指針,msg_iovlen指出這個結構數(shù)組的大小。msg_control和msg_controllen這兩個變量是用來接收和發(fā)送控制數(shù)據(jù)時的msg_flags指定接受和發(fā)送的操作選項。(和recv,send的選項一樣)高級套接字函數(shù)(續(xù))套接字的關閉關閉套接字有兩個函數(shù):

close

shutdown用close時和我們關閉文件一樣。shutdownintshutdown(intsockfd,inthowto)TCP連接是雙向的(是可讀寫的),當我們使用close時,會把讀寫通道都關閉,有時侯我們希望只關閉一個方向,這個時候我們可以使用shutdown。針對不同的howto,系統(tǒng)回采取不同的關閉方式。shutdownintshutdown(intsockfd,inthowto)TCP連接是雙向的(是可讀寫的),當我們使用close時,會把讀寫通道都關閉,有時侯我們希望只關閉一個方向,這個時候我們可以使用shutdown。針對不同的howto,系統(tǒng)回采取不同的關閉方式。系統(tǒng)為我們提供了獲取和修改套接字結構中一些屬性的函數(shù),通過修改這些屬性,我們可以調整套接字的性能,進而調整某些應用程序的性能。套接字選項1getsockopt和setsockoptintgetsockopt(intsockfd,intlevel,intoptname,void*optval,socklen_t*optlen)

intsetsockopt(intsockfd,intlevel,intoptname,constvoid*optval,socklen_t*optlen)

level指定控制套接字的層次.可以取三種值:

1)SOL_SOCKET:通用套接字選項.

2)IPPROTO_IP:IP選項.

3)IPPROTO_TCP:TCP選項.optname指定控制的方式(選項的名稱)。optval獲得或者是設置套接字選項.根據(jù)選項名稱的數(shù)據(jù)類型進行轉換。選項名稱說明數(shù)據(jù)類型=======================================================

SOL_SOCKET

----------------------------------------------------------------------------------------------

SO_BROADCAST允許發(fā)送廣播數(shù)據(jù)int

SO_DEBUG允許調試int

SO_DONTROUTE不查找路由int

SO_ERROR獲得套接字錯誤int

SO_KEEPALIVE保持連接int

SO_LINGER延遲關閉連接structlinger

SO_OOBINLINE帶外數(shù)據(jù)放入正常數(shù)據(jù)流int

SO_RCVBUF接收緩沖區(qū)大小int

SO_SNDBUF發(fā)送緩沖區(qū)大小int

SO_RCVLOWAT接收緩沖區(qū)下限int

SO_SNDLOWAT發(fā)送緩沖區(qū)下限int

SO_RCVTIMEO接收超時structtimeval

SO_SNDTIMEO發(fā)送超時structtimeval

SO_REUSERADDR允許重用本地地址和端口int

SO_TYPE獲得套接字類型int

SO_BSDCOMPAT與BSD系統(tǒng)兼容int

==========================================================================================================IPPROTO_IP--------------------------------------------------------------------------IP_HDRINCL在數(shù)據(jù)包中包含IP首部intIP_OPTINOSIP首部選項intIP_TOS服務類型

IP_TTL生存時間int===================================================IPPRO_TCP--------------------------------------------------------------------------TCP_MAXSEGTCP最大數(shù)據(jù)段的大小intTCP_NODELAY不使用Nagle算法int===================================================套接字選項(續(xù))2ioctlioctl可以控制所有的文件描述符的情況,這里介紹一下控制套接字的選項.定義格式:#include<unistd.h>intioctl(intfd,longcmd,unsignedlong*argp);參數(shù):fd是需要操作的套接字描述符;cmd是屬性類別;argp是屬性的參數(shù)。詳細的選項請用manioctl_list查看.===========================================iiioctl的控制選項--------------------------------------------------------------------------SIOCATMARK是否到達帶外標記intFIOASYNC異步輸入/輸出標志intFIONREAD緩沖區(qū)可讀的字節(jié)數(shù)int===========================================套接字選項(續(xù))3、fcntl定義格式:#include<fcntl.h>Intfcntl(intfd,intcmd,longarg);參數(shù):fd是需要操作的套接字描述符;cmd是屬性類別,cmd可以是如下值:F_SETFL:arg為O_NONBLOCK時進入非阻塞模式,為0是進入阻塞模式;F_GETFL:獲得屬性;F_SETOWN:用來設置套接字屬主,用來接收數(shù)據(jù)SIGIO和SIGURG;F_GETOWN:用來獲得上面設置的套接字屬主信息。系統(tǒng)IO與服務器模型

在Linux/UNIX下,有主要有4種I/O模型:阻塞I/O:最常用、最簡單、效率最低非

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論