版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
計(jì)算機(jī)網(wǎng)絡(luò)20世紀(jì)60年代出現(xiàn),經(jīng)歷了20世紀(jì)70年代、80年代和90年代的發(fā)展,進(jìn)入21世紀(jì)后,計(jì)算機(jī)網(wǎng)絡(luò)已經(jīng)成為信息社會(huì)的基礎(chǔ)設(shè)施,深入到人類社會(huì)的方方面面,與人們的工作、學(xué)習(xí)和生活息息相關(guān)。計(jì)算機(jī)網(wǎng)絡(luò)是通過(guò)傳輸介質(zhì)、通信設(shè)施和網(wǎng)絡(luò)通信協(xié)議,把分散在不同地點(diǎn)的計(jì)算機(jī)設(shè)備互連起來(lái)的,實(shí)現(xiàn)資源共享和數(shù)據(jù)傳輸?shù)南到y(tǒng)。網(wǎng)絡(luò)編程,就是在一定的協(xié)議下,實(shí)現(xiàn)多臺(tái)設(shè)備(計(jì)算機(jī))之間通信的程序。Java語(yǔ)言對(duì)網(wǎng)絡(luò)編程提供了良好的支持。通過(guò)其提供的接口我們可以很方便地進(jìn)行網(wǎng)絡(luò)編程。本模塊通過(guò)“文件上傳”案例介紹網(wǎng)絡(luò)編程的過(guò)程。模塊介紹思維導(dǎo)圖教學(xué)大綱能力目標(biāo)◎能夠編寫TCP協(xié)議下的數(shù)據(jù)傳輸程序知識(shí)目標(biāo)◎了解網(wǎng)絡(luò)通信協(xié)議◎理解TCP和UDP的協(xié)議特點(diǎn)◎掌握IP地址和端口號(hào)的作用◎熟悉TCP協(xié)議下兩個(gè)JDK常用類的使用◎掌握TCP通信的基本實(shí)現(xiàn)學(xué)習(xí)重點(diǎn)◎TCP通信的應(yīng)用◎TCP通信程序的實(shí)現(xiàn)學(xué)習(xí)難點(diǎn)◎TCP通信“三次握手”過(guò)程◎TCP通信程序的實(shí)現(xiàn)任務(wù)9.1文件上傳程序設(shè)計(jì)任務(wù)目標(biāo)了解網(wǎng)絡(luò)通信協(xié)議;理解TCP和UDP的協(xié)議特點(diǎn);理解TCP協(xié)議下文件上傳過(guò)程;實(shí)現(xiàn)TCP協(xié)議下文件傳輸程序。任務(wù)9.1文件上傳程序設(shè)計(jì)網(wǎng)盤是由互聯(lián)網(wǎng)公司推出的在線存儲(chǔ)服務(wù),它已經(jīng)融入了人們的日常生活中。人們?cè)诶镁W(wǎng)盤上傳資料時(shí),實(shí)際上是一個(gè)使用客戶端讀取本地文件,把文件上傳到服務(wù)器,服務(wù)器再把上傳的文件保存到服務(wù)器硬盤上的過(guò)程。本節(jié)要求了解網(wǎng)絡(luò)通信的基本原理,編寫使用TCP網(wǎng)絡(luò)通信程序,實(shí)現(xiàn)將客戶端的“c:\\1.jpg”文件上傳至服務(wù)器端的硬盤“d:\\upload\\”中。任務(wù)描述任務(wù)9.1文件上傳程序設(shè)計(jì)9.1.1網(wǎng)絡(luò)通信協(xié)議9.1.1.1網(wǎng)絡(luò)通信協(xié)議概述
要實(shí)現(xiàn)多臺(tái)計(jì)算機(jī)之間的通信,需要遵守一定的規(guī)則,如同人與人之間相互交流是需要遵循一定的規(guī)矩一樣,計(jì)算機(jī)之間能夠進(jìn)行相互通信是因?yàn)樗鼈兌脊餐袷匾欢ǖ囊?guī)則,即網(wǎng)絡(luò)通信協(xié)議。協(xié)議(protocol)是定義計(jì)算機(jī)如何通信的一組明確的規(guī)則,包括地址格式、數(shù)據(jù)如何分包等。針對(duì)網(wǎng)絡(luò)通信的不同方面,定義有很多不同的協(xié)議,如HTTP、FTP、TCP協(xié)議等。知識(shí)準(zhǔn)備任務(wù)9.1文件上傳程序設(shè)計(jì)
通過(guò)網(wǎng)絡(luò)發(fā)送數(shù)據(jù)是一項(xiàng)復(fù)雜的操作,必須仔細(xì)地協(xié)調(diào)網(wǎng)絡(luò)的物理特性以及所發(fā)送數(shù)據(jù)的路徑特征。為了對(duì)應(yīng)用程序開發(fā)人員和最終用戶隱藏這種復(fù)雜性,網(wǎng)絡(luò)通信的不同方面被分解為多個(gè)層。每一層表示為物理硬件(即線纜和電流)與所傳輸信息之間的不同抽象層次。在理論上,每一層只與緊挨其上和其下的層對(duì)話。將網(wǎng)絡(luò)分層,這樣就可以修改甚至替換某一層的軟件,只要層與層之間的接口保持不變,就不會(huì)影響到其他層。
TCP/IP協(xié)議(TransmissionControlProtocol/InternetProtocol,傳輸控制協(xié)議/因特網(wǎng)互聯(lián)協(xié)議)是Internet最基本、最廣泛的協(xié)議。它定義了計(jì)算機(jī)如何連入因特網(wǎng),以及數(shù)據(jù)如何在它們之間傳輸?shù)臉?biāo)準(zhǔn)。它的內(nèi)部包含一系列的用于處理數(shù)據(jù)通信的協(xié)議,并采用了4層的分層模型,每一層都呼叫它的下一層所提供的協(xié)議來(lái)完成自己的需求,如圖9-1所示。任務(wù)9.1文件上傳程序設(shè)計(jì)
圖9-1中,TCP/IP協(xié)議中的四層分別是應(yīng)用層、傳輸層、網(wǎng)絡(luò)層和鏈路層,每層分別負(fù)責(zé)不同的通信功能。鏈路層是用于定義物理傳輸通道,通常是對(duì)某些網(wǎng)絡(luò)連接設(shè)備的驅(qū)動(dòng)協(xié)議,例如針對(duì)光纖、網(wǎng)絡(luò)提供的驅(qū)動(dòng)。網(wǎng)絡(luò)層是整個(gè)TCP/IP的核心,它主要用于將傳輸?shù)臄?shù)據(jù)進(jìn)行分組,將分組數(shù)據(jù)發(fā)送到目標(biāo)計(jì)算機(jī)或者網(wǎng)絡(luò)。傳輸層主要使網(wǎng)絡(luò)程序進(jìn)行通信,在進(jìn)行網(wǎng)絡(luò)通信時(shí),可以采用TCP協(xié)議,也可以采用UDP協(xié)議。應(yīng)用層主要負(fù)責(zé)應(yīng)用程序的協(xié)議,例如HTTP協(xié)議、FTP協(xié)議等。圖9-1TCP/IP網(wǎng)絡(luò)模型在JDK中提供了包用于專注網(wǎng)絡(luò)程序開發(fā),包中提供了兩種常見的網(wǎng)絡(luò)協(xié)議支持,分別是TCP和UDP。任務(wù)9.1文件上傳程序設(shè)計(jì)9.1.1.2TCP協(xié)議
TCP(TransmissionControlProtocol)全稱是傳輸控制協(xié)議,是一種面向連接的通信協(xié)議,即傳輸數(shù)據(jù)之前,在發(fā)送端和接收端建立邏輯連接,然后再傳輸數(shù)據(jù),它提供了兩臺(tái)計(jì)算機(jī)之間可靠無(wú)差錯(cuò)的數(shù)據(jù)傳輸。在TCP協(xié)議中,在發(fā)送數(shù)據(jù)的準(zhǔn)備階段,客戶端與服務(wù)器之間的三次交互,以保證連接的可靠,這三次交互被稱為“三次握手”?!叭挝帐帧钡木唧w過(guò)程如下:·第一次握手,客戶端向服務(wù)器端發(fā)出連接請(qǐng)求,等待服務(wù)器確認(rèn)?!さ诙挝帐?,服務(wù)器端向客戶端回送一個(gè)響應(yīng),通知客戶端收到了連接請(qǐng)求?!さ谌挝帐?,客戶端再次向服務(wù)器端發(fā)送確認(rèn)信息,確認(rèn)連接。
整個(gè)交互過(guò)程如圖9-2所示。任務(wù)9.1文件上傳程序設(shè)計(jì)
完成三次握手,連接建立之后,客戶端和服務(wù)器就可以進(jìn)行數(shù)據(jù)傳輸了。由于面向連接的特性,TCP協(xié)議可以保證傳輸數(shù)據(jù)的安全,所以應(yīng)用十分廣泛,例如下載文件、瀏覽網(wǎng)頁(yè)等。圖9-2TCP協(xié)議通信——“三次握手”任務(wù)9.1文件上傳程序設(shè)計(jì)9.1.1.3UDP協(xié)議
UDP(UserDatagramProtocol)全稱是用戶數(shù)據(jù)報(bào)協(xié)議,是一種面向無(wú)連接的協(xié)議,即在使用UDP協(xié)議傳輸數(shù)據(jù)時(shí),數(shù)據(jù)的發(fā)送端和接收端不需要建立邏輯連接。簡(jiǎn)單的說(shuō),就是當(dāng)一臺(tái)計(jì)算機(jī)向另一臺(tái)計(jì)算機(jī)發(fā)送數(shù)據(jù)時(shí),發(fā)送端不會(huì)確認(rèn)接收端是否存在,可以直接將數(shù)據(jù)、數(shù)據(jù)源和目的地都封裝在數(shù)據(jù)包中發(fā)送出去。同樣接收端在收到數(shù)據(jù)時(shí),也不會(huì)向發(fā)送端反饋是否收到數(shù)據(jù)。和TCP不同的是,UDP不需要在發(fā)送數(shù)據(jù)前進(jìn)行三次握手建立連接,只要想發(fā)數(shù)據(jù)就可以發(fā)送數(shù)據(jù),只是數(shù)據(jù)的搬運(yùn)工,不會(huì)對(duì)數(shù)據(jù)報(bào)文進(jìn)行任何拆分和拼接操作。任務(wù)9.1文件上傳程序設(shè)計(jì)
正由于無(wú)連接性,UDP不能保證數(shù)據(jù)的完整性,具有不可靠性。另外,UDP沒(méi)有擁塞控制,一直會(huì)以恒定的速度發(fā)送數(shù)據(jù),即使網(wǎng)絡(luò)條件不好,也不會(huì)對(duì)發(fā)送速率進(jìn)行調(diào)整,那么在網(wǎng)絡(luò)條件不好的情況下可能產(chǎn)生丟包,因此在傳輸重要數(shù)據(jù)時(shí)不建議使用UDP協(xié)議,以免丟失重要數(shù)據(jù)。但是其優(yōu)點(diǎn)也很明顯,使用UDP協(xié)議消耗的資源小,每個(gè)數(shù)據(jù)包的大小限制在64k以內(nèi),通信效率高,所以通常會(huì)用于音頻、視頻這些實(shí)時(shí)性要求高的場(chǎng)景。例如視頻會(huì)議、群聊天等這些即使偶爾丟失一兩個(gè)數(shù)據(jù)包,也不會(huì)對(duì)接收結(jié)果產(chǎn)生太大影響的應(yīng)用。任務(wù)9.1文件上傳程序設(shè)計(jì)TCPUDP是否連接面向連接無(wú)連接是否可靠可靠傳輸,使用流量控制和塞控制不可靠傳輸,不使用流量控制和塞控制對(duì)象數(shù)量只支持一對(duì)一通信支持一對(duì)一,一對(duì)多,多對(duì)一和多對(duì)多交互通信適用場(chǎng)景適用于要求可靠傳輸?shù)膽?yīng)用,如文件傳輸適用于實(shí)時(shí)應(yīng)用,如IP電話、視頻會(huì)議、直播等表9-1TCP和UDP的對(duì)比任務(wù)9.1文件上傳程序設(shè)計(jì)9.1.1.4IP地址和端口號(hào)
在Java中,網(wǎng)絡(luò)編程需要掌握三個(gè)要素,除了協(xié)議以外,還有IP地址和端口號(hào)。網(wǎng)絡(luò)的通信,本質(zhì)上是兩個(gè)進(jìn)程(應(yīng)用程序)的通信。“協(xié)議+IP地址+端口號(hào)”三元組合可以標(biāo)識(shí)網(wǎng)絡(luò)中的進(jìn)程,那么進(jìn)程就可以利用這個(gè)標(biāo)識(shí)與其它進(jìn)程進(jìn)行交互。
IP地址是InternetProtocolAddress的簡(jiǎn)寫,指的是互聯(lián)網(wǎng)協(xié)議地址,用來(lái)給一個(gè)網(wǎng)絡(luò)中的計(jì)算機(jī)設(shè)備做唯一的編號(hào)。假如我們把“個(gè)人電腦”比作“一臺(tái)電話”的話,那么“IP地址”就相當(dāng)于“電話號(hào)碼”。IP地址分為IPv4和IPv6。IPv4是一個(gè)32位的二進(jìn)制數(shù),通常被分為4個(gè)字節(jié),表示成a.b.c.d的形式,例如00。其中a、b、c、d都是0~255之間的十進(jìn)制整數(shù),那么最多可以表示42億個(gè)。由于互聯(lián)網(wǎng)的蓬勃發(fā)展,互聯(lián)網(wǎng)對(duì)IP地址的需求量愈來(lái)愈大,但是網(wǎng)絡(luò)地址資源有限,使得IP的分配越發(fā)緊張。為了擴(kuò)大地址空間,擬通過(guò)IPv6重新定義地址空間,采用128位地址長(zhǎng)度,每16個(gè)字節(jié)一組,分成8組十六進(jìn)制數(shù),表示成ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,以解決了網(wǎng)絡(luò)地址資源數(shù)量不夠的問(wèn)題。任務(wù)9.1文件上傳程序設(shè)計(jì)
如果說(shuō)IP地址可以唯一標(biāo)識(shí)網(wǎng)絡(luò)中的設(shè)備,那么端口號(hào)就可以唯一標(biāo)識(shí)設(shè)備中的進(jìn)程(應(yīng)用程序)了。端口號(hào)是一個(gè)邏輯端口,它用兩個(gè)字節(jié)表示的整數(shù),取值范圍是0~65535。每當(dāng)打開一個(gè)網(wǎng)絡(luò)軟件,操作系統(tǒng)都會(huì)為網(wǎng)絡(luò)軟件分配一個(gè)隨機(jī)的或者是系統(tǒng)要指定的端口號(hào)。0~1023之間的端口號(hào)都已經(jīng)被用于一些知名的網(wǎng)絡(luò)服務(wù)和應(yīng)用,普通的應(yīng)用程序需要使用1024以上的端口號(hào)。如果端口號(hào)被另外一個(gè)服務(wù)或應(yīng)用所占用,會(huì)導(dǎo)致當(dāng)前程序啟動(dòng)失敗。任務(wù)9.1文件上傳程序設(shè)計(jì)9.1.2TCP通信程序設(shè)計(jì)
TCP通信能實(shí)現(xiàn)兩臺(tái)計(jì)算機(jī)之間的數(shù)據(jù)交互,TCP連接時(shí)必須明確服務(wù)器端(Server)與客戶端(Client)。兩端通信時(shí),服務(wù)端程序需要事先啟動(dòng),它不可以主動(dòng)連接客戶端,只能等待客戶端的連接。然后客戶端主動(dòng)連接服務(wù)器端,連接成功才能通信。連接過(guò)程中包含一個(gè)IO字節(jié)流對(duì)象,客戶端和服務(wù)端就用這個(gè)IO流對(duì)象進(jìn)行通信。需要注意的是,當(dāng)多個(gè)客戶端同時(shí)和服務(wù)端交互時(shí),服務(wù)端必須明確和哪個(gè)客戶端進(jìn)行交互,并且需要使用多個(gè)IO流對(duì)象。
Java在net包中提供了兩個(gè)類用于實(shí)現(xiàn)TCP通信程序。任務(wù)9.1文件上傳程序設(shè)計(jì)
(1)表示服務(wù)端的類:.ServerSocket類表示。該類實(shí)現(xiàn)了服務(wù)器套接字,套接字指的是兩臺(tái)設(shè)備之間通訊的端點(diǎn)。創(chuàng)建ServerSocket對(duì)象,相當(dāng)于開啟一個(gè)服務(wù),該對(duì)象等待通過(guò)網(wǎng)絡(luò)的請(qǐng)求,也就是等待客戶端的連接。ServerSocket類的主要構(gòu)造方法和成員方法如表9-2所示。表9-2ServerSocket類的主要構(gòu)造方法和成員方法方法聲明功能描述publicServerSocket(intport)使用該構(gòu)造方法在創(chuàng)建ServerSocket對(duì)象時(shí),就可以將其綁定到一個(gè)指定的端口號(hào)上,參數(shù)port就是端口號(hào)。publicSocketaccept()偵聽并接受連接,返回一個(gè)新的Socket對(duì)象,用于和客戶端實(shí)現(xiàn)通信。該方法會(huì)一直阻塞直到建立連接。任務(wù)9.1文件上傳程序設(shè)計(jì)
(2)表示客戶端的類:.Socket類表示。該類實(shí)現(xiàn)客戶端套接字。創(chuàng)建Socket對(duì)象,向服務(wù)端發(fā)出連接請(qǐng)求,服務(wù)端響應(yīng)請(qǐng)求,兩者建立連接開始通信。Socket類的主要構(gòu)造方法和成員方法如表9-3所示。表9-3Socket類的主要構(gòu)造方法和成員方法方法聲明功能描述publicSocket(Stringhost,intport)創(chuàng)建套接字對(duì)象并將其連接到指定主機(jī)上的指定端口號(hào)。如果指定的host是null,則相當(dāng)于指定地址為回送地址。publicInputStreamgetInputStream()返回此套接字的輸入流。關(guān)閉生成的InputStream也將關(guān)閉相關(guān)的Socket。publicOutputStreamgetOutputStream()返回此套接字的輸出流。關(guān)閉生成的OutputStream也將關(guān)閉相關(guān)的Socket。publicvoidclose()關(guān)閉此套接字。一旦一個(gè)socket被關(guān)閉,它不可再使用。關(guān)閉此socket也將關(guān)閉相關(guān)的InputStream和OutputStream。publicvoidshutdownOutput()禁用此套接字的輸出流。任何先前寫出的數(shù)據(jù)將被發(fā)送,隨后終止輸出流。任務(wù)9.1文件上傳程序設(shè)計(jì)
從通信邏輯上來(lái)看,TCP網(wǎng)絡(luò)通信的兩端程序編寫主要步驟如下:①服務(wù)端啟動(dòng),創(chuàng)建ServerSocket對(duì)象,指定端口號(hào),等待連接。②客戶端啟動(dòng),創(chuàng)建Socket對(duì)象,構(gòu)造方法綁定服務(wù)器的IP地址和端口號(hào),請(qǐng)求連接。③服務(wù)端接收連接,調(diào)用ServerSocket對(duì)象中的accept()方法,返回請(qǐng)求的客戶端對(duì)象Socket。④客戶端使用Socket對(duì)象的getOutputStream()方法獲取網(wǎng)絡(luò)字節(jié)輸出流OutputStream對(duì)象,然后使用網(wǎng)絡(luò)字節(jié)輸出流OutputStream對(duì)象中的write()方法,給服務(wù)器發(fā)送數(shù)據(jù)。⑤服務(wù)端使用Scoket對(duì)象的getInputStream()方法獲取網(wǎng)絡(luò)字節(jié)輸入流InputStream對(duì)象,然后使用網(wǎng)絡(luò)字節(jié)輸入流InputStream對(duì)象中的read()方法,讀取客戶端發(fā)送的數(shù)據(jù)。到此,客戶端向服務(wù)端發(fā)送數(shù)據(jù)成功,接下來(lái)服務(wù)端向客戶端回寫數(shù)據(jù)。任務(wù)9.1文件上傳程序設(shè)計(jì)⑥服務(wù)端使用Socket對(duì)象中的getOutputStream()方法獲取網(wǎng)絡(luò)字節(jié)輸出流OutputStream對(duì)象,然后使用網(wǎng)絡(luò)字節(jié)輸出流OutputStream對(duì)象中的write()方法,給客戶端回寫數(shù)據(jù)。⑦客戶端使用Scoket對(duì)象的getInputStream()方法獲取網(wǎng)絡(luò)字節(jié)輸入流InputStream對(duì)象,然后使用網(wǎng)絡(luò)字節(jié)輸入流InputStream對(duì)象中的read()方法,讀取服務(wù)端回寫的數(shù)據(jù)。⑧客戶端釋放Socket資源,斷開連接。⑨服務(wù)端釋放Socket和ServerSocket資源。圖9-3TCP網(wǎng)絡(luò)通信邏輯圖任務(wù)9.1文件上傳程序設(shè)計(jì)
需要注意的是,客戶端和服務(wù)端交互時(shí)必須使用Socket中提供的網(wǎng)絡(luò)流,不允許使用自己創(chuàng)建的流對(duì)象。當(dāng)創(chuàng)建客戶端對(duì)象Socket的時(shí)候,就會(huì)主動(dòng)請(qǐng)求服務(wù)器,要求經(jīng)過(guò)3次握手建立連接通路。如果此時(shí)服務(wù)器沒(méi)有啟動(dòng),則會(huì)拋出連接被拒絕的異常(ConnectException:Connectionrefused:connect);如果服務(wù)器已經(jīng)啟動(dòng),那么交互就可以順利進(jìn)行了。任務(wù)9.1文件上傳程序設(shè)計(jì)
接下來(lái)用一個(gè)案例來(lái)演示TCP網(wǎng)絡(luò)通信程序的編寫過(guò)程,服務(wù)器端程序如例9-1所示,客戶端程序如例9-2所示。importjava.io.*;import.*;publicclassExample9_1{publicstaticvoidmain(String[]args)throwsIOException{//1.創(chuàng)建ServerSocket對(duì)象,綁定端口,開始等待連接。ServerSocketserver=newServerSocket(8888);//2.接收連接,使用accept方法,返回socket對(duì)象。Socketsocket=server.accept();//3.通過(guò)socket獲取輸入流InputStreamis=socket.getInputStream();//4.一次性讀取數(shù)據(jù)//4.1創(chuàng)建字節(jié)數(shù)組byte[]bytes=newbyte[1024];//4.2將數(shù)據(jù)讀取到字節(jié)數(shù)組中intlen=is.read(bytes);//4.3解析數(shù)組,打印字符串信息System.out.println(newString(bytes,0,len));
例9-1Example9_1.java任務(wù)9.1文件上傳程序設(shè)計(jì)/*----------服務(wù)端回寫數(shù)據(jù)-----------*///5.通過(guò)socket獲取輸出流OutputStreamos=socket.getOutputStream();//6.回寫數(shù)據(jù)os.write("收到,謝謝".getBytes());//7.關(guān)閉資源socket.close();server.close();}}例9-1Example9_1.java任務(wù)9.1文件上傳程序設(shè)計(jì)importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;import.Socket;publicclassExample9_2{publicstaticvoidmain(String[]args)throwsIOException{//1.創(chuàng)建Socket(ip,port),確定連接到哪里。Socketsocket=newSocket("",8888);//2.通過(guò)Scoket,獲取輸出流對(duì)象OutputStreamos=socket.getOutputStream();//3.寫出數(shù)據(jù)os.write("你好,服務(wù)器".getBytes());/*----------客戶端解析服務(wù)端的回寫數(shù)據(jù)-----------*///4.通過(guò)Scoket,獲取輸入流對(duì)象。InputStreamis=socket.getInputStream();//5.讀取數(shù)據(jù)byte[]bytes=newbyte[1024];intlen=is.read(bytes);System.out.println(newString(bytes,0,len));//6.關(guān)閉資源socket.close();}}例9-2Example9_2.java任務(wù)9.1文件上傳程序設(shè)計(jì)
完成程序編寫后,首先運(yùn)行服務(wù)器端程序Example9_1.java,然后運(yùn)行客戶端程序Example9_2.java。從這兩個(gè)例子的運(yùn)行結(jié)果中看到,服務(wù)器端結(jié)果顯示“你好,服務(wù)器”,表示收到來(lái)自客戶端的數(shù)據(jù)信息,如圖9-4所示??蛻舳私Y(jié)果顯示“收到,謝謝”,表示收到來(lái)自服務(wù)器端的回寫數(shù)據(jù),如圖9-5所示。圖9-4Example9_1運(yùn)行結(jié)果(服務(wù)器端)圖9-5Example9_2運(yùn)行結(jié)果(客戶端)任務(wù)實(shí)施1.任務(wù)分析文件上傳的原理,本質(zhì)上就是文件的復(fù)制??蛻舳撕头?wù)器和本地硬盤進(jìn)行讀寫,需要使用自己創(chuàng)建的字節(jié)流對(duì)象,即本地IO流。客戶端和服務(wù)器之間進(jìn)行讀寫,必須使用Socket中提供的字節(jié)流對(duì)象,即網(wǎng)絡(luò)IO流。除了明確字節(jié)流對(duì)象,還要明確數(shù)據(jù)源和目的地??蛻舳说臄?shù)據(jù)源是客戶端文件所在地址,目的地是服務(wù)器;服務(wù)端的數(shù)據(jù)源是客戶端上傳的文件,目的地是服務(wù)器的硬盤,即要存放文件的地址。文件上傳的過(guò)程如圖9-6所示。任務(wù)9.1文件上傳程序設(shè)計(jì)(1)客戶端使用本地字節(jié)輸入流,讀取要上傳的文件;(2)客戶端使用網(wǎng)絡(luò)字節(jié)輸出流,把讀取到的文件上傳到服務(wù)器;(3)服務(wù)器使用網(wǎng)絡(luò)字節(jié)輸入流,讀取客戶端上傳的文件;(4)服務(wù)器使用本地字節(jié)輸出流,把讀取到的文件,保存到服務(wù)器的硬盤上;(5)服務(wù)器使用網(wǎng)絡(luò)字節(jié)輸出流,給客戶端回寫一個(gè)“上傳成功”的提示;(6)客戶端使用網(wǎng)絡(luò)字節(jié)輸入流,讀取服務(wù)器回寫的數(shù)據(jù);(7)釋放資源。任務(wù)9.1文件上傳程序設(shè)計(jì)圖9-6文件上傳過(guò)程2.具體實(shí)現(xiàn)步驟(1)客戶端實(shí)現(xiàn)步驟:①創(chuàng)建一個(gè)本地字節(jié)輸入流FileInputStream對(duì)象,構(gòu)造方法中綁定要讀取的數(shù)據(jù)源。②創(chuàng)建一個(gè)客戶端Socket對(duì)象,構(gòu)造方法中綁定服務(wù)器的IP地址和端口號(hào)。③使用Socket中的getOutputStream()方法,獲取網(wǎng)絡(luò)字節(jié)輸出流OutputStream對(duì)象。④使用本地字節(jié)輸入流FileInputStream對(duì)象中的read()方法,讀取本地文件。⑤使用網(wǎng)絡(luò)字節(jié)輸出流OutputStream對(duì)象中的write()方法,把讀取到的文件上傳到服務(wù)器。⑥使用Socket中的方法getInputStream,獲取網(wǎng)絡(luò)字節(jié)輸入流InputStream對(duì)象。⑦使用網(wǎng)絡(luò)字節(jié)輸入流InputStream對(duì)象中的read()方法讀取服務(wù)回寫的數(shù)據(jù)。⑧釋放資源(FileInputStream,Socket)。任務(wù)9.1文件上傳程序設(shè)計(jì)2.具體實(shí)現(xiàn)步驟(2)服務(wù)端實(shí)現(xiàn)步驟:①創(chuàng)建一個(gè)服務(wù)器ServerSocket對(duì)象,和系統(tǒng)要指定的端口號(hào)。②使用ServerSocket對(duì)象中的accept()方法,獲取到請(qǐng)求的客戶端Socket對(duì)象。③使用Socket對(duì)象中的方法getInputStream,獲取到InputStream對(duì)象。④判斷d:\\upload文件夾是否存在,不存在則創(chuàng)建。⑤創(chuàng)建一個(gè)FileOutputStream對(duì)象,構(gòu)造方法中綁定要輸出的目的地。⑥使用InputStream對(duì)象中的方法read,讀取客戶端上傳的文件。⑦使用FileOutputStream對(duì)象中的write()方法,把讀取到的文件保存到服務(wù)器的硬盤上。⑧使用Socket對(duì)象中的getOutputStream()方法,獲取到網(wǎng)絡(luò)字節(jié)輸出流OutputStream對(duì)象。⑨使用網(wǎng)絡(luò)字節(jié)輸出流OutputStream對(duì)象中的write()方法,給客戶端回寫“上傳成功”。⑩釋放資源(FileOutputStream,Socket,ServerSocket)。任務(wù)9.1文件上傳程序設(shè)計(jì)3.程序編寫服務(wù)器端程序如例9-3所示,客戶端程序如例9-4所示。任務(wù)9.1文件上傳程序設(shè)計(jì)importjava.io.*;import.*;publicclassExample9_4{publicstaticvoidmain(String[]args)throwsIOException{//1.創(chuàng)建服務(wù)器,指定端口號(hào)ServerSocketserver=newServerSocket(8888);//2.獲取到請(qǐng)求的客戶端Socketsocket=server.accept();//3.獲取到網(wǎng)絡(luò)字節(jié)輸入流InputStreamis=socket.getInputStream();//4.判斷d:\\upload文件夾是否存在,不存在則創(chuàng)建Filefile=newFile("d:\\upload");if(!file.exists()){file.mkdirs();}//5.創(chuàng)建一個(gè)本地字節(jié)輸出流,綁定要輸出的目的地FileOutputStreamfos=newFileOutputStream(file+"\\1.jpg");//6.使用網(wǎng)絡(luò)字節(jié)輸入流讀取客戶端上傳的文件intlen=0;
任務(wù)9.1文件上傳程序設(shè)計(jì)byte[]bytes=newbyte[1024];while((len=is.read(bytes))!=-1){//7.使用本地字節(jié)輸出流,把讀取到的文件保存到服務(wù)器的硬盤上fos.write(bytes,0,len);}//8.獲取到網(wǎng)絡(luò)字節(jié)輸出流//9.使用網(wǎng)絡(luò)字節(jié)輸出流,給客戶端回寫“上傳成功”socket.getOutputStream().write("上傳成功".getBytes());//10.釋放資源fos.close();socket.close();server.close();}}任務(wù)9.1文件上傳程序設(shè)計(jì)importjava.io.*;import.Socket;publicclassExample9_4{publicstaticvoidmain(String[]args)throwsIOException{//1.創(chuàng)建本地字節(jié)輸入流,綁定要讀取的數(shù)據(jù)源FileInputStreamfis=newFileInputStream("c:\\1.jpg");//2.創(chuàng)建客戶端對(duì)象,綁定服務(wù)器IP地址和端口號(hào)Socketsocket=newSocket("",8888);//3.獲取網(wǎng)絡(luò)字節(jié)輸出流OutputStreamos=socket.getOutputStream();//4.讀取本地文件intlen=0;byte[]bytes=newbyte[1024];while((len=fis.read(bytes))!=-1){//5.把讀取到的文件上傳到服務(wù)器os.write(bytes,0,len);}//6.獲取網(wǎng)絡(luò)字節(jié)輸入流InputStreamis=socket.getInputStream();//7.讀取服務(wù)回寫的數(shù)據(jù)while((len=is.read(bytes))!=-1){System.out.println(newString(bytes,0,len));例9-4Example9_4.java}//8.釋放資源fis.close();socket.close();}}
完成程序編寫后,按“先運(yùn)行服務(wù)器端,后運(yùn)行客戶端”的順序運(yùn)行程序,能夠看到D盤中生成了一個(gè)名為“upload”的文件夾,里面的存放了一個(gè)文件,該文件與“c:\\1.jpg”文件一致,說(shuō)明實(shí)現(xiàn)了文件的上傳。
需要注意的是,上述代碼雖然實(shí)現(xiàn)了文件的上傳,但無(wú)論是客戶端還是服務(wù)器端,程序都沒(méi)有停止運(yùn)行,這是因?yàn)槭艿紽ileInputStream類的read()方法影響。通過(guò)查看API手冊(cè)可以知道,read(byte[]b)方法功能是“從此輸入流中將最多b.length個(gè)字節(jié)數(shù)據(jù)讀入一個(gè)byte數(shù)組中,在某些輸入可用之前,此方法將阻塞”。在例9-3代碼中,客戶端中的fis.read(bytes)讀取本地文件,讀取到-1則結(jié)束,然而在while循環(huán)里并不會(huì)讀取到-1,也就不會(huì)把結(jié)束標(biāo)記寫給服務(wù)器端。服務(wù)器端中的is.read(bytes)用于讀取客戶端上傳的文件,但由于讀不到文件的結(jié)束標(biāo)志,read()方法就會(huì)進(jìn)入到阻塞狀態(tài),一直死循環(huán)等待結(jié)束標(biāo)記。同樣的,客戶端中的is.read(bytes)也會(huì)因?yàn)樽x取不到服務(wù)器回寫的數(shù)據(jù)而進(jìn)入阻塞狀態(tài)。
為了能夠更加直觀看出阻塞現(xiàn)象,現(xiàn)分別對(duì)例9-3和例9-4的代碼進(jìn)行修改。任務(wù)9.1文件上傳程序設(shè)計(jì)任務(wù)9.1文件上傳程序設(shè)計(jì)publicclassExample9_3{publicstaticvoidmain(String[]args)throwsIOException{……//省略例9-3重復(fù)代碼
System.out.println("服務(wù)器端阻塞測(cè)試");while((len=is.read(bytes))!=-1){fos.write(bytes,0,len);}
System.out.println("若此語(yǔ)句出現(xiàn),則說(shuō)明服務(wù)器端未發(fā)生阻塞");……}}修改例9-3Example9_3.java任務(wù)9.1文件上傳程序設(shè)計(jì)publicclassExample9_4{publicstaticvoidmain(String[]args)throwsIOException{……//省略例9-4重復(fù)代碼System.out.println("客戶端阻塞測(cè)試");while((len=is.read(bytes))!=-1){System.out.println(newString(bytes,0,len));}
System.out.println("若此語(yǔ)句出現(xiàn),則說(shuō)明客戶端未發(fā)生阻塞");……}}修改例9-4Example9_4.java
修改后的例9-3和例9-4運(yùn)行結(jié)果分別如圖9-7和圖9-8所示,可以看到服務(wù)器端打印輸出語(yǔ)句“服務(wù)器端阻塞測(cè)試”,但沒(méi)有出現(xiàn)“若此語(yǔ)句出現(xiàn),則說(shuō)明服務(wù)器端未發(fā)生阻塞”;客戶端打印輸出“客戶端阻塞測(cè)試”,但沒(méi)有出現(xiàn)“若此語(yǔ)句出現(xiàn),則說(shuō)明客戶器端未發(fā)生阻塞”,說(shuō)明服務(wù)器端和客戶端均在while中產(chǎn)生了死循環(huán),發(fā)生了阻塞現(xiàn)象。任務(wù)9.1文件上傳程序設(shè)計(jì)圖9-7Example9_3修改后的運(yùn)行結(jié)果(服務(wù)器端)圖9-8Example9_4修改后的運(yùn)行結(jié)果(客戶端)
那么如何解決上述的阻塞問(wèn)題呢?由于阻塞問(wèn)題是由于服務(wù)器端沒(méi)有讀到結(jié)束標(biāo)記造成的,所以在上傳文件后,只需要給服務(wù)器寫一個(gè)結(jié)束標(biāo)記就可以解決問(wèn)題了。在這里需要用到socket類的shutdownOutput()方法,此方法的作用是禁用此套接字的輸出流。對(duì)于TCP套接字,任何以前寫入的數(shù)據(jù)都將被發(fā)送,并且后跟TCP的正常連接終止序列?,F(xiàn)對(duì)例9-4的代碼再次進(jìn)行修改。任務(wù)9.1文件上傳程序設(shè)計(jì)任務(wù)9.1文件上傳程序設(shè)計(jì)publicclassExample9_4{publicstaticvoidmain(String[]args)throwsIOException{……//省略例9-4重復(fù)代碼while((len=fis.read(bytes))!=-1){os.write(bytes,0,len);}socket.shutdownOutput();InputStreamis=socket.getInputStream();System.out.println("客戶端阻塞測(cè)試");while((len=is.read(bytes))!=-1){System.out.println(newString(bytes,0,len));}System.out.println("若此語(yǔ)句出現(xiàn),則說(shuō)明客戶端未發(fā)生阻塞");……}}再次修改例9-4Example9_4.java
修改完成后,重新運(yùn)行例9-3和例9-4程序,其運(yùn)行結(jié)果分別如圖9-9和圖9-10所示。可以看到服務(wù)器端打印輸出語(yǔ)句“服務(wù)器端阻塞測(cè)試”、“若此語(yǔ)句出現(xiàn),則說(shuō)明服務(wù)器端未發(fā)生阻塞”,;客戶端打印輸出“客戶端阻塞測(cè)試”、“上傳成功”、“若此語(yǔ)句出現(xiàn),則說(shuō)明客戶器端未發(fā)生阻塞”,但沒(méi)有出現(xiàn)“若此語(yǔ)句出現(xiàn),則說(shuō)明客戶器端未發(fā)生阻塞”,并且程序在完成文件的上傳后停止運(yùn)行,說(shuō)明成功解決了阻塞問(wèn)題。任務(wù)9.1文件上傳程序設(shè)計(jì)圖9-9Example9_3的運(yùn)行結(jié)果(服務(wù)器端)圖9-10Example9_4的運(yùn)行結(jié)果(客戶端)4.程序優(yōu)化
現(xiàn)對(duì)文件上傳進(jìn)行優(yōu)化,以解決以下兩個(gè)問(wèn)題:
(1)文件名稱寫死的問(wèn)題。在服務(wù)器端中,保存文件的名稱如果寫死,那么最終導(dǎo)致服務(wù)器硬盤只會(huì)保留一個(gè)文件。建議使用系統(tǒng)時(shí)間優(yōu)化,保證文件名稱唯一。
(2)循環(huán)接收的問(wèn)題。在前面的程序中,服務(wù)器端只保存一個(gè)文件就關(guān)閉了,之后的用戶無(wú)法再上傳,這是不符合實(shí)際的。使用循環(huán)改進(jìn),可以不斷的接收不同用戶的文件。
(3)效率問(wèn)題。如果多個(gè)人需要同時(shí)上傳文件時(shí),且文件較大時(shí),服務(wù)端每接收一個(gè)文件可能會(huì)耗費(fèi)幾秒鐘的時(shí)間,此時(shí)不能接收其他用戶上傳,那么效率就會(huì)非常低??梢允褂枚嗑€程技術(shù)進(jìn)行優(yōu)化。
修改后的服務(wù)端代碼如例9-5所示。任務(wù)9.1文件上傳程序設(shè)計(jì)任務(wù)9.1文件上傳程序設(shè)計(jì)importjava.io.*;import.*;importjava.util.Random;publicclassExample9_5{publicstaticvoidmain(String[]args)throwsIOException{ServerSocketserver=newServerSocket(8888);//優(yōu)化2:讓服務(wù)器一直處于監(jiān)聽狀態(tài)//有一個(gè)客戶端上傳文件,就保存一個(gè)文件while(true){Socketsocket=server.accept();//優(yōu)化3:使用多線程技術(shù),提高程序的效率//有一個(gè)客戶端上傳文件,就開啟一個(gè)線程,完成文件的上傳newThread(newRunnable(){//完成文件的
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 生日鮮花合同范本
- 襪廠工人協(xié)議書
- 認(rèn)干爹的協(xié)議書
- 設(shè)備包機(jī)協(xié)議書
- 設(shè)備經(jīng)銷協(xié)議書
- 設(shè)計(jì)修改協(xié)議書
- 設(shè)計(jì)蓋章協(xié)議書
- 試工培訓(xùn)協(xié)議書
- 康養(yǎng)聯(lián)合體協(xié)議書
- 建設(shè)大門協(xié)議書
- 水利信息化水情監(jiān)測(cè)系統(tǒng)單元工程質(zhì)量驗(yàn)收評(píng)定表、檢查記錄
- 電驅(qū)動(dòng)石油深井鉆機(jī)相關(guān)項(xiàng)目投資計(jì)劃書范本
- 車位轉(zhuǎn)讓車位協(xié)議書模板
- 國(guó)家基本公共衛(wèi)生服務(wù)項(xiàng)目之健康教育
- 中國(guó)融通地產(chǎn)社招筆試
- DLT 572-2021 電力變壓器運(yùn)行規(guī)程
- DL∕T 1430-2015 變電設(shè)備在線監(jiān)測(cè)系統(tǒng)技術(shù)導(dǎo)則
- 國(guó)家開放大學(xué)電大《11876國(guó)際私法》期末終考題庫(kù)及答案
- 員工下班喝酒意外免責(zé)協(xié)議書
- 光動(dòng)力療法治愈牙周潰瘍探討
- 2024年載貨汽車項(xiàng)目營(yíng)銷策劃方案
評(píng)論
0/150
提交評(píng)論