用javasocket開發(fā)高并發(fā)小型服務(wù)器_第1頁
用javasocket開發(fā)高并發(fā)小型服務(wù)器_第2頁
用javasocket開發(fā)高并發(fā)小型服務(wù)器_第3頁
用javasocket開發(fā)高并發(fā)小型服務(wù)器_第4頁
用javasocket開發(fā)高并發(fā)小型服務(wù)器_第5頁
已閱讀5頁,還剩46頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

1、用JavaSocket開發(fā)高并發(fā)小型效勞器JavaSocket套接字socket為兩臺計算機之間的通信提供了一種機制,在JamesGosling注意到Java語言之前,套接字就早已赫赫有名。該語言只是讓您不必理解底層操作系統(tǒng)的細節(jié)就能有效地使用套接字。1 客戶機/效勞器模型在飯店里,菜單上各種具有異國情調(diào)的食品映入你的眼簾,于是你要了一份pizza。幾分鐘后,你用力咀嚼澆著融化的乳酪和其他你喜歡的配料的熱pizza。你不知道,也不想知道:侍者從那里弄來了pizza,在制作過程中加進了什么,以及配料是如何獲得的。上例中包含的實體有:美味的pizza、承受你定餐的侍者、制作pizza的廚房,當然還

2、有你。你是定pizza的顧客或客戶。制作pizza的過程對于你而言是被封裝的。你的懇求在廚房中被處理,pizza制作完成后,由侍者端給你。你所看到的就是一個客戶機/效勞器模型??蛻魴C向效勞器發(fā)送一個懇求或命令。效勞器處理客戶機的懇求??蛻魴C和效勞器之間的通訊是客戶機/效勞器模型中的一個重要組成部分,通常通過網(wǎng)絡(luò)進展??蛻魴C/效勞器模型是一個應(yīng)用程序開發(fā)框架,該框架是為了將數(shù)據(jù)的表示與其內(nèi)部的處理和存儲別分開來而設(shè)計的??蛻魴C懇求效勞,效勞器為這些懇求效勞。懇求通過網(wǎng)絡(luò)從客戶機傳遞到效勞器。效勞器所進展的處理對客戶機而言是隱藏的。一個效勞器可以為多臺客戶機效勞。多臺客戶機訪問效勞器效勞器和客戶機

3、不一定是硬件組件。它們可以是工作啊同一機器或不同機器上的程序。、考慮一個航空定票系統(tǒng)中的數(shù)據(jù)輸入程序:數(shù)據(jù)乘客名、航班號、飛行日期、目的地等可以被輸入到前端客戶機的應(yīng)用程序中。一旦數(shù)據(jù)輸入之后,客戶機將數(shù)據(jù)發(fā)送到后端效勞器端。效勞器處理數(shù)據(jù)并在數(shù)據(jù)庫中保存數(shù)據(jù)??蛻魴C/效勞器模型的重要性在于所有的數(shù)據(jù)都存放在同一地點。客戶機從不同的地方訪問同一數(shù)據(jù)源,效勞器對所有的輸入數(shù)據(jù)應(yīng)用同樣的檢驗規(guī)那么。萬維網(wǎng)為為什么要將數(shù)據(jù)的表示與其存儲、處理別分開來提供了一個很好的例子。在Web上,你無需控制最終用戶用來訪問你數(shù)據(jù)的平臺和軟件。你可以考慮編寫出適用與每一種潛在的目的平臺的應(yīng)用程序??蛻魴C/效勞器應(yīng)用

4、程序的效勞器部分管理通過多個客戶機訪問效勞器的、多個用戶共享的資源。說明客戶機/效勞器程序的效勞器部分強大功能的最好例子應(yīng)該是Web效勞器,它通過Internet將HTML頁傳遞給不同的Web用戶。Java編程語言中最根本的特點是在Java中創(chuàng)立的程序的代碼的可移植性。因為具有其他語言所不具備的代碼可移植性,Java允許用戶只要編寫一次應(yīng)用程序,就可以在任何客戶機系統(tǒng)上發(fā)布它,并可以讓客戶機系統(tǒng)解釋該程序。這意味著:你只要寫一次代碼,就能使其在任何平臺上運行。2 協(xié)議當你同朋友交談時,你們遵循一些暗含的規(guī)那么或協(xié)議。例如:你們倆不能同時開始說話,或連續(xù)不連續(xù)地說話。假設(shè)你們這樣作的話,誰也不能

