




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
用JavaSocket開(kāi)發(fā)高并發(fā)小型服務(wù)器JavaSocket套接字(socket)為兩臺(tái)計(jì)算機(jī)之間的通信提供了一種機(jī)制,在JamesGosling注意到Java語(yǔ)言之前,套接字就早已赫赫有名。該語(yǔ)言只是讓您不必了解底層操作系統(tǒng)的細(xì)節(jié)就能有效地使用套接字。1客戶機(jī)/服務(wù)器模型在飯店里,菜單上各種具有異國(guó)情調(diào)的食品映入你的眼簾,于是你要了一份pizza。幾分鐘后,你用力咀嚼澆著融化的乳酪和其他你喜歡的配料的熱pizza。你不知道,也不想知道:侍者從那里弄來(lái)了pizza,在制作過(guò)程中加進(jìn)了什么,以及配料是如何獲得的。上例中包含的實(shí)體有:美味的pizza、接受你定餐的侍者、制作pizza的廚房,當(dāng)然還有你。你是定pizza的顧客或客戶。制作pizza的過(guò)程對(duì)于你而言是被封裝的。你的請(qǐng)求在廚房中被處理,pizza制作完成后,由侍者端給你。你所看到的就是一個(gè)客戶機(jī)/服務(wù)器模型??蛻魴C(jī)向服務(wù)器發(fā)送一個(gè)請(qǐng)求或命令。服務(wù)器處理客戶機(jī)的請(qǐng)求??蛻魴C(jī)和服務(wù)器之間的通訊是客戶機(jī)/服務(wù)器模型中的一個(gè)重要組成部分,通常通過(guò)網(wǎng)絡(luò)進(jìn)行??蛻魴C(jī)/服務(wù)器模型是一個(gè)應(yīng)用程序開(kāi)發(fā)框架,該框架是為了將數(shù)據(jù)的表示與其內(nèi)部的處理和存儲(chǔ)分離開(kāi)來(lái)而設(shè)計(jì)的。客戶機(jī)請(qǐng)求服務(wù),服務(wù)器為這些請(qǐng)求服務(wù)。請(qǐng)求通過(guò)網(wǎng)絡(luò)從客戶機(jī)傳遞到服務(wù)器。服務(wù)器所進(jìn)行的處理對(duì)客戶機(jī)而言是隱藏的。一個(gè)服務(wù)器可以為多臺(tái)客戶機(jī)服務(wù)。多臺(tái)客戶機(jī)訪問(wèn)服務(wù)器服務(wù)器和客戶機(jī)不一定是硬件組件。它們可以是工作啊同一機(jī)器或不同機(jī)器上的程序。、考慮一個(gè)航空定票系統(tǒng)中的數(shù)據(jù)輸入程序:數(shù)據(jù)----乘客名、航班號(hào)、飛行日期、目的地等可以被輸入到前端----客戶機(jī)的應(yīng)用程序中。一旦數(shù)據(jù)輸入之后,客戶機(jī)將數(shù)據(jù)發(fā)送到后端----服務(wù)器端。服務(wù)器處理數(shù)據(jù)并在數(shù)據(jù)庫(kù)中保存數(shù)據(jù)。客戶機(jī)/服務(wù)器模型的重要性在于所有的數(shù)據(jù)都存放在同一地點(diǎn)??蛻魴C(jī)從不同的地方訪問(wèn)同一數(shù)據(jù)源,服務(wù)器對(duì)所有的輸入數(shù)據(jù)應(yīng)用同樣的檢驗(yàn)規(guī)則。萬(wàn)維網(wǎng)為‘為什么要將數(shù)據(jù)的表示與其存儲(chǔ)、處理分離開(kāi)來(lái)’提供了一個(gè)很好的例子。在Web上,你無(wú)需控制最終用戶用來(lái)訪問(wèn)你數(shù)據(jù)的平臺(tái)和軟件。你可以考慮編寫(xiě)出適用與每一種潛在的目標(biāo)平臺(tái)的應(yīng)用程序。‘客戶機(jī)/服務(wù)器應(yīng)用程序的服務(wù)器部分’管理通過(guò)多個(gè)客戶機(jī)訪問(wèn)服務(wù)器的、多個(gè)用戶共享的資源。表明‘客戶機(jī)/服務(wù)器程序的服務(wù)器部分’強(qiáng)大功能的最好例子應(yīng)該是Web服務(wù)器,它通過(guò)Internet將HTML頁(yè)傳遞給不同的Web用戶。Java編程語(yǔ)言中最基本的特點(diǎn)是在Java中創(chuàng)建的程序的代碼的可移植性。因?yàn)榫哂衅渌Z(yǔ)言所不具備的代碼可移植性,Java允許用戶只要編寫(xiě)一次應(yīng)用程序,就可以在任何客戶機(jī)系統(tǒng)上發(fā)布它,并可以讓客戶機(jī)系統(tǒng)解釋該程序。這意味著:你只要寫(xiě)一次代碼,就能使其在任何平臺(tái)上運(yùn)行。2協(xié)議當(dāng)你同朋友交談時(shí),你們遵循一些暗含的規(guī)則(或協(xié)議)。例如:你們倆不能同時(shí)開(kāi)始說(shuō)話,或連續(xù)不間斷地說(shuō)話。如果你們這樣作的話,誰(shuí)也不能理解對(duì)方所說(shuō)的東西。當(dāng)你說(shuō)話時(shí),你的朋友傾聽(tīng),反之亦然。你們以雙方都能理解的語(yǔ)言和速度進(jìn)行對(duì)話。當(dāng)計(jì)算機(jī)之間進(jìn)行通訊的時(shí)候,也需要遵循一定的規(guī)則。數(shù)據(jù)以包的形式從一臺(tái)機(jī)器發(fā)送到另一臺(tái)。這些規(guī)則管理數(shù)據(jù)打包、數(shù)據(jù)傳輸速度和重新數(shù)據(jù)將其恢復(fù)成原始形式。這些規(guī)則被稱(chēng)為網(wǎng)絡(luò)協(xié)議。網(wǎng)絡(luò)協(xié)議是通過(guò)網(wǎng)絡(luò)進(jìn)行通訊的系統(tǒng)所遵循的一系列規(guī)則和慣例。連網(wǎng)軟件通常實(shí)現(xiàn)有高低層次之分的多層協(xié)議。網(wǎng)絡(luò)協(xié)議的例子有:TCP/IP、UDP、AppleTalk和NetBEUI。Java提供了一個(gè)豐富的、支持網(wǎng)絡(luò)的類(lèi)庫(kù),這些類(lèi)使得應(yīng)用程序能方便地訪問(wèn)網(wǎng)絡(luò)資源。Java提供了兩種通訊工具。它們是:使用用戶報(bào)文協(xié)議(UDP)的報(bào)文和使用傳輸控制協(xié)議/因特網(wǎng)協(xié)議(TCP/IP)的Sockets(套接字)。數(shù)據(jù)報(bào)包是一個(gè)字節(jié)數(shù)組從一個(gè)程序(發(fā)送程序)傳送到另一個(gè)(接受程序)。由于數(shù)據(jù)報(bào)遵守UDP,不保證發(fā)出的數(shù)據(jù)包必須到達(dá)目的地。數(shù)據(jù)報(bào)并不是可信賴(lài)的。因此,僅當(dāng)傳送少量數(shù)據(jù)時(shí)才使用,而且發(fā)送者和接受者之間的距離間隔不大,假如是網(wǎng)絡(luò)交通高峰,或接受程序正處理來(lái)自其他程序的多個(gè)請(qǐng)求,就有機(jī)會(huì)出現(xiàn)數(shù)據(jù)報(bào)包的丟失。Sockets套接字用TCP來(lái)進(jìn)行通訊。套接字模型同其他模型相比,優(yōu)越性在于其不受客戶請(qǐng)求來(lái)自何處的影響。只要客戶機(jī)遵循TCP/IP協(xié)議,服務(wù)器就會(huì)對(duì)它的請(qǐng)求提供服務(wù)。這意味著客戶機(jī)可以是任何類(lèi)型的計(jì)算機(jī)??蛻魴C(jī)不再局限為UNIX、Windows、DOS或Macintosh平臺(tái),因此,網(wǎng)上所有遵循TCP/IP協(xié)議的計(jì)算機(jī)可以通過(guò)套接字互相通訊。3Sockets套接字3.1Sockets概況在客戶機(jī)/服務(wù)器應(yīng)用程序中,服務(wù)器提供象處理數(shù)據(jù)庫(kù)查詢或修改數(shù)據(jù)庫(kù)中的數(shù)據(jù)之類(lèi)的服務(wù)。發(fā)生在客戶機(jī)和服務(wù)器之間的通訊必須是可靠的,同時(shí)數(shù)據(jù)在客戶機(jī)上的次序應(yīng)該和服務(wù)器發(fā)送出來(lái)的次序相同。什么是套接字?既然我們已經(jīng)知道套接字扮演的角色,那么剩下的問(wèn)題是:什么是套接字?BruceEckel在他的《Java編程思想》一書(shū)中這樣描述套接字:套接字是一種軟件抽象,用于表達(dá)兩臺(tái)機(jī)器之間的連接“終端”。對(duì)于一個(gè)給定的連接,每臺(tái)機(jī)器上都有一個(gè)套接字,您也可以想象它們之間有一條虛擬的“電纜”,“電纜”的每一端都插入到套接字中。當(dāng)然,機(jī)器之間的物理硬件和電纜連接都是完全未知的。抽象的全部目的是使我們無(wú)須知道不必知道的細(xì)節(jié)。簡(jiǎn)言之,一臺(tái)機(jī)器上的套接字與另一臺(tái)機(jī)器上的套接字交談就創(chuàng)建一條通信通道。程序員可以用該通道來(lái)在兩臺(tái)機(jī)器之間發(fā)送數(shù)據(jù)。當(dāng)您發(fā)送數(shù)據(jù)時(shí),TCP/IP協(xié)議棧的每一層都會(huì)添加適當(dāng)?shù)膱?bào)頭信息來(lái)包裝數(shù)據(jù)。這些報(bào)頭幫助協(xié)議棧把您的數(shù)據(jù)送到目的地。好消息是Java語(yǔ)言通過(guò)"流"為您的代碼提供數(shù)據(jù),從而隱藏了所有這些細(xì)節(jié),這也是為什么它們有時(shí)候被叫做流套接字(streamingsocket)的原因。把套接字想成兩端上的聽(tīng)筒,我和您通過(guò)專(zhuān)用通道在我們的聽(tīng)筒上講話和聆聽(tīng)。直到我們決定掛斷,對(duì)話才會(huì)結(jié)束(除非我們?cè)谑褂梅涓C)。而且我們各自的線路都占線,直到我們掛斷。如果想在沒(méi)有更高級(jí)機(jī)制如ORB(以及CORBA、RMI、IIOP等等)開(kāi)銷(xiāo)的情況下進(jìn)行兩臺(tái)計(jì)算機(jī)之間的通信,那么套接字就適合您。套接字的低級(jí)細(xì)節(jié)相當(dāng)棘手。幸運(yùn)的是,Java平臺(tái)給了您一些雖然簡(jiǎn)單但卻強(qiáng)大的更高級(jí)抽象,使您可以容易地創(chuàng)建和使用套接字。傳輸控制協(xié)議(TCP)提供了一條可靠的、點(diǎn)對(duì)點(diǎn)的通訊通道,客戶機(jī)/服務(wù)器應(yīng)用程序可以用該通道互相通訊。要通過(guò)TCP進(jìn)行通訊,客戶機(jī)和服務(wù)器程序建立連接并綁定套接字。套接字用于處理通過(guò)網(wǎng)絡(luò)連接的應(yīng)用程序之間的通訊??蛻魴C(jī)和服務(wù)器之間更深入的通訊通過(guò)套接字完成。Java被設(shè)計(jì)成一種連網(wǎng)語(yǔ)言。它通過(guò)將連接功能封裝到套接字類(lèi)里而使得網(wǎng)絡(luò)編程更加容易。套接字類(lèi)即Socket類(lèi)(它創(chuàng)建一個(gè)客戶套接字)和ServerSocket類(lèi)(它創(chuàng)建一個(gè)服務(wù)器套接字)。套接字類(lèi)大致介紹如下:lSocket是基類(lèi),它支持TCP協(xié)議。TCP是一個(gè)可靠的流網(wǎng)絡(luò)連接協(xié)議。Socket類(lèi)提供了流輸入/輸出的方法,使得從套接字中讀出數(shù)據(jù)和往套接字中寫(xiě)數(shù)據(jù)都很容易。該類(lèi)對(duì)于編寫(xiě)因特網(wǎng)上的通訊程序而言是必不可少的。lServerSocket是一個(gè)因特網(wǎng)服務(wù)程序用來(lái)監(jiān)聽(tīng)客戶請(qǐng)求的類(lèi)。ServerSocket實(shí)際上并不執(zhí)行服務(wù);而是創(chuàng)建了一個(gè)Socket對(duì)象來(lái)代表客戶機(jī)。通訊由創(chuàng)建的對(duì)象來(lái)完成。3.2IP地址和端口因特網(wǎng)服務(wù)器可以被認(rèn)為是一組套接字類(lèi),它們提供了一般稱(chēng)為服務(wù)的附加功能。服務(wù)的例子有:電子郵件、遠(yuǎn)程登錄的Telnet、和通過(guò)網(wǎng)絡(luò)傳輸文件的文件傳輸協(xié)議(FTP)。每種服務(wù)都與一個(gè)端口相聯(lián)系。端口是一個(gè)數(shù)值地址,通過(guò)它來(lái)處理服務(wù)請(qǐng)求(就象請(qǐng)求Web頁(yè)一樣)。TCP協(xié)議需要兩個(gè)數(shù)據(jù)項(xiàng):IP地址和端口號(hào)。因此,當(dāng)鍵入://jinnuo時(shí),你是如何進(jìn)入金諾的主頁(yè)呢?因特網(wǎng)協(xié)議(IP)提供每一項(xiàng)網(wǎng)絡(luò)設(shè)備。這些設(shè)備都帶有一個(gè)稱(chēng)為IP地址的邏輯地址。由因特網(wǎng)協(xié)議提供的IP地址具有特定的形式。每個(gè)IP地址都是32位的數(shù)值,表示4個(gè)范圍在0到255之間的8位數(shù)值金諾已經(jīng)注冊(cè)了它的名字,分配給://jinnuo的IP地址為10。注意:域名服務(wù)或DNS服務(wù)是將://jinnuo翻譯成10的服務(wù)。這使你可以鍵入://jinnuo而不必記住IP地址。想象一下,怎么可能記住所有需要訪問(wèn)的站點(diǎn)的IP地址!有趣的是一個(gè)網(wǎng)絡(luò)名可以映射到許多IP地址。對(duì)于經(jīng)常訪問(wèn)的站點(diǎn)可能需要這一功能,因?yàn)檫@些站點(diǎn)容納大量的信息,并需要多個(gè)IP地址來(lái)提供業(yè)務(wù)服務(wù)。例如:10的實(shí)際的內(nèi)部名稱(chēng)為://jinnuo。DNS可以將分配給jinnuoLtd.的一系列IP地址翻譯成://jinnuo。如果沒(méi)有指明端口號(hào),則使用服務(wù)文件中服務(wù)器的端口。每種協(xié)議有一個(gè)缺省的端口號(hào),在端口號(hào)未指明時(shí)使用該缺省端口號(hào)。端口號(hào)應(yīng)用21FTP.傳輸文件23Telnet.提供遠(yuǎn)程登錄25SMTP.傳遞郵件信息67BOOTP.在啟動(dòng)時(shí)提供配置情況80.傳輸Web頁(yè)109POP.使用戶能訪問(wèn)遠(yuǎn)程系統(tǒng)中的郵箱讓我們?cè)賮?lái)看一下URL:://jinnuoURL的第一部分()意味著你正在使用超文本傳輸協(xié)議(),該協(xié)議處理Web文檔。如果沒(méi)有指明文件,大多數(shù)的Web服務(wù)器會(huì)取一個(gè)叫index.html文件。因此,IP地址和端口既可以通過(guò)明確指出URL各部分來(lái)決定,也可以由缺省值決定。4創(chuàng)建Socket客戶我們將在本部分討論的示例將闡明在Java代碼中如何使用Socket和ServerSocket??蛻魴C(jī)用Socket連接到服務(wù)器。服務(wù)器用ServerSocket在端口1001偵聽(tīng)。客戶機(jī)請(qǐng)求服務(wù)器C:驅(qū)動(dòng)器上的文件內(nèi)容。創(chuàng)建RemoteFileClient類(lèi)importjava.io.*;import.*;publicclassRemoteFileClient{protectedBufferedReadersocketReader;protectedPrintWritersocketWriter;protectedStringhostIp;protectedinthostPort;//構(gòu)造方法publicRemoteFileClient(StringhostIp,inthostPort){this.hostIp=hostIp;this.hostPort=hostPort;}//向服務(wù)器請(qǐng)求文件的內(nèi)容publicStringgetFile(StringfileNameToGet){StringBufferfileLines=newStringBuffer();try{socketWriter.println(fileNameToGet);socketWriter.flush();Stringline=null;while((line=socketReader.readLine())!=null)fileLines.append(line+"\n");}catch(IOExceptione){System.out.println("Errorreadingfromfile:"+fileNameToGet);}returnfileLines.toString();}//連接到遠(yuǎn)程服務(wù)器publicvoidsetUpConnection(){try{Socketclient=newSocket(hostIp,hostPort);socketReader=newBufferedReader(newInputStreamReader(client.getInputStream()));socketWriter=newPrintWriter(client.getOutputStream());}catch(UnknownHostExceptione){System.out.println("Error1settingupsocketconnection:unknownhostat"+hostIp+":"+hostPort);}catch(IOExceptione){System.out.println("Error2settingupsocketconnection:"+e);}}//斷開(kāi)遠(yuǎn)程服務(wù)器publicvoidtearDownConnection(){try{socketWriter.close();socketReader.close();}catch(IOExceptione){System.out.println("Errortearingdownsocketconnection:"+e);}}publicstaticvoidmain(Stringargs[]){RemoteFileClientremoteFileClient=newRemoteFileClient("",1001);remoteFileClient.setUpConnection();StringBufferfileContents=newStringBuffer();fileContents.append(remoteFileClient.getFile("RemoteFileServer.java"));//remoteFileClient.tearDownConnection();System.out.println(fileContents);}}首先我們導(dǎo)入和包為您提供您需要的套接字工具。java.io包為您提供對(duì)流進(jìn)行讀寫(xiě)的工具,這是您與TCP套接字通信的唯一途徑。我們給我們的類(lèi)實(shí)例變量以支持對(duì)套接字流的讀寫(xiě)和存儲(chǔ)我們將連接到的遠(yuǎn)程主機(jī)的詳細(xì)信息。我們類(lèi)的構(gòu)造器有兩個(gè)參數(shù):遠(yuǎn)程主機(jī)的IP地址和端口號(hào)各一個(gè),而且構(gòu)造器將它們賦給實(shí)例變量。我們的類(lèi)有一個(gè)main()方法和三個(gè)其它方法。稍后我們將探究這些方法的細(xì)節(jié)。現(xiàn)在您只需知道setUpConnection()將連接到遠(yuǎn)程服務(wù)器,getFile()將向遠(yuǎn)程服務(wù)器請(qǐng)求fileNameToGet的內(nèi)容以及tearDownConnection()將從遠(yuǎn)程服務(wù)器上斷開(kāi)。實(shí)現(xiàn)main()這里我們實(shí)現(xiàn)main()方法,它將創(chuàng)建RemoteFileClient并用它來(lái)獲取遠(yuǎn)程文件的內(nèi)容,然后打印結(jié)果。main()方法用主機(jī)的IP地址和端口號(hào)實(shí)例化一個(gè)新RemoteFileClient(客戶機(jī))。然后,我們告訴客戶機(jī)建立一個(gè)到主機(jī)的連接。接著,我們告訴客戶機(jī)獲取主機(jī)上一個(gè)指定文件的內(nèi)容。最后,我們告訴客戶機(jī)斷開(kāi)它到主機(jī)的連接。我們把文件內(nèi)容打印到控制臺(tái),只是為了證明一切都是按計(jì)劃進(jìn)行的。建立連接這里我們實(shí)現(xiàn)setUpConnection()方法,它將創(chuàng)建我們的Socket并讓我們?cè)L問(wèn)該套接字的流:publicvoidsetUpConnection(){try{Socketclient=newSocket(hostIp,hostPort);socketReader=newBufferedReader(newInputStreamReader(client.getInputStream()));socketWriter=newPrintWriter(client.getOutputStream());}catch(UnknownHostExceptione){System.out.println("Error1settingupsocketconnection:unknownhostat"+hostIp+":"+hostPort);}catch(IOExceptione){System.out.println("Error2settingupsocketconnection:"+e);}}setUpConnection()方法用主機(jī)的IP地址和端口號(hào)創(chuàng)建一個(gè)Socket:Socketclient=newSocket(hostIp,hostPort);我們把Socket的InputStream包裝進(jìn)BufferedReader以使我們能夠讀取流的行。然后,我們把Socket的OutputStream包裝進(jìn)PrintWriter以使我們能夠發(fā)送文件請(qǐng)求到服務(wù)器:socketReader=newBufferedReader(newInputStreamReader(client.getInputStream()));socketWriter=newPrintWriter(client.getOutputStream());請(qǐng)記住我們的客戶機(jī)和服務(wù)器只是來(lái)回傳送字節(jié)??蛻魴C(jī)和服務(wù)器都必須知道另一方即將發(fā)送的是什么以使它們能夠作出適當(dāng)?shù)捻憫?yīng)。在這個(gè)案例中,服務(wù)器知道我們將發(fā)送一條有效的文件路徑。當(dāng)您實(shí)例化一個(gè)Socket時(shí),將拋出UnknownHostException。這里我們不特別處理它,但我們打印一些信息到控制臺(tái)以告訴我們發(fā)生了什么錯(cuò)誤。同樣地,當(dāng)我們?cè)噲D獲取Socket的InputStream或OutputStream時(shí),如果拋出了一個(gè)一般IOException,我們也打印一些信息到控制臺(tái)。與主機(jī)交談這里我們實(shí)現(xiàn)getFile()方法,它將告訴服務(wù)器我們想要什么文件并在服務(wù)器傳回其內(nèi)容時(shí)接收該內(nèi)容。publicStringgetFile(StringfileNameToGet){StringBufferfileLines=newStringBuffer();try{socketWriter.println(fileNameToGet);socketWriter.flush();Stringline=null;while((line=socketReader.readLine())!=null)fileLines.append(line+"\n");}catch(IOExceptione){System.out.println("Errorreadingfromfile:"+fileNameToGet);}returnfileLines.toString();}對(duì)getFile()方法的調(diào)用要求一個(gè)有效的文件路徑String。它首先創(chuàng)建名為fileLines的StringBuffer,fileLines用于存儲(chǔ)我們讀自服務(wù)器上的文件的每一行。StringBufferfileLines=newStringBuffer();在try{}catch{}塊中,我們用PrintWriter把請(qǐng)求發(fā)送到主機(jī),PrintWriter是我們?cè)趧?chuàng)建連接期間建立的。socketWriter.println(fileNameToGet);socketWriter.flush();請(qǐng)注意這里我們是flush()該P(yáng)rintWriter,而不是關(guān)閉它。這迫使數(shù)據(jù)被發(fā)送到服務(wù)器而不關(guān)閉Socket。一旦我們已經(jīng)寫(xiě)到Socket,我們就希望有一些響應(yīng)。我們不得不在Socket的InputStream上等待它,我們通過(guò)在while循環(huán)中調(diào)用BufferedReader上的readLine()來(lái)達(dá)到這個(gè)目的。我們把每一個(gè)返回行附加到fileLinesStringBuffer(帶有一個(gè)換行符以保護(hù)行):Stringline=null;while((line=socketReader.readLine())!=null)fileLines.append(line+"\n");斷開(kāi)連接這里我們實(shí)現(xiàn)tearDownConnection()方法,它將在我們使用完畢連接后負(fù)責(zé)“清除”。tearDownConnection()方法只是分別關(guān)閉我們?cè)赟ocket的InputStream和OutputStream上創(chuàng)建的BufferedReader和PrintWriter。這樣做會(huì)關(guān)閉我們從Socket獲取的底層流,所以我們必須捕捉可能的IOException。總結(jié)一下客戶機(jī)我們的類(lèi)研究完了。在我們繼續(xù)往前討論服務(wù)器端的情況之前,讓我們回顧一下創(chuàng)建和使用Socket的步驟:1.用您想連接的機(jī)器的IP地址和端口實(shí)例化Socket(如有問(wèn)題則拋出Exception)。2.獲取Socket上的流以進(jìn)行讀寫(xiě)。3.把流包裝進(jìn)BufferedReader/PrintWriter的實(shí)例,如果這樣做能使事情更簡(jiǎn)單的話。4.對(duì)Socket進(jìn)行讀寫(xiě)。5.關(guān)閉打開(kāi)的流。5創(chuàng)建服務(wù)器Socket創(chuàng)建RemoteFileServer類(lèi)importjava.io.*;import.*;publicclassRemoteFileServer{intlistenPort;publicRemoteFileServer(intlistenPort){this.listenPort=listenPort;}//允許客戶機(jī)連接到服務(wù)器,等待客戶機(jī)請(qǐng)求publicvoidacceptConnections(){try{ServerSocketserver=newServerSocket(listenPort);SocketincomingConnection=null;while(true){incomingConnection=server.accept();handleConnection(incomingConnection);}}catch(BindExceptione){System.out.println("Unabletobindtoport"+listenPort);}catch(IOExceptione){System.out.println("UnabletoinstantiateaServerSocketonport:"+listenPort);}}//與客戶機(jī)Socket交互以將客戶機(jī)所請(qǐng)求的文件的內(nèi)容發(fā)送到客戶機(jī)publicvoidhandleConnection(SocketincomingConnection){try{OutputStreamoutputToSocket=incomingConnection.getOutputStream();InputStreaminputFromSocket=incomingConnection.getInputStream();BufferedReaderstreamReader=newBufferedReader(newInputStreamReader(inputFromSocket));FileReaderfileReader=newFileReader(newFile(streamReader.readLine()));BufferedReaderbufferedFileReader=newBufferedReader(fileReader);PrintWriterstreamWriter=newPrintWriter(incomingConnection.getOutputStream());Stringline=null;while((line=bufferedFileReader.readLine())!=null){streamWriter.println(line);}fileReader.close();streamWriter.close();streamReader.close();}catch(Exceptione){System.out.println("Errorhandlingaclient:"+e);e.printStackTrace();}}publicstaticvoidmain(Stringargs[]){RemoteFileServerserver=newRemoteFileServer(1001);server.acceptConnections();}}跟客戶機(jī)中一樣,我們首先導(dǎo)入的java.io。接著,我們給我們的類(lèi)一個(gè)實(shí)例變量以保存端口,我們從該端口偵聽(tīng)進(jìn)入的連接。缺省情況下,端口是1001。我們的類(lèi)有一個(gè)main()方法和兩個(gè)其它方法。稍后我們將探究這些方法的細(xì)節(jié)?,F(xiàn)在您只需知道acceptConnections()將允許客戶機(jī)連接到服務(wù)器以及handleConnection()與客戶機(jī)Socket交互以將您所請(qǐng)求的文件的內(nèi)容發(fā)送到客戶機(jī)。實(shí)現(xiàn)main()這里我們實(shí)現(xiàn)main()方法,它將創(chuàng)建RemoteFileServer并告訴它接受連接:服務(wù)器端的main()方法中,我們實(shí)例化一個(gè)新RemoteFileServer,它將在偵聽(tīng)端口(1001)上偵聽(tīng)進(jìn)入的連接請(qǐng)求。然后我們調(diào)用acceptConnections()來(lái)告訴該server進(jìn)行偵聽(tīng)。接受連接這里我們實(shí)現(xiàn)acceptConnections()方法,它將創(chuàng)建一個(gè)ServerSocket并等待連接請(qǐng)求:publicvoidacceptConnections(){try{ServerSocketserver=newServerSocket(listenPort);SocketincomingConnection=null;while(true){incomingConnection=server.accept();handleConnection(incomingConnection);}}catch(BindExceptione){System.out.println("Unabletobindtoport"+listenPort);}catch(IOExceptione){System.out.println("UnabletoinstantiateaServerSocketonport:"+listenPort);}}acceptConnections()用欲偵聽(tīng)的端口號(hào)來(lái)創(chuàng)建ServerSocket。然后我們通過(guò)調(diào)用該ServerSocket的accept()來(lái)告訴它開(kāi)始偵聽(tīng)。accept()方法將造成阻塞直到來(lái)了一個(gè)連接請(qǐng)求。此時(shí),accept()返回一個(gè)新的Socket,這個(gè)Socket綁定到服務(wù)器上一個(gè)隨機(jī)指定的端口,返回的Socket被傳遞給handleConnection()。請(qǐng)注意我們?cè)谝粋€(gè)無(wú)限循環(huán)中處理對(duì)連接的接受。這里不支持任何關(guān)機(jī)。無(wú)論何時(shí)如果您創(chuàng)建了一個(gè)無(wú)法綁定到指定端口(可能是因?yàn)閯e的什么控制了該端口)的ServerSocket,Java代碼都將拋出一個(gè)錯(cuò)誤。所以這里我們必須捕捉可能的BindException。就跟在客戶機(jī)端上時(shí)一樣,我們必須捕捉IOException,當(dāng)我們?cè)噲D在ServerSocket上接受連接時(shí),它就會(huì)被拋出。請(qǐng)注意,您可以通過(guò)用毫秒數(shù)調(diào)用setSoTimeout()來(lái)為accept()調(diào)用設(shè)置超時(shí),以避免實(shí)際長(zhǎng)時(shí)間的等待。調(diào)用setSoTimeout()將使accept()經(jīng)過(guò)指定占用時(shí)間后拋出IOException。處理連接這里我們實(shí)現(xiàn)handleConnection()方法,它將用連接的流來(lái)接收輸入和寫(xiě)輸出:publicvoidhandleConnection(SocketincomingConnection){try{OutputStreamoutputToSocket=incomingConnection.getOutputStream();InputStreaminputFromSocket=incomingConnection.getInputStream();BufferedReaderstreamReader=newBufferedReader(newInputStreamReader(inputFromSocket));FileReaderfileReader=newFileReader(newFile(streamReader.readLine()));BufferedReaderbufferedFileReader=newBufferedReader(fileReader);PrintWriterstreamWriter=newPrintWriter(incomingConnection.getOutputStream());Stringline=null;while((line=bufferedFileReader.readLine())!=null){streamWriter.println(line);}fileReader.close();streamWriter.close();streamReader.close();}catch(Exceptione){System.out.println("Errorhandlingaclient:"+e);e.printStackTrace();}}跟在客戶機(jī)中一樣,我們用getOutputStream()和getInputStream()來(lái)獲取與我們剛創(chuàng)建的Socket相關(guān)聯(lián)的流。跟在客戶機(jī)端一樣,我們把InputStream包裝進(jìn)BufferedReader,把OutputStream包裝進(jìn)PrintWriter。在服務(wù)器端上,我們需要添加一些代碼,用來(lái)讀取目標(biāo)文件和把內(nèi)容逐行發(fā)送到客戶機(jī)。這里是重要的代碼:FileReaderfileReader=newFileReader(newFile(streamReader.readLine()));BufferedReaderbufferedFileReader=newBufferedReader(fileReader);Stringline=null;while((line=bufferedFileReader.readLine())!=null){streamWriter.println(line);}這些代碼值得詳細(xì)解釋。讓我們一點(diǎn)一點(diǎn)來(lái)看:FileReaderfileReader=newFileReader(newFile(streamReader.readLine()));首先,我們使用Socket的InputStream的BufferedReader。我們應(yīng)該獲取一條有效的文件路徑,所以我們用該路徑名構(gòu)造一個(gè)新File。我們創(chuàng)建一個(gè)新FileReader來(lái)處理讀文件的操作。BufferedReaderbufferedFileReader=newBufferedReader(fileReader);這里我們把FileReader包裝進(jìn)BufferedReader以使我們能夠逐行地讀該文件。接著,我們調(diào)用BufferedReader的readLine()。這個(gè)調(diào)用將造成阻塞直到有字節(jié)到來(lái)。我們獲取一些字節(jié)之后就把它們放到本地的line變量中,然后再寫(xiě)出到客戶機(jī)上。完成讀寫(xiě)操作之后,我們就關(guān)閉打開(kāi)的流。請(qǐng)注意我們?cè)谕瓿蓮腟ocket的讀操作之后關(guān)閉streamWriter和streamReader。您或許會(huì)問(wèn)我們?yōu)槭裁床辉谧x取文件名之后立刻關(guān)閉streamReader。原因是當(dāng)您這樣做時(shí),您的客戶機(jī)將不會(huì)獲取任何數(shù)據(jù)。如果您在關(guān)閉streamWriter之前關(guān)閉streamReader,則您可以往Socket寫(xiě)任何東西,但卻沒(méi)有任何數(shù)據(jù)能通過(guò)通道(通道被關(guān)閉了)??偨Y(jié)一下服務(wù)器在我們接著討論另一個(gè)更實(shí)際的示例之前,讓我們回顧一下創(chuàng)建和使用ServerSocket的步驟:1.用一個(gè)您想讓它偵聽(tīng)傳入客戶機(jī)連接的端口來(lái)實(shí)例化一個(gè)ServerSocket(如有問(wèn)題則拋出Exception)。2.調(diào)用ServerSocket的accept()以在等待連接期間造成阻塞。3.獲取位于該底層Socket的流以進(jìn)行讀寫(xiě)操作。4.按使事情簡(jiǎn)單化的原則包裝流。5.對(duì)Socket進(jìn)行讀寫(xiě)。6.關(guān)閉打開(kāi)的流(并請(qǐng)記住,永遠(yuǎn)不要在關(guān)閉Writer之前關(guān)閉Reader)。6創(chuàng)建多線程Socket服務(wù)器前面的示例教給您基礎(chǔ)知識(shí),但并不能令您更深入。如果您到此就停止了,那么您一次只能處理一臺(tái)客戶機(jī)。原因是handleConnection()是一個(gè)阻塞方法。只有當(dāng)它完成了對(duì)當(dāng)前連接的處理時(shí),服務(wù)器才能接受另一個(gè)客戶機(jī)。在多數(shù)時(shí)候,您將需要(也有必要)一個(gè)多線程服務(wù)器。創(chuàng)建MultithreadedRemoteFileServer類(lèi)importjava.io.*;import.*;publicclassMultithreadedRemoteFileServer{intlistenPort;publicMultithreadedRemoteFileServer(intlistenPort){this.listenPort=listenPort;}//允許客戶機(jī)連接到服務(wù)器,等待客戶機(jī)請(qǐng)求publicvoidacceptConnections(){try{ServerSocketserver=newServerSocket(listenPort,5);SocketincomingConnection=null;while(true){incomingConnection=server.accept();handleConnection(incomingConnection);}}catch(BindExceptione){System.out.println("Unabletobindtoport"+listenPort);}catch(IOExceptione){System.out.println("UnabletoinstantiateaServerSocketonport:"+listenPort);}}//與客戶機(jī)Socket交互以將客戶機(jī)所請(qǐng)求的文件的內(nèi)容發(fā)送到客戶機(jī)publicvoidhandleConnection(SocketconnectionToHandle){newThread(newConnectionHandler(connectionToHandle)).start();}publicstaticvoidmain(Stringargs[]){MultithreadedRemoteFileServerserver=newMultithreadedRemoteFileServer(1001);server.acceptConnections();}}這里我們實(shí)現(xiàn)改動(dòng)過(guò)acceptConnections()方法,它將創(chuàng)建一個(gè)能夠處理待發(fā)請(qǐng)求的ServerSocket,并告訴ServerSocket接受連接。新的server仍然需要acceptConnections(),所以這些代碼實(shí)際上是一樣的。突出顯示的行表示一個(gè)重大的不同。對(duì)這個(gè)多線程版,我們現(xiàn)在可以指定客戶機(jī)請(qǐng)求的最大數(shù)目,這些請(qǐng)求都能在實(shí)例化ServerSocket期間處于待發(fā)狀態(tài)。如果我們沒(méi)有指定客戶機(jī)請(qǐng)求的最大數(shù)目,則我們假設(shè)使用缺省值50。這里是它的工作機(jī)制。假設(shè)我們指定待發(fā)數(shù)(backlog值)是5并且有五臺(tái)客戶機(jī)請(qǐng)求連接到我們的服務(wù)器。我們的服務(wù)器將著手處理第一個(gè)連接,但處理該連接需要很長(zhǎng)時(shí)間。由于我們的待發(fā)值是5,所以我們一次可以放五個(gè)請(qǐng)求到隊(duì)列中。我們正在處理一個(gè),所以這意味著還有其它五個(gè)正在等待。等待的和正在處理的一共有六個(gè)。當(dāng)我們的服務(wù)器仍忙于接受一號(hào)連接(記住隊(duì)列中還有2?6號(hào))時(shí),如果有第七個(gè)客戶機(jī)提出連接申請(qǐng),那么,該第七個(gè)客戶機(jī)將遭到拒絕。我們將在帶有連接池服務(wù)器示例中說(shuō)明如何限定能同時(shí)連接的客戶機(jī)數(shù)目。處理連接:publicvoidhandleConnection(SocketconnectionToHandle){newThread(newConnectionHandler(connectionToHandle)).start();}我們對(duì)RemoteFileServer所做的大改動(dòng)就體現(xiàn)在這個(gè)方法上。我們?nèi)匀辉诜?wù)器接受一個(gè)連接之后調(diào)用handleConnection(),但現(xiàn)在我們把該Socket傳遞給ConnectionHandler的一個(gè)實(shí)例,它是Runnable的。我們用ConnectionHandler創(chuàng)建一個(gè)新Thread并啟動(dòng)它。ConnectionHandler的run()方法包Socket讀/寫(xiě)和讀File的代碼,這些代碼原來(lái)在RemoteFileServer的handleConnection()中。創(chuàng)建ConnectionHandler類(lèi)importjava.io.*;import.*;publicclassConnectionHandlerimplementsRunnable{protectedSocketsocketToHandle;publicConnectionHandler(SocketsocketToHandle){this.socketToHandle=socketToHandle;}publicvoidrun(){try{PrintWriterstreamWriter=newPrintWriter(socketToHandle.getOutputStream());BufferedReaderstreamReader=newBufferedReader(newInputStreamReader(socketToHandle.getInputStream()));StringfileToRead=streamReader.readLine();BufferedReaderfileReader=newBufferedReader(newFileReader(fileToRead));Stringline=null;while((line=fileReader.readLine())!=null){streamWriter.println(line);}fileReader.close();streamWriter.close();streamReader.close();}catch(Exceptione){System.out.println("Errorhandlingaclient:"+e);e.printStackTrace();}}}這個(gè)助手類(lèi)相當(dāng)簡(jiǎn)單。跟我們到目前為止的其它類(lèi)一樣,我們導(dǎo)入和java.io。該類(lèi)只有一個(gè)實(shí)例變量socketToHandle,它保存由該實(shí)例處理的Socket。類(lèi)的構(gòu)造器用一個(gè)Socket實(shí)例作參數(shù)并將它賦給socketToHandle。請(qǐng)注意該類(lèi)實(shí)現(xiàn)了Runnable接口。實(shí)現(xiàn)這個(gè)接口的類(lèi)都必須實(shí)現(xiàn)run()方法。這里我們實(shí)現(xiàn)run()方法,它將攫取我們的連接的流,用它來(lái)讀寫(xiě)該連接,并在任務(wù)完成之后關(guān)閉它。ConnectionHandler的run()方法所做的事情就是RemoteFileServer上的handleConnection()所做的事情。首先,我們把InputStream和OutputStream分別包裝(用Socket的getOutputStream()和getInputStream())進(jìn)BufferedReader和PrintWriter。然后我們用這些代碼逐行地讀目標(biāo)文件:PrintWriterstreamWriter=newPrintWriter(socketToHandle.getOutputStream());BufferedReaderstreamReader=newBufferedReader(newInputStreamReader(socketToHandle.getInputStream()));StringfileToRead=streamReader.readLine();BufferedReaderfileReader=newBufferedReader(newFileReader(fileToRead));Stringline=null;while((line=fileReader.readLine())!=null){streamWriter.println(line);}請(qǐng)記住我們應(yīng)該從客戶機(jī)獲取一條有效的文件路徑,這樣用該路徑名構(gòu)造一個(gè)新File,把它包裝進(jìn)FileReader以處理讀文件的操作,然后把它包裝進(jìn)BufferedReader以讓我們逐行地讀該文件。我們while循環(huán)中調(diào)用BufferedReader上的readLine()直到不再有要讀的行。請(qǐng)記注,對(duì)readLine()的調(diào)用將造成阻塞,直到有字節(jié)來(lái)到為止。我們獲取一些字節(jié)之后就把它們放到本地的line變量中,然后寫(xiě)出到客戶機(jī)上。完成讀寫(xiě)操作之后,我們關(guān)閉打開(kāi)的流??偨Y(jié)一下多線程服務(wù)器讓我們回顧一下創(chuàng)建和使用“多線程版”的服務(wù)器的步驟:1.修改acceptConnections()以用缺省為50(或任何您想要的大于1的指定數(shù)字)實(shí)例化ServerSocket。2.修改ServerSocket的handleConnection()以用ConnectionHandler的一個(gè)實(shí)例生成一個(gè)新的Thread。3.借用RemoteFileServer的handleConnection()方法的代碼實(shí)現(xiàn)ConnectionHandler類(lèi)。7創(chuàng)建帶有連接池的Socket服務(wù)器我們現(xiàn)在已經(jīng)擁有的MultithreadedServer每當(dāng)有客戶機(jī)申請(qǐng)一個(gè)連接時(shí)都在一個(gè)新Thread中創(chuàng)建一個(gè)新ConnectionHandler。這意味著可能有一捆Thread“躺”在我們周?chē)?。而且?chuàng)建Thread的系統(tǒng)開(kāi)銷(xiāo)并不是微不足道的。如果性能成為了問(wèn)題(也請(qǐng)不要事到臨頭才意識(shí)到它),更高效地處理我們的服務(wù)器是件好事。那么,我們?nèi)绾胃咝У毓芾矸?wù)器端呢?我們可以維護(hù)一個(gè)進(jìn)入的連接池,一定數(shù)量的ConnectionHandler將為它提供服務(wù)。這種設(shè)計(jì)能帶來(lái)以下好處:?它限定了允許同時(shí)連接的數(shù)目。?我們只需啟動(dòng)ConnectionHandlerThread一次。幸運(yùn)的是,跟在我們的多線程示例中一樣,往代碼中添加“池”不需要來(lái)一個(gè)大改動(dòng)。事實(shí)上,應(yīng)用程序的客戶機(jī)端根本就不受影響。在服務(wù)器端,我們?cè)诜?wù)器啟動(dòng)時(shí)創(chuàng)建一定數(shù)量的ConnectionHandler,我們把進(jìn)入的連接放入“池”中并讓ConnectionHandler打理剩下的事情。這種設(shè)計(jì)中有很多我們不打算討論的可能存在的技巧。例如,我們可以通過(guò)限定允許在“池”中建立的連接的數(shù)目來(lái)拒絕客戶機(jī)。請(qǐng)注意:我們將不會(huì)再次討論acceptConnections()。這個(gè)方法跟前面示例中的完全一樣。它無(wú)限循環(huán)地調(diào)用ServerSocket上的accept()并把連接傳遞到handleConnection()。創(chuàng)建PooledRemoteFileServer類(lèi)importjava.io.*;import.*;importjava.util.*;publicclassPooledRemoteFileServer{protectedintmaxConnections;protectedintlistenPort;protectedServerSocketserverSocket;publicPooledRemoteFileServer(intaListenPort,intmaxConnections){listenPort=aListenPort;this.maxConnections=maxConnections;}publicvoidacceptConnections(){try{ServerSocketserver=newServerSocket(listenPort,5);SocketincomingConnection=null;while(true){incomingConnection=server.accept();handleConnection(incomingConnection);}}catch(BindExceptione){System.out.println("");}catch(IOExceptione){System.out.println(""+listenPort);}}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.setUpHandlers();server.acceptConnections();}}請(qǐng)注意一下您現(xiàn)在應(yīng)該熟悉了的import語(yǔ)句。我們給類(lèi)以下實(shí)例變量以保存:?我們的服務(wù)器能同時(shí)處理的活動(dòng)客戶機(jī)連接的最大數(shù)目?進(jìn)入的連接的偵聽(tīng)端口(我們沒(méi)有指定缺省值,但如果您想這樣做,并不會(huì)受到限制)?將接受客戶機(jī)連接請(qǐng)求的ServerSocket類(lèi)的構(gòu)造器用的參數(shù)是偵聽(tīng)端口和連接的最大數(shù)目我們的類(lèi)有一個(gè)main()方法和三個(gè)其它方法。稍后我們將探究這些方法的細(xì)節(jié)?,F(xiàn)在只須知道setUpHandlers()創(chuàng)建數(shù)目為maxConnections的大量PooledConnectionHandler,而其它兩個(gè)方法則與我們前面已經(jīng)看到的相似:acceptConnections()在ServerSocket上偵聽(tīng)傳入的客戶機(jī)連接,而handleConnection則在客戶機(jī)連接一旦被建立后就實(shí)際處理它。實(shí)現(xiàn)main()這里我們實(shí)現(xiàn)需作改動(dòng)的main()方法,該方法將創(chuàng)建能夠處理給定數(shù)目的客戶機(jī)連接的PooledRemoteFileServer,并告訴它接受連接:publicstaticvoidmain(Stringargs[]){PooledRemoteFileServerserver=newPooledRemoteFileServer(1001,3);server.setUpHandlers();server.acceptConnections();}我們的main()方法很簡(jiǎn)單。我們實(shí)例化一個(gè)新的PooledRemoteFileServer,它將通過(guò)調(diào)用setUpHandlers()來(lái)建立三個(gè)PooledConnectionHandler。一旦服務(wù)器就緒,我們就告訴它acceptConnections()。建立連接處理程序publicvoidsetUpHandlers(){for(inti=0;i<maxConnections;i++){PooledConnectionHandlercurrentHandler=newPooledConnectionHandler();newThread(currentHandler,"Handler"+i).start();}}setUpHandlers()方法創(chuàng)建maxConnections(例如3)個(gè)PooledConnectionHandler并在新Thread中激活它們。用實(shí)現(xiàn)了Runnable的對(duì)象來(lái)創(chuàng)建Thread使我們可以在Thread調(diào)用start()并且可以期望在Runnable上調(diào)用了run()。換句話說(shuō),我們的PooledConnectionHandler將等著處理進(jìn)入的連接,每個(gè)都在它自己的Thread中進(jìn)行。我們?cè)谑纠兄粍?chuàng)建三個(gè)Thread,而且一旦服務(wù)器運(yùn)行,這就不能被改變。處理連接這里我們實(shí)現(xiàn)需作改動(dòng)的handleConnections()方法,它將委派PooledConnectionHandler處理連接:protectedvoidhandleConnection(SocketconnectionToHandle){PooledConnectionHcessRequest(connectionToHandle);}我們現(xiàn)在叫PooledConnectionHandler處理所有進(jìn)入的連接(processRequest()是一個(gè)靜態(tài)方法)。創(chuàng)建PooledRemoteFileServer類(lèi)importjava.io.*;import.*;importjava.util.*;publicclassPooledConnectionHandlerimplementsRunnable{protectedSocketconnection;protectedstaticListpool=newLinkedList();publicPooledConnectionHandler(){}publicvoidhandleConnection(){try{PrintWriterstreamWriter=newPrintWriter(connection.getOutputStream());BufferedReaderstreamReader=newBufferedReader(newInputStreamReader(connection.getInputStream()));StringfileToRead=streamReader.readLine();BufferedReaderfileReader=newBufferedReader(newFileReader(fileToRead));Stringline=null;while((line=fileReader.readLine())!=null)streamWriter.println(line);fileReader.close();streamWriter.close();streamReader.close();}catch(FileNotFoundExceptione){System.out.println("");}catch(IOExceptione){System.out.println(""+e);}}publicstaticvoidprocessRequest(SocketrequestToHandle){synchronized(pool){pool.add(pool.size(),requestToHandle);pool.notifyAll();}}publicvoidrun(){while(true){synchronized(pool){while(pool.isEmpty()){try{pool.wait();}catch(InterruptedExceptione){e.printStackTrace();}}connection=(Socket)pool.remove(0);}handleConnection();}}}這個(gè)助手類(lèi)與ConnectionHandler非常相似,但它帶有處理連接池的手段。該類(lèi)有兩個(gè)實(shí)例變量:?connection是當(dāng)前正在處理的Socket?名為pool的靜態(tài)LinkedList保存需被處理的連接填充連接池這里我們實(shí)現(xiàn)PooledConnectionHandler上的processRequest()方法,它將把傳入請(qǐng)求添加到池中,并告訴其它正在等待的對(duì)象該池已經(jīng)有一些內(nèi)容:publicstaticvoidprocessRequest(SocketrequestToHandle){synchronized(pool){pool.add(pool.size(),requestToHandle);pool.notifyAll();}}synchronized塊是個(gè)稍微有些不同的東西。您可以同步任何對(duì)象上的一個(gè)塊,而不只是在本身的某個(gè)方法中含有該塊的對(duì)象。在我們的示例中,processRequest()方法包含有一個(gè)pool(請(qǐng)記住它是一個(gè)LinkedList,保存等待處理的連接池)的synchronized塊。我們這樣做的原因是確保沒(méi)有別人能跟我們同時(shí)修改連接池。既然我們已經(jīng)保證了我們是唯一“涉水”池中的人,我們就可以把傳入的Socket添加到LinkedList的尾端。一旦我們添加了新的連接,我們就用以下代碼通知其它正在等待該池的Thread,池現(xiàn)在已經(jīng)可用:pool.notifyAll();Object的所有子類(lèi)都繼承這個(gè)notifyAll()方法。這個(gè)方法,連同我們下一屏將要討論的wait()方法一起,就使一個(gè)Thread能夠讓另一個(gè)Thread知道一些條件已經(jīng)具備。這意味著該第二個(gè)Thread一定正在等待那些條件的滿足。從池中獲取連接這里我們實(shí)現(xiàn)PooledConnectionHandler上需作改動(dòng)的run()方法,它將在連接池上等待,并且池中一有連接就處理它:publicvoidrun(){while(true){synchronized(pool){while(pool.isEmpty()){try{pool.wait();}catch(InterruptedExceptione){e.printStackTrace();}}connection=(Socket)pool.remove(0);}handleConnection();}}回想一下在前面講過(guò)的:一個(gè)Thread正在等待有人通知它連接池方面的條件已經(jīng)滿足了。在我們的示例中,請(qǐng)記住我們有三個(gè)PooledConnectionHandler在等待使用池中的連接。每個(gè)PooledConnectionHandler都在它自已的Thread中運(yùn)行,并通過(guò)調(diào)用pool.wait()產(chǎn)生阻塞。當(dāng)我們的processRequest()在連接池上調(diào)用notifyAll()時(shí),所有正在等待的PooledConnectionHandler都將得到“池已經(jīng)可用”的通知。然后各自繼續(xù)前行調(diào)用pool.wait(),并重新檢查while(pool.isEmpty())循環(huán)條件。除了一個(gè)處理程序,其它池對(duì)所有處理程序都將是空的,因此,在調(diào)用pool.wait()時(shí),除了一個(gè)處理程序,其它所有處理程序都將再次產(chǎn)生阻塞。恰巧碰上非空池的處理程序?qū)⑻鰓hile(pool.isEmpty())循環(huán)并攫取池中的第一個(gè)連接:connection=(Socket)pool.remove(0);處理程序一旦有一個(gè)連接可以使用,就調(diào)用handleConnection()處理它。在我們的示例中,池中可能永遠(yuǎn)不會(huì)有多個(gè)連接,只是因?yàn)槭虑楹芸炀捅惶幚淼袅?。如果池中有一個(gè)以上連接,那么其它處理程序?qū)⒉槐氐却碌倪B接被添加到池。當(dāng)它們檢查pool.isEmp
溫馨提示
- 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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五農(nóng)村宅基地買(mǎi)賣(mài)及農(nóng)村住房改造合同
- 2025年度離婚同撫養(yǎng)協(xié)議及子女學(xué)業(yè)獎(jiǎng)勵(lì)基金
- 二零二五年度高端住宅小區(qū)門(mén)衛(wèi)免責(zé)合同
- 二零二五年度充電樁充電設(shè)備供應(yīng)商與安裝方合作協(xié)議中的售后服務(wù)標(biāo)準(zhǔn)
- 二零二五年度夫妻財(cái)產(chǎn)共有及婚姻生活協(xié)議
- 2025年度環(huán)保型廠房建設(shè)施工合作協(xié)議
- 2025年度海量城市交通樞紐拆除搬遷協(xié)議
- 二零二五年度企業(yè)員工入職與培訓(xùn)考核合同
- 二零二五年度租賃住宅安全事故責(zé)任劃分與賠償合同
- 二零二五年度中式快餐連鎖加盟運(yùn)營(yíng)合作協(xié)議
- 參保人員轉(zhuǎn)診就醫(yī)審核意見(jiàn)單
- 動(dòng)物免疫接種技術(shù)課件
- 大班健康《換牙我不怕》課件
- 93年國(guó)際大專(zhuān)辯論賽經(jīng)典辯詞
- 凍豬肉儲(chǔ)備投標(biāo)方案
- 幼兒園大班繪本故事-神奇的大蒜【幼兒教案】
- GB/T 17639-2023土工合成材料長(zhǎng)絲紡粘針刺非織造土工布
- 2023年廣東省深圳市龍華區(qū)中考道德與法治二模試卷及答案解析
- 舟山國(guó)儲(chǔ)基地?cái)U(kuò)建項(xiàng)目開(kāi)山回填與隧道工程爆破項(xiàng)目設(shè)計(jì)方案
- 信用修復(fù)申請(qǐng)文書(shū)(當(dāng)事人適用)
- 2023年新改版教科版六年級(jí)下冊(cè)科學(xué)全冊(cè)教案(新課標(biāo))
評(píng)論
0/150
提交評(píng)論