![用javasocket開發(fā)高并發(fā)小型服務(wù)器_第1頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/8/bc02912b-af12-4a61-b66a-004e13263662/bc02912b-af12-4a61-b66a-004e132636621.gif)
![用javasocket開發(fā)高并發(fā)小型服務(wù)器_第2頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/8/bc02912b-af12-4a61-b66a-004e13263662/bc02912b-af12-4a61-b66a-004e132636622.gif)
![用javasocket開發(fā)高并發(fā)小型服務(wù)器_第3頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/8/bc02912b-af12-4a61-b66a-004e13263662/bc02912b-af12-4a61-b66a-004e132636623.gif)
![用javasocket開發(fā)高并發(fā)小型服務(wù)器_第4頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/8/bc02912b-af12-4a61-b66a-004e13263662/bc02912b-af12-4a61-b66a-004e132636624.gif)
![用javasocket開發(fā)高并發(fā)小型服務(wù)器_第5頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/8/bc02912b-af12-4a61-b66a-004e13263662/bc02912b-af12-4a61-b66a-004e132636625.gif)
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、用JavaSocket開發(fā)高并發(fā)小型效勞器JavaSocket套接字socket為兩臺(tái)計(jì)算機(jī)之間的通信提供了一種機(jī)制,在JamesGosling注意到Java語言之前,套接字就早已赫赫有名。該語言只是讓您不必理解底層操作系統(tǒng)的細(xì)節(jié)就能有效地使用套接字。1 客戶機(jī)/效勞器模型在飯店里,菜單上各種具有異國情調(diào)的食品映入你的眼簾,于是你要了一份pizza。幾分鐘后,你用力咀嚼澆著融化的乳酪和其他你喜歡的配料的熱pizza。你不知道,也不想知道:侍者從那里弄來了pizza,在制作過程中加進(jìn)了什么,以及配料是如何獲得的。上例中包含的實(shí)體有:美味的pizza、承受你定餐的侍者、制作pizza的廚房,當(dāng)然還
2、有你。你是定pizza的顧客或客戶。制作pizza的過程對(duì)于你而言是被封裝的。你的懇求在廚房中被處理,pizza制作完成后,由侍者端給你。你所看到的就是一個(gè)客戶機(jī)/效勞器模型??蛻魴C(jī)向效勞器發(fā)送一個(gè)懇求或命令。效勞器處理客戶機(jī)的懇求??蛻魴C(jī)和效勞器之間的通訊是客戶機(jī)/效勞器模型中的一個(gè)重要組成部分,通常通過網(wǎng)絡(luò)進(jìn)展??蛻魴C(jī)/效勞器模型是一個(gè)應(yīng)用程序開發(fā)框架,該框架是為了將數(shù)據(jù)的表示與其內(nèi)部的處理和存儲(chǔ)別分開來而設(shè)計(jì)的。客戶機(jī)懇求效勞,效勞器為這些懇求效勞。懇求通過網(wǎng)絡(luò)從客戶機(jī)傳遞到效勞器。效勞器所進(jìn)展的處理對(duì)客戶機(jī)而言是隱藏的。一個(gè)效勞器可以為多臺(tái)客戶機(jī)效勞。多臺(tái)客戶機(jī)訪問效勞器效勞器和客戶機(jī)
3、不一定是硬件組件。它們可以是工作啊同一機(jī)器或不同機(jī)器上的程序。、考慮一個(gè)航空定票系統(tǒng)中的數(shù)據(jù)輸入程序:數(shù)據(jù)乘客名、航班號(hào)、飛行日期、目的地等可以被輸入到前端客戶機(jī)的應(yīng)用程序中。一旦數(shù)據(jù)輸入之后,客戶機(jī)將數(shù)據(jù)發(fā)送到后端效勞器端。效勞器處理數(shù)據(jù)并在數(shù)據(jù)庫中保存數(shù)據(jù)??蛻魴C(jī)/效勞器模型的重要性在于所有的數(shù)據(jù)都存放在同一地點(diǎn)??蛻魴C(jī)從不同的地方訪問同一數(shù)據(jù)源,效勞器對(duì)所有的輸入數(shù)據(jù)應(yīng)用同樣的檢驗(yàn)規(guī)那么。萬維網(wǎng)為為什么要將數(shù)據(jù)的表示與其存儲(chǔ)、處理別分開來提供了一個(gè)很好的例子。在Web上,你無需控制最終用戶用來訪問你數(shù)據(jù)的平臺(tái)和軟件。你可以考慮編寫出適用與每一種潛在的目的平臺(tái)的應(yīng)用程序。客戶機(jī)/效勞器應(yīng)用
4、程序的效勞器部分管理通過多個(gè)客戶機(jī)訪問效勞器的、多個(gè)用戶共享的資源。說明客戶機(jī)/效勞器程序的效勞器部分強(qiáng)大功能的最好例子應(yīng)該是Web效勞器,它通過Internet將HTML頁傳遞給不同的Web用戶。Java編程語言中最根本的特點(diǎn)是在Java中創(chuàng)立的程序的代碼的可移植性。因?yàn)榫哂衅渌Z言所不具備的代碼可移植性,Java允許用戶只要編寫一次應(yīng)用程序,就可以在任何客戶機(jī)系統(tǒng)上發(fā)布它,并可以讓客戶機(jī)系統(tǒng)解釋該程序。這意味著:你只要寫一次代碼,就能使其在任何平臺(tái)上運(yùn)行。2 協(xié)議當(dāng)你同朋友交談時(shí),你們遵循一些暗含的規(guī)那么或協(xié)議。例如:你們倆不能同時(shí)開始說話,或連續(xù)不連續(xù)地說話。假設(shè)你們這樣作的話,誰也不能
5、理解對(duì)方所說的東西。當(dāng)你說話時(shí),你的朋友傾聽,反之亦然。你們以雙方都能理解的語言和速度進(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ī)那么被稱為網(wǎng)絡(luò)協(xié)議。網(wǎng)絡(luò)協(xié)議是通過網(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ò)的類庫,這些類使得應(yīng)用程序能方便地訪問網(wǎng)絡(luò)資源。Java提供了兩種通訊工具。它們是:使用用戶報(bào)文協(xié)議UDP的報(bào)
6、文和使用傳輸控制協(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)并不是可信賴的。因此,僅當(dāng)傳送少量數(shù)據(jù)時(shí)才使用,而且發(fā)送者和承受者之間的間隔間隔不大,假設(shè)是網(wǎng)絡(luò)交通頂峰,或承受程序正處理來自其他程序的多個(gè)懇求,就有時(shí)機(jī)出現(xiàn)數(shù)據(jù)報(bào)包的喪失。Sockets套接字用TCP來進(jìn)展通訊。套接字模型同其他模型相比,優(yōu)越性在于其不受客戶懇求來自何處的影響。只要客戶機(jī)遵循TCP/IP協(xié)議,效勞器就會(huì)對(duì)它的懇求提供效勞。這意味著客戶機(jī)可以是任何類型的計(jì)算機(jī)??蛻魴C(jī)不再局限為UNIX、W
7、indows、DOS或Macintosh平臺(tái),因此,網(wǎng)上所有遵循TCP/IP協(xié)議的計(jì)算機(jī)可以通過套接字互相通訊。3 Sockets套接字3.1 Sockets概況在客戶機(jī)/效勞器應(yīng)用程序中,效勞器提供象處理數(shù)據(jù)庫查詢或修改數(shù)據(jù)庫中的數(shù)據(jù)之類的效勞。發(fā)生在客戶機(jī)和效勞器之間的通訊必須是可靠的,同時(shí)數(shù)據(jù)在客戶機(jī)上的次序應(yīng)該和效勞器發(fā)送出來的次序一樣。什么是套接字?既然我們已經(jīng)知道套接字扮演的角色,那么剩下的問題是:什么是套接字?BruceEckel在他的?Java編程思想?一書中這樣描繪套接字:套接字是一種軟件抽象,用于表達(dá)兩臺(tái)機(jī)器之間的連接“終端。對(duì)于一個(gè)給定的連接,每臺(tái)機(jī)器上都有一個(gè)套接字,您
8、也可以想象它們之間有一條虛擬的“電纜,“電纜的每一端都插入到套接字中。當(dāng)然,機(jī)器之間的物理硬件和電纜連接都是完全未知的。抽象的全部目的是使我們無須知道不必知道的細(xì)節(jié)。簡言之,一臺(tái)機(jī)器上的套接字與另一臺(tái)機(jī)器上的套接字交談就創(chuàng)立一條通信通道。程序員可以用該通道來在兩臺(tái)機(jī)器之間發(fā)送數(shù)據(jù)。當(dāng)您發(fā)送數(shù)據(jù)時(shí),TCP/IP協(xié)議棧的每一層都會(huì)添加適當(dāng)?shù)膱?bào)頭信息來包裝數(shù)據(jù)。這些報(bào)頭幫助協(xié)議棧把您的數(shù)據(jù)送到目的地。好消息是Java語言通過"流"為您的代碼提供數(shù)據(jù),從而隱藏了所有這些細(xì)節(jié),這也是為什么它們有時(shí)候被叫做流套接字streamingsocket的原因。把套接字想成兩端上的聽筒,我和您通
9、過專用通道在我們的聽筒上講話和聆聽。直到我們決定掛斷,對(duì)話才會(huì)完畢除非我們?cè)谑褂梅涓C。而且我們各自的線路都占線,直到我們掛斷。假設(shè)想在沒有更高級(jí)機(jī)制如ORB以及CORBA、RMI、IIOP等等開銷的情況下進(jìn)展兩臺(tái)計(jì)算機(jī)之間的通信,那么套接字就適宜您。套接字的低級(jí)細(xì)節(jié)相當(dāng)棘手。幸運(yùn)的是,Java平臺(tái)給了您一些雖然簡單但卻強(qiáng)大的更高級(jí)抽象,使您可以容易地創(chuàng)立和使用套接字。傳輸控制協(xié)議TCP提供了一條可靠的、點(diǎn)對(duì)點(diǎn)的通訊通道,客戶機(jī)/效勞器應(yīng)用程序可要通過TCP進(jìn)展通訊,客戶機(jī)和效勞器程序建立連接并綁定套接字。套接字用于處理通過網(wǎng)絡(luò)連接的應(yīng)用程序之間的通訊。客戶機(jī)和效勞器之間更深化的通訊通過套接字完
10、成。Java被設(shè)計(jì)成一種連網(wǎng)語言。它通過將連接功能封裝到套接字類里而使得網(wǎng)絡(luò)編程更加容易。套接字類即Socket類它創(chuàng)立一個(gè)客戶套接字和ServerSocket類它創(chuàng)立一個(gè)效勞器套接字。套接字類大致介紹如下:lSocket是基類,它支持TCP協(xié)議。TCP是一個(gè)可靠的流網(wǎng)絡(luò)連接協(xié)議。Socket類提供了流輸入/輸出的方法,使得從套接字中讀出數(shù)據(jù)和往套接字中寫數(shù)據(jù)都很容易。該類對(duì)于編寫因特網(wǎng)上的通訊程序而言是必不可少的。lServerSocket是一個(gè)因特網(wǎng)效勞程序用來監(jiān)聽客戶懇求的類。ServerSocket實(shí)際上并不執(zhí)行效勞;而是創(chuàng)立了一個(gè)Socket對(duì)象來代表客戶機(jī)。通訊由創(chuàng)立的對(duì)象來完成。
11、3.2 IP地址和端口因特網(wǎng)效勞器可以被認(rèn)為是一組套接字類,它們提供了一般稱為效勞的附加功能。效勞的例子有:電子郵件、遠(yuǎn)程登錄的Telnet、和通過網(wǎng)絡(luò)傳輸文件的文件傳輸協(xié)議FTP。每種效勞都與一個(gè)端口相聯(lián)絡(luò)。端口是一個(gè)數(shù)值地址,通過它來處理效勞懇求就象懇求Web頁一樣。TCP協(xié)議需要兩個(gè)數(shù)據(jù)項(xiàng):IP地址和端口號(hào)。因此,當(dāng)鍵入時(shí),你是如何進(jìn)入金諾的主頁呢?因特網(wǎng)協(xié)議IP提供每一項(xiàng)網(wǎng)絡(luò)設(shè)備。這些設(shè)備都帶有一個(gè)稱為IP地址的邏輯地址。由因特網(wǎng)協(xié)議提供的IP地址具有特定的形式。每個(gè)IP地址都是32位的數(shù)值,表示4個(gè)范圍在0到255之間的8位數(shù)值金諾已經(jīng)注冊(cè)了它的名字,分配給注意:域名效勞或DNS效勞
12、是將。假設(shè)沒有指明端口號(hào),那么使用效勞文件中效勞器的端口。每種協(xié)議有一個(gè)缺省的端口號(hào),在端口號(hào)未指明時(shí)使用該缺省端口號(hào)。端口號(hào)應(yīng)用21 FTP.傳輸文件23 Telnet.提供遠(yuǎn)程登錄25 SMTP.傳遞郵件信息67 BOOTP.在啟動(dòng)時(shí)提供配置情況80. 傳輸 Web 頁109POP.使用戶能訪問遠(yuǎn)程系統(tǒng)中的郵箱讓我們?cè)賮砜匆幌耈RL:URL的第一部分意味著你正在使用超文本傳輸協(xié)議,該協(xié)議處理Web文檔。假設(shè)沒有指明文件,大多數(shù)的Web效勞器會(huì)取一個(gè)叫index.html文件。因此,IP地址和端口既可以通過明確指出URL各部分來決定,也可以由缺省值決定。4創(chuàng)立Socket客戶我們將在本部分討
13、論的例如將說明在Java代碼中如何使用Socket和ServerSocket??蛻魴C(jī)用Socket連接到效勞器。效勞器用ServerSocket在端口1001偵聽。客戶機(jī)懇求效勞器C:驅(qū)動(dòng)器上的文件內(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();/連接到遠(yuǎn)程效勞器publicvoidsetUpConnection()trySocketclient=newSocket(hostIp,hostPort);socketReader=newBufferedReader(newInputStreamReader(client.getInputStream();socketWriter=newPrintWriter(client.getOutputStream();catch(UnknownHostExceptione)catch(IOException
16、e)/斷開遠(yuǎn)程效勞器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包為您提供對(duì)流進(jìn)展讀寫的工具,這是您與TCP套接字通信的唯一途徑。我們給我們的類實(shí)例變量以支持對(duì)套接字流的讀寫和存儲(chǔ)我們將連接到的遠(yuǎn)程主機(jī)的詳細(xì)信息。我們類的構(gòu)造器有兩個(gè)參數(shù):遠(yuǎn)程主機(jī)的IP地址和端口號(hào)各一個(gè),而且構(gòu)造器將它們賦給實(shí)例變量。我們的類有一個(gè)main()方法和三個(gè)其它方法。稍后我們將探究這些方法的細(xì)節(jié)。如今您只需知道setUpConnection()將連接到遠(yuǎn)程效勞器,getFile()將向遠(yuǎn)程效勞器懇求fileNameToGet的內(nèi)容以及t
18、earDownConnection()將從遠(yuǎn)程效勞器上斷開。實(shí)現(xiàn)main()這里我們實(shí)現(xiàn)main()方法,它將創(chuàng)立RemoteFileClient并用它來獲取遠(yuǎn)程文件的內(nèi)容,然后打印結(jié)果。main()方法用主機(jī)的IP地址和端口號(hào)實(shí)例化一個(gè)新RemoteFileClient客戶機(jī)。然后,我們告訴客戶機(jī)建立一個(gè)到主機(jī)的連接。接著,我們告訴客戶機(jī)獲取主機(jī)上一個(gè)指定文件的內(nèi)容。最后,我們告訴客戶機(jī)斷開它到主機(jī)的連接。我們把文件內(nèi)容打印到控制臺(tái),只是為了證明一切都是按方案進(jìn)展的。建立連接這里我們實(shí)現(xiàn)setUpConnection()方法,它將創(chuàng)立我們的Socket并讓我們?cè)L問該套接字的流:publicv
19、oidsetUpConnection()trySocketclient=newSocket(hostIp,hostPort);socketReader=newBufferedReader(newInputStreamReader(client.getInputStream();socketWriter=newPrintWriter(client.getOutputStream();catch(UnknownHostExceptione)catch(IOExceptione)setUpConnection()方法用主機(jī)的IP地址和端口號(hào)創(chuàng)立一個(gè)Socket:Socketclient=newSoc
20、ket(hostIp,hostPort);我們把Socket的InputStream包裝進(jìn)BufferedReader以使我們可以讀取流的行。然后,我們把Socket的OutputStream包裝進(jìn)PrintWriter以使我們可以發(fā)送文件懇求到效勞器:socketReader=newBufferedReader(newInputStreamReader(client.getInputStream();socketWriter=newPrintWriter(client.getOutputStream();客戶機(jī)和效勞器都必須知道另一方即將發(fā)請(qǐng)記住我們的客戶機(jī)和效勞器只是來回傳送字節(jié)。送的是什
21、么以使它們可以作出適當(dāng)?shù)捻憫?yīng)。在這個(gè)案例中,效勞器知道我們將發(fā)送一條有效的文件途徑。當(dāng)您實(shí)例化一個(gè)Socket時(shí),將拋出UnknownHostException。這里我們不特別處理它,但我們打印一些信息到控制臺(tái)以告訴我們發(fā)生了什么錯(cuò)誤。同樣地,當(dāng)我們?cè)噲D獲取Socket的InputStream或OutputStream時(shí),假設(shè)拋出了一個(gè)一般IOException,我們也打印一些信息到控制臺(tái)。與主機(jī)交談這里我們實(shí)現(xiàn)getFile()方法,它將告訴效勞器我們想要什么文件并在效勞器傳回其內(nèi)容時(shí)接收該內(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();對(duì)getFile()方法的調(diào)用要求一個(gè)有效的文件途徑String。它首先創(chuàng)立名為fileLines的StringBuffer,fileL
23、ines用于存儲(chǔ)我們讀自效勞器上的文件的每一行。StringBufferfileLines=newStringBuffer();在trycatch塊中,我們用PrintWriter把懇求發(fā)送到主機(jī),PrintWriter是我們?cè)趧?chuàng)立連接期間建立的。socketWriter.flush();socketWriter.println(fileNameToGet);請(qǐng)注意這里我們是flush()該P(yáng)rintWriter,而不是關(guān)閉它。這迫使數(shù)據(jù)被發(fā)送到效勞器而不關(guān)閉Socket。一旦我們已經(jīng)寫到Socket,我們就希望有一些響應(yīng)。我們不得不在Socket的InputStream上等待它,我們通過在w
24、hile循環(huán)中調(diào)用BufferedReader上的readLine()來到達(dá)這個(gè)目的。我們把每一個(gè)返回行附加到fileLinesStringBuffer帶有一個(gè)換行符以保護(hù)行:Stringline=null;while(line=socketReader.readLine()!=null)fileLines.append(line+"n");斷開連接這里我們實(shí)現(xiàn)tearDownConnection()方法,它將在我們使用完畢連接后負(fù)責(zé)“去除。tearDownConnection()方法只是分別關(guān)閉我們?cè)赟ocket的InputStream和OutputStream上創(chuàng)立的B
25、ufferedReader和PrintWriter。這樣做會(huì)關(guān)閉我們從Socket獲取的底層流,所以我們必須捕捉可能的IOException??偨Y(jié)一下客戶機(jī)我們的類研究完了。在我們繼續(xù)往前討論效勞器端的情況之前,讓我們回憶一下創(chuàng)立和使用Socket的步驟:1. 用您想連接的機(jī)器的IP地址和端口實(shí)例化Socket如有問題那么拋出Exception。2. 獲取Socket上的流以進(jìn)展讀寫。3. 把流包裝進(jìn)BufferedReader/PrintWriter的實(shí)例,假設(shè)這樣做能使事情更簡單的話。4. 對(duì)Socket進(jìn)展讀寫。5. 關(guān)閉翻開的流。5創(chuàng)立效勞器Socket創(chuàng)立RemoteFileServ
26、er類publicclassRemoteFileServerintlistenPort;publicRemoteFileServer(intlistenPort)this.listenPort=listenPort;/允許客戶機(jī)連接到效勞器,等待客戶機(jī)懇求publicvoidacceptConnections()tryServerSocketserver=newServerSocket(listenPort);SocketincomingConnection=null;while(true)incomingConnection=server.accept();handleConnection(
27、incomingConnection);catch(BindExceptione)catch(IOExceptione)/與客戶機(jī)Socket交互以將客戶機(jī)所懇求的文件的內(nèi)容發(fā)送到客戶機(jī)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();我們的類有一個(gè)main()方法和兩個(gè)其它方法。稍后我們將探究這些方法的細(xì)節(jié)。如今
30、您只需知道acceptConnections()將允許客戶機(jī)連接到效勞器以及handleConnection()與客戶機(jī)Socket交互以將您所懇求的文件的內(nèi)容發(fā)送到客戶機(jī)。實(shí)現(xiàn)main()這里我們實(shí)現(xiàn)main()方法,它將創(chuàng)立RemoteFileServer并告訴它承受連接:效勞器端的main()方法中,我們實(shí)例化一個(gè)新RemoteFileServer,它將在偵聽端口1001上偵聽進(jìn)入的連接懇求。然后我們調(diào)用acceptConnections()來告訴該server進(jìn)展偵聽。承受連接這里我們實(shí)現(xiàn)acceptConnections()方法,它將創(chuàng)立一個(gè)ServerSocket并等待連接懇求:p
31、ublicvoidacceptConnections()tryServerSocketserver=newServerSocket(listenPort);SocketincomingConnection=null;while(true)incomingConnection=server.accept();handleConnection(incomingConnection);catch(BindExceptione)catch(IOExceptione)acceptConnections()用欲偵聽的端口號(hào)來創(chuàng)立ServerSocket。然后我們通過調(diào)用該ServerSocket的acce
32、pt()來告訴它開始偵聽。accept()方法將造成阻塞直到來了一個(gè)連接懇求。此時(shí),accept()返回一個(gè)新的Socket,這個(gè)Socket綁定到效勞器上一個(gè)隨機(jī)指定的端口,返回的Socket被傳遞給handleConnection()。請(qǐng)注意我們?cè)谝粋€(gè)無限循環(huán)中處理對(duì)連接的承受。這里不支持任何關(guān)機(jī)。無論何時(shí)假設(shè)您創(chuàng)立了一個(gè)無法綁定到指定端口可能是因?yàn)閯e的什么控制了該端口的ServerSocket,Java代碼都將拋出一個(gè)錯(cuò)誤。所以這里我們必須捕捉可能的BindException就跟在客戶機(jī)端上時(shí)一樣,我們必須捕捉IOException,當(dāng)我們?cè)噲D在ServerSocket上承受連接時(shí),它就
33、會(huì)被拋出。請(qǐng)注意,您可以通過用毫秒數(shù)調(diào)用setSoTimeout()來為accept()調(diào)用設(shè)置超時(shí),以防止實(shí)際長時(shí)間的等待。調(diào)用setSoTimeout()將使accept()經(jīng)過指定占用時(shí)間后拋出IOException。處理連接這里我們實(shí)現(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();跟在客戶機(jī)中一樣,我們用getOutputStream()和getInputStream()來獲取與我們剛創(chuàng)立的Socket相關(guān)聯(lián)的流。跟在客戶機(jī)端一樣,我們把I
36、nputStream包裝進(jìn)BufferedReader,把OutputStream包裝進(jìn)PrintWriter。在效勞器端上,我們需要添加一些代碼,用來讀取目的文件和把內(nèi)容逐行發(fā)送到客戶機(jī)。這里是重要的代碼:FileReaderfileReader=newFileReader(newFile(streamReader.readLine();BufferedReaderbufferedFileReader=newBufferedReader(fileReader);Stringline=null;while(line=bufferedFileReader.readLine()!=null)str
37、eamWriter.println(line);這些代碼值得詳細(xì)解釋。讓我們一點(diǎn)一點(diǎn)來看:FileReaderfileReader=newFileReader(newFile(streamReader.readLine();首先,我們使用Socket的InputStream的BufferedReader。我們應(yīng)該獲取一條有效的文件途徑,所以我們用該途徑名構(gòu)造一個(gè)新File。我們創(chuàng)立一個(gè)新FileReader來處理讀文件的操作。BufferedReaderbufferedFileReader=newBufferedReader(fileReader);這里我們把FileReader包裝進(jìn)Buff
38、eredReader以使我們可以逐行地讀該文件。接著,我們調(diào)用BufferedReader的readLine()。這個(gè)調(diào)用將造成阻塞直到有字節(jié)到來。我們獲取一些字節(jié)之后就把它們放到本地的line變量中,然后再寫出到客戶機(jī)上。完成讀寫操作之后,我們就關(guān)閉翻開的流。請(qǐng)注意我們?cè)谕瓿蓮腟ocket的讀操作之后關(guān)閉streamWriter和streamReader。您或許會(huì)問我們?yōu)槭裁床辉谧x取文件名之后立即關(guān)閉streamReader。原因是當(dāng)您這樣做時(shí),您的客戶機(jī)將不會(huì)獲取任何數(shù)據(jù)。假設(shè)您在關(guān)閉streamWriter之前關(guān)閉streamReader,那么您可以往Socket寫任何東西,但卻沒有任何
39、數(shù)據(jù)能通過通道通道被關(guān)閉了??偨Y(jié)一下效勞器在我們接著討論另一個(gè)更實(shí)際的例如之前,讓我們回憶一下創(chuàng)立和使用ServerSocket的步驟:1.用一個(gè)您想讓它偵聽傳入客戶機(jī)連接的端口來實(shí)例化一個(gè)ServerSocket 如有問題那么拋出Exception。2. 調(diào)用ServerSocket的accept()以在等待連接期間造成阻塞。3. 獲取位于該底層Socket的流以進(jìn)展讀寫操作。4. 按使事情簡單化的原那么包裝流。5. 對(duì)Socket進(jìn)展讀寫。6. 關(guān)閉翻開的流并請(qǐng)記住,永遠(yuǎn)不要在關(guān)閉Writer之前關(guān)閉Reader。6創(chuàng)立多線程Socket效勞器前面的例如教給您根底知識(shí),但并不能令您更深化。
40、假設(shè)您到此就停頓了,那么您一次只能處理一臺(tái)客戶機(jī)。原因是handleConnection()是一個(gè)阻塞方法。只有當(dāng)它完成了對(duì)當(dāng)前連接的處理時(shí),效勞器才能承受另一個(gè)客戶機(jī)。在多數(shù)時(shí)候,您將需要也有必要一個(gè)多線程效勞器。創(chuàng)立MultithreadedRemoteFileServer類publicclassMultithreadedRemoteFileServerintlistenPort;publicMultithreadedRemoteFileServer(intlistenPort)this.listenPort=listenPort;/允許客戶機(jī)連接到效勞器,等待客戶機(jī)懇求publicvoi
41、dacceptConnections()tryServerSocketserver=newServerSocket(listenPort,5);SocketincomingConnection=null;while(true)incomingConnection=server.accept();handleConnection(incomingConnection);catch(BindExceptione)catch(IOExceptione)/與客戶機(jī)Socket交互以將客戶機(jī)所懇求的文件的內(nèi)容發(fā)送到客戶機(jī)publicvoidhandleConnection(Socketconnectio
42、nToHandle)newThread(newConnectionHandler(connectionToHandle).start();publicstaticvoidmain(Stringargs)MultithreadedRemoteFileServerserver=newMultithreadedRemoteFileServer(1001);server.acceptConnections();這里我們實(shí)現(xiàn)改動(dòng)過acceptConnections()方法,它將創(chuàng)立一個(gè)可以處理待發(fā)懇求的ServerSocket,并告訴ServerSocket承受連接。新的server仍然需要accept
43、Connections(),所以這些代碼實(shí)際上是一樣的。突出顯示的行表示一個(gè)重大的不同。對(duì)這個(gè)多線程版,我們?nèi)缃窨梢灾付蛻魴C(jī)懇求的最大數(shù)目,這些懇求都能在實(shí)例化ServerSocket期間處于待發(fā)狀態(tài)。假設(shè)我們沒有指定客戶機(jī)懇求的最大數(shù)目,那么我們假設(shè)使用缺省值50。這里是它的工作機(jī)制。假設(shè)我們指定待發(fā)數(shù)backlog值是5并且有五臺(tái)客戶機(jī)懇求連接到我們的效勞器。我們的效勞器將著手處理第一個(gè)連接,但處理該連接需要很長時(shí)間。由于我們的待發(fā)值是5,所以我們一次可以放五個(gè)懇求到隊(duì)列中。我們正在處理一個(gè),所以這意味著還有其它五個(gè)正在等待。等待的和正在處理的一共有六個(gè)。當(dāng)我們的效勞器仍忙于承受一號(hào)連接
44、記住隊(duì)列中還有2?6號(hào)時(shí),假設(shè)有第七個(gè)客戶機(jī)提出連接申請(qǐng),那么,該第七個(gè)客戶機(jī)將遭到回絕。我們將在帶有連接池效勞器例如中說明如何限定能同時(shí)連接的客戶機(jī)數(shù)目。處理連接:publicvoidhandleConnection(SocketconnectionToHandle)newThread(newConnectionHandler(connectionToHandle).start();我們對(duì)RemoteFileServer所做的大改動(dòng)就表達(dá)在這個(gè)方法上。我們?nèi)匀辉谛谄鞒惺芤粋€(gè)連接之后調(diào)用handleConnection(),但如今我們把該Socket傳遞給ConnectionHandler的
45、一個(gè)實(shí)例,它是Runnable的。我們用ConnectionHandler創(chuàng)立一個(gè)新Thread并啟動(dòng)它。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)造器用一個(gè)Socket實(shí)例作參數(shù)并將它賦給socketToHandle。請(qǐng)注意該類實(shí)現(xiàn)了Runnable接口。實(shí)現(xiàn)這個(gè)接口的類都必須實(shí)現(xiàn)run()方法。這里我們實(shí)現(xiàn)run()方法,它將攫取我們的連接的流
48、,用它來讀寫該連接,并在任務(wù)完成之后關(guān)閉它。ConnectionHandler的run()方法所做的事情就是RemoteFileServer上的handleConnection()所做的事情。首先,我們把InputStream和OutputStream分別包裝用Socket的getOutputStream()和getInputStream()進(jìn)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);請(qǐng)記住我們應(yīng)該從客戶機(jī)獲取一條有效的文件
50、途徑,這樣用該途徑名構(gòu)造一個(gè)新File,把它包裝進(jìn)FileReader以處理讀文件的操作,然后把它包裝進(jìn)BufferedReader以讓我們逐行地讀該文件。我們while循環(huán)中調(diào)用BufferedReader上的readLine()直到不再有要讀的行。請(qǐng)記注,對(duì)readLine()的調(diào)用將造成阻塞,直到有字節(jié)來到為止。我們獲取一些字節(jié)之后就把它們放到本地的line變量中,然后寫出到客戶機(jī)上。完成讀寫操作之后,我們關(guān)閉翻開的流??偨Y(jié)一下多線程效勞器讓我們回憶一下創(chuàng)立和使用“多線程版的效勞器的步驟:1. 修改acceptConnections()以用缺省為50或任何您想要的大于1的指定數(shù)字實(shí)例化S
51、erverSocket。2. 修改ServerSocket的handleConnection()以用ConnectionHandler的一個(gè)實(shí)例生成一個(gè)新的Thread。3. 借用RemoteFileServer的handleConnection()方法的代碼實(shí)現(xiàn)ConnectionHandler類。7創(chuàng)立帶有連接池的Socket效勞器我們?nèi)缃褚呀?jīng)擁有的MultithreadedServer每當(dāng)有客戶機(jī)申請(qǐng)一個(gè)連接時(shí)都在一個(gè)新Thread中創(chuàng)立一個(gè)新ConnectionHandler。這意味著可能有一捆Thread“躺在我們周圍。而且創(chuàng)立Thread的系統(tǒng)開銷并不是微缺乏道的。假設(shè)性能成為了問
52、題也請(qǐng)不要事到臨頭才意識(shí)到它,更高效地處理我們的效勞器是件好事。那么,我們?nèi)绾胃咝У毓芾硇谄鞫四??我們可以維護(hù)一個(gè)進(jìn)入的連接池,一定數(shù)量的ConnectionHandler將為它提供效勞。這種設(shè)計(jì)能帶來以下好處:?它限定了允許同時(shí)連接的數(shù)目。?我們只需啟動(dòng)ConnectionHandlerThread一次。幸運(yùn)的是,跟在我們的多線程例如中一樣,往代碼中添加“池不需要來一個(gè)大改動(dòng)。事實(shí)上,應(yīng)用程序的客戶機(jī)端根本就不受影響。在效勞器端,我們?cè)谛谄鲉?dòng)時(shí)創(chuàng)立一定數(shù)量的ConnectionHandler,我們把進(jìn)入的連接放入“池中并讓ConnectionHandler打理剩下的事情。這種設(shè)計(jì)中有
53、很多我們不打算討論的可能存在的技巧。例如,我們可以通過限定允許在“池中建立的連接的數(shù)目來回絕客戶機(jī)。請(qǐng)注意:我們將不會(huì)再次討論acceptConnections()。這個(gè)方法跟前面例如中的完全一樣。它無限循環(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等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 企業(yè)公司融資合同范本
- 艙口蓋系統(tǒng)行業(yè)深度研究報(bào)告
- 化肥長期供貨合同范本
- 場(chǎng)地使用出租合同范本
- 事業(yè)單位聘用合同范本
- 共享叉車租賃合同范例
- 副食購買合同范本
- 充電樁維修合同范本
- 勞務(wù)法合同范本
- 加盟合同范本
- 戰(zhàn)略管理與倫理
- 如何構(gòu)建高效課堂課件
- 虛擬化與云計(jì)算技術(shù)應(yīng)用實(shí)踐項(xiàng)目化教程 教案全套 第1-14周 虛擬化與云計(jì)算導(dǎo)論-騰訊云服務(wù)
- 甲基丙烯酸甲酯生產(chǎn)工藝畢業(yè)設(shè)計(jì)設(shè)備選型與布置模板
- 徐金桂行政法與行政訴訟法新講義
- 瀝青拌合設(shè)備結(jié)構(gòu)認(rèn)知
- 2023年北京高考政治真題試題及答案
- 復(fù)旦中華傳統(tǒng)體育課程講義05木蘭拳基本技術(shù)
- 北師大版五年級(jí)上冊(cè)數(shù)學(xué)教學(xué)課件第5課時(shí) 人民幣兌換
- 工程回訪記錄單
- 住房公積金投訴申請(qǐng)書
評(píng)論
0/150
提交評(píng)論