5、理解對方所說的東西。當你說話時,你的朋友傾聽,反之亦然。你們以雙方都能理解的語言和速度進展對話。當計算機之間進展通訊的時候,也需要遵循一定的規(guī)那么。數(shù)據(jù)以包的形式從一臺機器發(fā)送到另一臺。這些規(guī)那么管理數(shù)據(jù)打包、數(shù)據(jù)傳輸速度和重新數(shù)據(jù)將其恢復(fù)成原始形式。這些規(guī)那么被稱為網(wǎng)絡(luò)協(xié)議。網(wǎng)絡(luò)協(xié)議是通過網(wǎng)絡(luò)進展通訊的系統(tǒng)所遵循的一系列規(guī)那么和慣例。連網(wǎng)軟件通常實現(xiàn)有上下層次之分的多層協(xié)議。網(wǎng)絡(luò)協(xié)議的例子有:TCP/IP、UDP、AppleTalk和NetBEUI。Java提供了一個豐富的、支持網(wǎng)絡(luò)的類庫,這些類使得應(yīng)用程序能方便地訪問網(wǎng)絡(luò)資源。Java提供了兩種通訊工具。它們是:使用用戶報文協(xié)議UDP的報

6、文和使用傳輸控制協(xié)議/因特網(wǎng)協(xié)議TCP/IP的Sockets套接字。數(shù)據(jù)報包是一個字節(jié)數(shù)組從一個程序發(fā)送程序傳送到另一個承受程序。由于數(shù)據(jù)報遵守UDP,不保證發(fā)出的數(shù)據(jù)包必須到達目的地。數(shù)據(jù)報并不是可信賴的。因此,僅當傳送少量數(shù)據(jù)時才使用,而且發(fā)送者和承受者之間的間隔間隔不大,假設(shè)是網(wǎng)絡(luò)交通頂峰,或承受程序正處理來自其他程序的多個懇求,就有時機出現(xiàn)數(shù)據(jù)報包的喪失。Sockets套接字用TCP來進展通訊。套接字模型同其他模型相比,優(yōu)越性在于其不受客戶懇求來自何處的影響。只要客戶機遵循TCP/IP協(xié)議,效勞器就會對它的懇求提供效勞。這意味著客戶機可以是任何類型的計算機??蛻魴C不再局限為UNIX、W

7、indows、DOS或Macintosh平臺,因此,網(wǎng)上所有遵循TCP/IP協(xié)議的計算機可以通過套接字互相通訊。3 Sockets套接字3.1 Sockets概況在客戶機/效勞器應(yīng)用程序中,效勞器提供象處理數(shù)據(jù)庫查詢或修改數(shù)據(jù)庫中的數(shù)據(jù)之類的效勞。發(fā)生在客戶機和效勞器之間的通訊必須是可靠的,同時數(shù)據(jù)在客戶機上的次序應(yīng)該和效勞器發(fā)送出來的次序一樣。什么是套接字?既然我們已經(jīng)知道套接字扮演的角色,那么剩下的問題是:什么是套接字?BruceEckel在他的?Java編程思想?一書中這樣描繪套接字:套接字是一種軟件抽象,用于表達兩臺機器之間的連接“終端。對于一個給定的連接,每臺機器上都有一個套接字,您

8、也可以想象它們之間有一條虛擬的“電纜,“電纜的每一端都插入到套接字中。當然,機器之間的物理硬件和電纜連接都是完全未知的。抽象的全部目的是使我們無須知道不必知道的細節(jié)。簡言之,一臺機器上的套接字與另一臺機器上的套接字交談就創(chuàng)立一條通信通道。程序員可以用該通道來在兩臺機器之間發(fā)送數(shù)據(jù)。當您發(fā)送數(shù)據(jù)時,TCP/IP協(xié)議棧的每一層都會添加適當?shù)膱箢^信息來包裝數(shù)據(jù)。這些報頭幫助協(xié)議棧把您的數(shù)據(jù)送到目的地。好消息是Java語言通過"流"為您的代碼提供數(shù)據(jù),從而隱藏了所有這些細節(jié),這也是為什么它們有時候被叫做流套接字streamingsocket的原因。把套接字想成兩端上的聽筒,我和您通

9、過專用通道在我們的聽筒上講話和聆聽。直到我們決定掛斷,對話才會完畢除非我們在使用蜂窩。而且我們各自的線路都占線,直到我們掛斷。假設(shè)想在沒有更高級機制如ORB以及CORBA、RMI、IIOP等等開銷的情況下進展兩臺計算機之間的通信,那么套接字就適宜您。套接字的低級細節(jié)相當棘手。幸運的是,Java平臺給了您一些雖然簡單但卻強大的更高級抽象,使您可以容易地創(chuàng)立和使用套接字。傳輸控制協(xié)議TCP提供了一條可靠的、點對點的通訊通道,客戶機/效勞器應(yīng)用程序可要通過TCP進展通訊,客戶機和效勞器程序建立連接并綁定套接字。套接字用于處理通過網(wǎng)絡(luò)連接的應(yīng)用程序之間的通訊。客戶機和效勞器之間更深化的通訊通過套接字完

10、成。Java被設(shè)計成一種連網(wǎng)語言。它通過將連接功能封裝到套接字類里而使得網(wǎng)絡(luò)編程更加容易。套接字類即Socket類它創(chuàng)立一個客戶套接字和ServerSocket類它創(chuàng)立一個效勞器套接字。套接字類大致介紹如下:lSocket是基類,它支持TCP協(xié)議。TCP是一個可靠的流網(wǎng)絡(luò)連接協(xié)議。Socket類提供了流輸入/輸出的方法,使得從套接字中讀出數(shù)據(jù)和往套接字中寫數(shù)據(jù)都很容易。該類對于編寫因特網(wǎng)上的通訊程序而言是必不可少的。lServerSocket是一個因特網(wǎng)效勞程序用來監(jiān)聽客戶懇求的類。ServerSocket實際上并不執(zhí)行效勞;而是創(chuàng)立了一個Socket對象來代表客戶機。通訊由創(chuàng)立的對象來完成。

11、3.2 IP地址和端口因特網(wǎng)效勞器可以被認為是一組套接字類,它們提供了一般稱為效勞的附加功能。效勞的例子有:電子郵件、遠程登錄的Telnet、和通過網(wǎng)絡(luò)傳輸文件的文件傳輸協(xié)議FTP。每種效勞都與一個端口相聯(lián)絡(luò)。端口是一個數(shù)值地址,通過它來處理效勞懇求就象懇求Web頁一樣。TCP協(xié)議需要兩個數(shù)據(jù)項:IP地址和端口號。因此,當鍵入時,你是如何進入金諾的主頁呢?因特網(wǎng)協(xié)議IP提供每一項網(wǎng)絡(luò)設(shè)備。這些設(shè)備都帶有一個稱為IP地址的邏輯地址。由因特網(wǎng)協(xié)議提供的IP地址具有特定的形式。每個IP地址都是32位的數(shù)值,表示4個范圍在0到255之間的8位數(shù)值金諾已經(jīng)注冊了它的名字,分配給注意:域名效勞或DNS效勞

12、是將。假設(shè)沒有指明端口號,那么使用效勞文件中效勞器的端口。每種協(xié)議有一個缺省的端口號,在端口號未指明時使用該缺省端口號。端口號應(yīng)用21 FTP.傳輸文件23 Telnet.提供遠程登錄25 SMTP.傳遞郵件信息67 BOOTP.在啟動時提供配置情況80. 傳輸 Web 頁109POP.使用戶能訪問遠程系統(tǒng)中的郵箱讓我們再來看一下URL:URL的第一部分意味著你正在使用超文本傳輸協(xié)議,該協(xié)議處理Web文檔。假設(shè)沒有指明文件,大多數(shù)的Web效勞器會取一個叫index.html文件。因此,IP地址和端口既可以通過明確指出URL各部分來決定,也可以由缺省值決定。4創(chuàng)立Socket客戶我們將在本部分討

13、論的例如將說明在Java代碼中如何使用Socket和ServerSocket??蛻魴C用Socket連接到效勞器。效勞器用ServerSocket在端口1001偵聽??蛻魴C懇求效勞器C:驅(qū)動器上的文件內(nèi)容。創(chuàng)立RemoteFileClient類publicclassRemoteFileClientprotectedBufferedReadersocketReader;protectedPrintWritersocketWriter;protectedStringhostIp;protectedinthostPort;/構(gòu)造方法publicRemoteFileClient(StringhostIp

14、,inthostPort)this.hostIp=hostIp;this.hostPort=hostPort;/向效勞器懇求文件的內(nèi)容publicStringgetFile(StringfileNameToGet)StringBufferfileLines=newStringBuffer();trysocketWriter.println(fileNameToGet);socketWriter.flush();Stringline=null;while(line=socketReader.readLine()!=null)fileLines.append(line+"n")

15、;catch(IOExceptione)returnfileLines.toString();/連接到遠程效勞器publicvoidsetUpConnection()trySocketclient=newSocket(hostIp,hostPort);socketReader=newBufferedReader(newInputStreamReader(client.getInputStream();socketWriter=newPrintWriter(client.getOutputStream();catch(UnknownHostExceptione)catch(IOException

16、e)/斷開遠程效勞器publicvoidtearDownConnection()try socketWriter.close();socketReader.close();catch(IOExceptione)publicstaticvoidmain(Stringargs)remoteFileClient.setUpConnection();StringBufferfileContents=newStringBuffer();fileContents.append(remoteFileClient.getFile("RemoteFileServer.java");/remo

17、teFileClient.tearDownConnection();首先我們導(dǎo)入和java.io。包為您提供您需要的套接字工具。java.io包為您提供對流進展讀寫的工具,這是您與TCP套接字通信的唯一途徑。我們給我們的類實例變量以支持對套接字流的讀寫和存儲我們將連接到的遠程主機的詳細信息。我們類的構(gòu)造器有兩個參數(shù):遠程主機的IP地址和端口號各一個,而且構(gòu)造器將它們賦給實例變量。我們的類有一個main()方法和三個其它方法。稍后我們將探究這些方法的細節(jié)。如今您只需知道setUpConnection()將連接到遠程效勞器,getFile()將向遠程效勞器懇求fileNameToGet的內(nèi)容以及t

18、earDownConnection()將從遠程效勞器上斷開。實現(xiàn)main()這里我們實現(xiàn)main()方法,它將創(chuàng)立RemoteFileClient并用它來獲取遠程文件的內(nèi)容,然后打印結(jié)果。main()方法用主機的IP地址和端口號實例化一個新RemoteFileClient客戶機。然后,我們告訴客戶機建立一個到主機的連接。接著,我們告訴客戶機獲取主機上一個指定文件的內(nèi)容。最后,我們告訴客戶機斷開它到主機的連接。我們把文件內(nèi)容打印到控制臺,只是為了證明一切都是按方案進展的。建立連接這里我們實現(xiàn)setUpConnection()方法,它將創(chuàng)立我們的Socket并讓我們訪問該套接字的流:publicv

19、oidsetUpConnection()trySocketclient=newSocket(hostIp,hostPort);socketReader=newBufferedReader(newInputStreamReader(client.getInputStream();socketWriter=newPrintWriter(client.getOutputStream();catch(UnknownHostExceptione)catch(IOExceptione)setUpConnection()方法用主機的IP地址和端口號創(chuàng)立一個Socket:Socketclient=newSoc

20、ket(hostIp,hostPort);我們把Socket的InputStream包裝進BufferedReader以使我們可以讀取流的行。然后,我們把Socket的OutputStream包裝進PrintWriter以使我們可以發(fā)送文件懇求到效勞器:socketReader=newBufferedReader(newInputStreamReader(client.getInputStream();socketWriter=newPrintWriter(client.getOutputStream();客戶機和效勞器都必須知道另一方即將發(fā)請記住我們的客戶機和效勞器只是來回傳送字節(jié)。送的是什

21、么以使它們可以作出適當?shù)捻憫?yīng)。在這個案例中,效勞器知道我們將發(fā)送一條有效的文件途徑。當您實例化一個Socket時,將拋出UnknownHostException。這里我們不特別處理它,但我們打印一些信息到控制臺以告訴我們發(fā)生了什么錯誤。同樣地,當我們試圖獲取Socket的InputStream或OutputStream時,假設(shè)拋出了一個一般IOException,我們也打印一些信息到控制臺。與主機交談這里我們實現(xiàn)getFile()方法,它將告訴效勞器我們想要什么文件并在效勞器傳回其內(nèi)容時接收該內(nèi)容。publicStringgetFile(StringfileNameToGet)StringBu

22、fferfileLines=newStringBuffer();trysocketWriter.println(fileNameToGet);socketWriter.flush();Stringline=null;while(line=socketReader.readLine()!=null)fileLines.append(line+"n");catch(IOExceptione)returnfileLines.toString();對getFile()方法的調(diào)用要求一個有效的文件途徑String。它首先創(chuàng)立名為fileLines的StringBuffer,fileL

23、ines用于存儲我們讀自效勞器上的文件的每一行。StringBufferfileLines=newStringBuffer();在trycatch塊中,我們用PrintWriter把懇求發(fā)送到主機,PrintWriter是我們在創(chuàng)立連接期間建立的。socketWriter.flush();socketWriter.println(fileNameToGet);請注意這里我們是flush()該PrintWriter,而不是關(guān)閉它。這迫使數(shù)據(jù)被發(fā)送到效勞器而不關(guān)閉Socket。一旦我們已經(jīng)寫到Socket,我們就希望有一些響應(yīng)。我們不得不在Socket的InputStream上等待它,我們通過在w

24、hile循環(huán)中調(diào)用BufferedReader上的readLine()來到達這個目的。我們把每一個返回行附加到fileLinesStringBuffer帶有一個換行符以保護行:Stringline=null;while(line=socketReader.readLine()!=null)fileLines.append(line+"n");斷開連接這里我們實現(xiàn)tearDownConnection()方法,它將在我們使用完畢連接后負責“去除。tearDownConnection()方法只是分別關(guān)閉我們在Socket的InputStream和OutputStream上創(chuàng)立的B

25、ufferedReader和PrintWriter。這樣做會關(guān)閉我們從Socket獲取的底層流,所以我們必須捕捉可能的IOException??偨Y(jié)一下客戶機我們的類研究完了。在我們繼續(xù)往前討論效勞器端的情況之前,讓我們回憶一下創(chuàng)立和使用Socket的步驟:1. 用您想連接的機器的IP地址和端口實例化Socket如有問題那么拋出Exception。2. 獲取Socket上的流以進展讀寫。3. 把流包裝進BufferedReader/PrintWriter的實例,假設(shè)這樣做能使事情更簡單的話。4. 對Socket進展讀寫。5. 關(guān)閉翻開的流。5創(chuàng)立效勞器Socket創(chuàng)立RemoteFileServ

26、er類publicclassRemoteFileServerintlistenPort;publicRemoteFileServer(intlistenPort)this.listenPort=listenPort;/允許客戶機連接到效勞器,等待客戶機懇求publicvoidacceptConnections()tryServerSocketserver=newServerSocket(listenPort);SocketincomingConnection=null;while(true)incomingConnection=server.accept();handleConnection(

27、incomingConnection);catch(BindExceptione)catch(IOExceptione)/與客戶機Socket交互以將客戶機所懇求的文件的內(nèi)容發(fā)送到客戶機publicvoidhandleConnection(SocketincomingConnection)tryOutputStreamoutputToSocket=incomingConnection.getOutputStream();InputStreaminputFromSocket=incomingConnection.getInputStream();BufferedReaderstreamReade

28、r=newBufferedReader(newInputStreamReader(inputFromSocket);FileReaderfileReader=newFileReader(newFile(streamReader.readLine();BufferedReaderbufferedFileReader=newBufferedReader(fileReader);PrintWriterstreamWriter=newPrintWriter(incomingConnection.getOutputStream();Stringline=null;while(line=bufferedF

29、ileReader.readLine()!=null)streamWriter.println(line);fileReader.close();streamWriter.close();streamReader.close();catch(Exceptione)e.printStackTrace();publicstaticvoidmain(Stringargs)RemoteFileServerserver=newRemoteFileServer(1001);server.acceptConnections();我們的類有一個main()方法和兩個其它方法。稍后我們將探究這些方法的細節(jié)。如今

30、您只需知道acceptConnections()將允許客戶機連接到效勞器以及handleConnection()與客戶機Socket交互以將您所懇求的文件的內(nèi)容發(fā)送到客戶機。實現(xiàn)main()這里我們實現(xiàn)main()方法,它將創(chuàng)立RemoteFileServer并告訴它承受連接:效勞器端的main()方法中,我們實例化一個新RemoteFileServer,它將在偵聽端口1001上偵聽進入的連接懇求。然后我們調(diào)用acceptConnections()來告訴該server進展偵聽。承受連接這里我們實現(xiàn)acceptConnections()方法,它將創(chuàng)立一個ServerSocket并等待連接懇求:p

31、ublicvoidacceptConnections()tryServerSocketserver=newServerSocket(listenPort);SocketincomingConnection=null;while(true)incomingConnection=server.accept();handleConnection(incomingConnection);catch(BindExceptione)catch(IOExceptione)acceptConnections()用欲偵聽的端口號來創(chuàng)立ServerSocket。然后我們通過調(diào)用該ServerSocket的acce

32、pt()來告訴它開始偵聽。accept()方法將造成阻塞直到來了一個連接懇求。此時,accept()返回一個新的Socket,這個Socket綁定到效勞器上一個隨機指定的端口,返回的Socket被傳遞給handleConnection()。請注意我們在一個無限循環(huán)中處理對連接的承受。這里不支持任何關(guān)機。無論何時假設(shè)您創(chuàng)立了一個無法綁定到指定端口可能是因為別的什么控制了該端口的ServerSocket,Java代碼都將拋出一個錯誤。所以這里我們必須捕捉可能的BindException就跟在客戶機端上時一樣,我們必須捕捉IOException,當我們試圖在ServerSocket上承受連接時,它就

33、會被拋出。請注意,您可以通過用毫秒數(shù)調(diào)用setSoTimeout()來為accept()調(diào)用設(shè)置超時,以防止實際長時間的等待。調(diào)用setSoTimeout()將使accept()經(jīng)過指定占用時間后拋出IOException。處理連接這里我們實現(xiàn)handleConnection()方法,它將用連接的流來接收輸入和寫輸出:publicvoidhandleConnection(SocketincomingConnection)tryOutputStreamoutputToSocket=incomingConnection.getOutputStream();InputStreaminputFromS

34、ocket=incomingConnection.getInputStream();BufferedReaderstreamReader=newBufferedReader(newInputStreamReader(inputFromSocket);FileReaderfileReader=newFileReader(newFile(streamReader.readLine();BufferedReaderbufferedFileReader=newBufferedReader(fileReader);PrintWriterstreamWriter=newPrintWriter(incomi

35、ngConnection.getOutputStream();Stringline=null;while(line=bufferedFileReader.readLine()!=null)streamWriter.println(line);fileReader.close();streamWriter.close();streamReader.close();catch(Exceptione)e.printStackTrace();跟在客戶機中一樣,我們用getOutputStream()和getInputStream()來獲取與我們剛創(chuàng)立的Socket相關(guān)聯(lián)的流。跟在客戶機端一樣,我們把I

36、nputStream包裝進BufferedReader,把OutputStream包裝進PrintWriter。在效勞器端上,我們需要添加一些代碼,用來讀取目的文件和把內(nèi)容逐行發(fā)送到客戶機。這里是重要的代碼:FileReaderfileReader=newFileReader(newFile(streamReader.readLine();BufferedReaderbufferedFileReader=newBufferedReader(fileReader);Stringline=null;while(line=bufferedFileReader.readLine()!=null)str

37、eamWriter.println(line);這些代碼值得詳細解釋。讓我們一點一點來看:FileReaderfileReader=newFileReader(newFile(streamReader.readLine();首先,我們使用Socket的InputStream的BufferedReader。我們應(yīng)該獲取一條有效的文件途徑,所以我們用該途徑名構(gòu)造一個新File。我們創(chuàng)立一個新FileReader來處理讀文件的操作。BufferedReaderbufferedFileReader=newBufferedReader(fileReader);這里我們把FileReader包裝進Buff

38、eredReader以使我們可以逐行地讀該文件。接著,我們調(diào)用BufferedReader的readLine()。這個調(diào)用將造成阻塞直到有字節(jié)到來。我們獲取一些字節(jié)之后就把它們放到本地的line變量中,然后再寫出到客戶機上。完成讀寫操作之后,我們就關(guān)閉翻開的流。請注意我們在完成從Socket的讀操作之后關(guān)閉streamWriter和streamReader。您或許會問我們?yōu)槭裁床辉谧x取文件名之后立即關(guān)閉streamReader。原因是當您這樣做時,您的客戶機將不會獲取任何數(shù)據(jù)。假設(shè)您在關(guān)閉streamWriter之前關(guān)閉streamReader,那么您可以往Socket寫任何東西,但卻沒有任何

39、數(shù)據(jù)能通過通道通道被關(guān)閉了??偨Y(jié)一下效勞器在我們接著討論另一個更實際的例如之前,讓我們回憶一下創(chuàng)立和使用ServerSocket的步驟:1.用一個您想讓它偵聽傳入客戶機連接的端口來實例化一個ServerSocket 如有問題那么拋出Exception。2. 調(diào)用ServerSocket的accept()以在等待連接期間造成阻塞。3. 獲取位于該底層Socket的流以進展讀寫操作。4. 按使事情簡單化的原那么包裝流。5. 對Socket進展讀寫。6. 關(guān)閉翻開的流并請記住,永遠不要在關(guān)閉Writer之前關(guān)閉Reader。6創(chuàng)立多線程Socket效勞器前面的例如教給您根底知識,但并不能令您更深化。

40、假設(shè)您到此就停頓了,那么您一次只能處理一臺客戶機。原因是handleConnection()是一個阻塞方法。只有當它完成了對當前連接的處理時,效勞器才能承受另一個客戶機。在多數(shù)時候,您將需要也有必要一個多線程效勞器。創(chuàng)立MultithreadedRemoteFileServer類publicclassMultithreadedRemoteFileServerintlistenPort;publicMultithreadedRemoteFileServer(intlistenPort)this.listenPort=listenPort;/允許客戶機連接到效勞器,等待客戶機懇求publicvoi

41、dacceptConnections()tryServerSocketserver=newServerSocket(listenPort,5);SocketincomingConnection=null;while(true)incomingConnection=server.accept();handleConnection(incomingConnection);catch(BindExceptione)catch(IOExceptione)/與客戶機Socket交互以將客戶機所懇求的文件的內(nèi)容發(fā)送到客戶機publicvoidhandleConnection(Socketconnectio

42、nToHandle)newThread(newConnectionHandler(connectionToHandle).start();publicstaticvoidmain(Stringargs)MultithreadedRemoteFileServerserver=newMultithreadedRemoteFileServer(1001);server.acceptConnections();這里我們實現(xiàn)改動過acceptConnections()方法,它將創(chuàng)立一個可以處理待發(fā)懇求的ServerSocket,并告訴ServerSocket承受連接。新的server仍然需要accept

43、Connections(),所以這些代碼實際上是一樣的。突出顯示的行表示一個重大的不同。對這個多線程版,我們?nèi)缃窨梢灾付蛻魴C懇求的最大數(shù)目,這些懇求都能在實例化ServerSocket期間處于待發(fā)狀態(tài)。假設(shè)我們沒有指定客戶機懇求的最大數(shù)目,那么我們假設(shè)使用缺省值50。這里是它的工作機制。假設(shè)我們指定待發(fā)數(shù)backlog值是5并且有五臺客戶機懇求連接到我們的效勞器。我們的效勞器將著手處理第一個連接,但處理該連接需要很長時間。由于我們的待發(fā)值是5,所以我們一次可以放五個懇求到隊列中。我們正在處理一個,所以這意味著還有其它五個正在等待。等待的和正在處理的一共有六個。當我們的效勞器仍忙于承受一號連接

44、記住隊列中還有2?6號時,假設(shè)有第七個客戶機提出連接申請,那么,該第七個客戶機將遭到回絕。我們將在帶有連接池效勞器例如中說明如何限定能同時連接的客戶機數(shù)目。處理連接:publicvoidhandleConnection(SocketconnectionToHandle)newThread(newConnectionHandler(connectionToHandle).start();我們對RemoteFileServer所做的大改動就表達在這個方法上。我們?nèi)匀辉谛谄鞒惺芤粋€連接之后調(diào)用handleConnection(),但如今我們把該Socket傳遞給ConnectionHandler的

45、一個實例,它是Runnable的。我們用ConnectionHandler創(chuàng)立一個新Thread并啟動它。ConnectionHandler的run()方法包Socket讀/寫和讀File的代碼,這些代碼原來在RemoteFileServer的handleConnection()中。創(chuàng)立ConnectionHandler類publicclassConnectionHandlerimplementsRunnableprotectedSocketsocketToHandle;publicConnectionHandler(SocketsocketToHandle)this.socketToHand

46、le=socketToHandle;publicvoidrun()tryPrintWriterstreamWriter=newPrintWriter(socketToHandle.getOutputStream();BufferedReaderstreamReader=newBufferedReader(newInputStreamReader(socketToHandle.getInputStream();StringfileToRead=streamReader.readLine();BufferedReaderfileReader=newBufferedReader(newFileRea

47、der(fileToRead);Stringline=null;while(line=fileReader.readLine()!=null)streamWriter.println(line);fileReader.close();streamWriter.close();streamReader.close();catch(Exceptione)e.printStackTrace();類的構(gòu)造器用一個Socket實例作參數(shù)并將它賦給socketToHandle。請注意該類實現(xiàn)了Runnable接口。實現(xiàn)這個接口的類都必須實現(xiàn)run()方法。這里我們實現(xiàn)run()方法,它將攫取我們的連接的流

48、,用它來讀寫該連接,并在任務(wù)完成之后關(guān)閉它。ConnectionHandler的run()方法所做的事情就是RemoteFileServer上的handleConnection()所做的事情。首先,我們把InputStream和OutputStream分別包裝用Socket的getOutputStream()和getInputStream()進BufferedReader和PrintWriter。然后我們用這些代碼逐行地讀目的文件:PrintWriterstreamWriter=newPrintWriter(socketToHandle.getOutputStream();BufferedRe

49、aderstreamReader=newBufferedReader(newInputStreamReader(socketToHandle.getInputStream();StringfileToRead=streamReader.readLine();BufferedReaderfileReader=newBufferedReader(newFileReader(fileToRead);Stringline=null;while(line=fileReader.readLine()!=null)streamWriter.println(line);請記住我們應(yīng)該從客戶機獲取一條有效的文件

50、途徑,這樣用該途徑名構(gòu)造一個新File,把它包裝進FileReader以處理讀文件的操作,然后把它包裝進BufferedReader以讓我們逐行地讀該文件。我們while循環(huán)中調(diào)用BufferedReader上的readLine()直到不再有要讀的行。請記注,對readLine()的調(diào)用將造成阻塞,直到有字節(jié)來到為止。我們獲取一些字節(jié)之后就把它們放到本地的line變量中,然后寫出到客戶機上。完成讀寫操作之后,我們關(guān)閉翻開的流??偨Y(jié)一下多線程效勞器讓我們回憶一下創(chuàng)立和使用“多線程版的效勞器的步驟:1. 修改acceptConnections()以用缺省為50或任何您想要的大于1的指定數(shù)字實例化S

51、erverSocket。2. 修改ServerSocket的handleConnection()以用ConnectionHandler的一個實例生成一個新的Thread。3. 借用RemoteFileServer的handleConnection()方法的代碼實現(xiàn)ConnectionHandler類。7創(chuàng)立帶有連接池的Socket效勞器我們?nèi)缃褚呀?jīng)擁有的MultithreadedServer每當有客戶機申請一個連接時都在一個新Thread中創(chuàng)立一個新ConnectionHandler。這意味著可能有一捆Thread“躺在我們周圍。而且創(chuàng)立Thread的系統(tǒng)開銷并不是微缺乏道的。假設(shè)性能成為了問

52、題也請不要事到臨頭才意識到它,更高效地處理我們的效勞器是件好事。那么,我們?nèi)绾胃咝У毓芾硇谄鞫四??我們可以維護一個進入的連接池,一定數(shù)量的ConnectionHandler將為它提供效勞。這種設(shè)計能帶來以下好處:?它限定了允許同時連接的數(shù)目。?我們只需啟動ConnectionHandlerThread一次。幸運的是,跟在我們的多線程例如中一樣,往代碼中添加“池不需要來一個大改動。事實上,應(yīng)用程序的客戶機端根本就不受影響。在效勞器端,我們在效勞器啟動時創(chuàng)立一定數(shù)量的ConnectionHandler,我們把進入的連接放入“池中并讓ConnectionHandler打理剩下的事情。這種設(shè)計中有

53、很多我們不打算討論的可能存在的技巧。例如,我們可以通過限定允許在“池中建立的連接的數(shù)目來回絕客戶機。請注意:我們將不會再次討論acceptConnections()。這個方法跟前面例如中的完全一樣。它無限循環(huán)地調(diào)用ServerSocket上的accept()并把連接傳遞到handleConnection()。創(chuàng)立PooledRemoteFileServer類publicclassPooledRemoteFileServerprotectedintmaxConnections;protectedintlistenPort;protectedServerSocketserverSocket;pub

54、licPooledRemoteFileServer(intaListenPort,intmaxConnections)listenPort=aListenPort;this.maxConnections=maxConnections;publicvoidacceptConnections()tryServerSocketserver=newServerSocket(listenPort,5);SocketincomingConnection=null;while(true)incomingConnection=server.accept();handleConnection(incomingC

55、onnection);catch(BindExceptione)catch(IOExceptione)protectedvoidhandleConnection(SocketconnectionToHandle)PooledConnectionHcessRequest(connectionToHandle);publicvoidsetUpHandlers()for(inti=0;i<maxConnections;i+)PooledConnectionHandlercurrentHandler=newPooledConnectionHandler();newThread(currentHandler,"Handler"+i).start();publicstaticvoidmain(Stringargs)PooledRemoteFileServerserver=newPooledRemoteFileServer(1001,3);server.setUpHandle

溫馨提示

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

評論

0/150

提交評論