程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 用Java Socket開發支持上千個並發的小型服務器(下)

用Java Socket開發支持上千個並發的小型服務器(下)

編輯:關於JAVA

總結一下客戶機

我們的類研究完了。在我們繼續往前討論服務器端的情況之前,讓我們回顧一下創建和使用Socket的步驟:

1.用您想連接的機器的IP地址和端口實例化Socket(如有問題則拋出Exception)。

2.獲取Socket上的流以進行讀寫。

3.把流包裝進BufferedReader/PrintWriter的實例,如果這樣做能使事情更簡單的話。

4.對Socket進行讀寫。

5.關閉打開的流。

5創建服務器Socket

創建RemoteFileServer類

1.import java.io.*;
2.import java.net.*;
3.public class RemoteFileServer {
4. int listenPort;
5. public RemoteFileServer(int listenPort) {
6. this.listenPort=listenPort;
7. }
8. //允許客戶機連接到服務器,等待客戶機請求
9. public void acceptConnections() {
10. try {
11. ServerSocket server = new ServerSocket(listenPort);
12. Socket incomingConnection = null;
13. while(true) {
14. incomingConnection = server.accept();
15. handleConnection(incomingConnection);
16. }
17. }
18. catch(BindException e) {
19. System.out.println("Unable to bind to port "+listenPort);
20. }
21. catch(IOException e) {
22. System.out.println("Unable to instantiate a ServerSocket on port: "+listenPort);
23.
24. }
25. }
26. //與客戶機Socket交互以將客戶機所請求的文件的內容發送到客戶機
27. public void handleConnection(Socket incomingConnection) {
28. try {
29. OutputStream outputToSocket = incomingConnection.getOutputStream();
30. InputStream inputFromSocket = incomingConnection.getInputStream();
31. BufferedReader streamReader = new BufferedReader(new InputStreamReader(inputFromSocket));
32. FileReader fileReader = new FileReader(new File(streamReader.readLine()));
33. BufferedReader bufferedFileReader = new BufferedReader(fileReader);
34. PrintWriter streamWriter = new PrintWriter(incomingConnection.getOutputStream());
35. String line = null;
36. while((line=bufferedFileReader.readLine())!=null){
37. streamWriter.println(line);
38. }
39. fileReader.close();
40. streamWriter.close();
41. streamReader.close();
42. }
43. catch(Exception e) {
44. System.out.println("Error handling a client: "+e);
45. e.printStackTrace();
46. }
47. }
48. public static void main(String args[]) {
49. RemoteFileServer server = new RemoteFileServer(1001);
50. server.acceptConnections();
51. }
52.}

跟客戶機中一樣,我們首先導入java.net的java.io。接著,我們給我們的類一個實例變量以保存端口,我們從該端口偵聽進入的連接。缺省情況下,端口是1001。

我們的類有一個main()方法和兩個其它方法。稍後我們將探究這些方法的細節。現在您只需知道acceptConnections()將允許客戶機連接到服務器以及handleConnection()與客戶機Socket交互以將您所請求的文件的內容發送到客戶機。

實現main()

這裡我們實現main()方法,它將創建RemoteFileServer並告訴它接受連接:服務器端的main()方法中,我們實例化一個新RemoteFileServer,它將在偵聽端口(1001)上偵聽進入的連接請求。然後我們調用acceptConnections()來告訴該server進行偵聽。

接受連接

這裡我們實現acceptConnections()方法,它將創建一個ServerSocket並等待連接請求:

1.public void acceptConnections() {
2. try {
3. ServerSocket server = new ServerSocket(listenPort);
4. Socket incomingConnection = null;
5. while(true) {
6. incomingConnection = server.accept();
7. handleConnection(incomingConnection);
8. }
9. }
10. catch(BindException e) {
11. System.out.println("Unable to bind to port "+listenPort);
12. }
13. catch(IOException e) {
14. System.out.println("Unable to instantiate a ServerSocket on port: "+listenPort);
15.
16. }
17. }

acceptConnections()用欲偵聽的端口號來創建ServerSocket。然後我們通過調用該ServerSocket的accept()來告訴它開始偵聽。accept()方法將造成阻塞直到來了一個連接請求。此時,accept()返回一個新的Socket,這個Socket綁定到服務器上一個隨機指定的端口,返回的Socket被傳遞給handleConnection()。請注意我們在一個無限循環中處理對連接的接受。這裡不支持任何關機。

無論何時如果您創建了一個無法綁定到指定端口(可能是因為別的什麼控制了該端口)的ServerSocket,Java代碼都將拋出一個錯誤。所以這裡我們必須捕捉可能的BindException。就跟在客戶機端上時一樣,我們必須捕捉IOException,當我們試圖在ServerSocket上接受連接時,它就會被拋出。請注意,您可以通過用毫秒數調用setSoTimeout()來為accept()調用設置超時,以避免實際長時間的等待。調用setSoTimeout()將使accept()經過指定占用時間後拋出IOException。

處理連接

這裡我們實現handleConnection()方法,它將用連接的流來接收輸入和寫輸出:

1.public void handleConnection(Socket incomingConnection) {
2. try {
3. OutputStream outputToSocket = incomingConnection.getOutputStream();
4. InputStream inputFromSocket = incomingConnection.getInputStream();
5. BufferedReader streamReader = new BufferedReader(new InputStreamReader(inputFromSocket));
6. FileReader fileReader = new FileReader(new File(streamReader.readLine()));
7. BufferedReader bufferedFileReader = new BufferedReader(fileReader);
8. PrintWriter streamWriter = new PrintWriter(incomingConnection.getOutputStream());
9. String line = null;
10. while((line=bufferedFileReader.readLine())!=null){
11. streamWriter.println(line);
12. }
13. fileReader.close();
14. streamWriter.close();
15. streamReader.close();
16. }
17. catch(Exception e) {
18. System.out.println("Error handling a client: "+e);
19. e.printStackTrace();
20. }
21.}

跟在客戶機中一樣,我們用getOutputStream()和getInputStream()來獲取與我們剛創建的Socket相關聯的流。跟在客戶機端一樣,我們把InputStream包裝進BufferedReader,把OutputStream包裝進PrintWriter。在服務器端上,我們需要添加一些代碼,用來讀取目標文件和把內容逐行發送到客戶機。這裡是重要的代碼:

FileReader fileReader = new FileReader(new File(streamReader.readLine()));
BufferedReader bufferedFileReader = new BufferedReader(fileReader);
String line = null;
while((line=bufferedFileReader.readLine())!=null)
{
streamWriter.println(line);
}

這些代碼值得詳細解釋。讓我們一點一點來看:

FileReader fileReader = new FileReader(new File(streamReader.readLine()));

首先,我們使用Socket的InputStream的BufferedReader。我們應該獲取一條有效的文件路徑,所以我們用該路徑名構造一個新File。我們創建一個新FileReader來處理讀文件的操作。

BufferedReader bufferedFileReader = new BufferedReader(fileReader);

這裡我們把FileReader包裝進BufferedReader以使我們能夠逐行地讀該文件。

接著,我們調用BufferedReader的readLine()。這個調用將造成阻塞直到有字節到來。我們獲取一些字節之後就把它們放到本地的line變量中,然後再寫出到客戶機上。完成讀寫操作之後,我們就關閉打開的流。

請注意我們在完成從Socket的讀操作之後關閉streamWriter和streamReader。您或許會問我們為什麼不在讀取文件名之後立刻關閉streamReader。原因是當您這樣做時,您的客戶機將不會獲取任何數據。如果您在關閉streamWriter之前關閉streamReader,則您可以往Socket寫任何東西,但卻沒有任何數據能通過通道(通道被關閉了)。

總結一下服務器

在我們接著討論另一個更實際的示例之前,讓我們回顧一下創建和使用ServerSocket的步驟:

1.用一個您想讓它偵聽傳入客戶機連接的端口來實例化一個ServerSocket(如有問題則拋出Exception)。

2.調用ServerSocket的accept()以在等待連接期間造成阻塞。

3.獲取位於該底層Socket的流以進行讀寫操作。

4.按使事情簡單化的原則包裝流。

5.對Socket進行讀寫。

6.關閉打開的流(並請記住,永遠不要在關閉Writer之前關閉Reader)。

6創建多線程Socket服務器

前面的示例教給您基礎知識,但並不能令您更深入。如果您到此就停止了,那麼您一次只能處理一台客戶機。原因是handleConnection()是一個阻塞方法。只有當它完成了對當前連接的處理時,服務器才能接受另一個客戶機。在多數時候,您將需要(也有必要)一個多線程服務器。

創建MultithreadedRemoteFileServer類

1.import java.io.*;
2.import java.net.*;
3.public class MultithreadedRemoteFileServer {
4. int listenPort;
5. public MultithreadedRemoteFileServer(int listenPort) {
6. this.listenPort=listenPort;
7. }
8. //允許客戶機連接到服務器,等待客戶機請求
9. public void acceptConnections() {
10. try {
11. ServerSocket server = new ServerSocket(listenPort, 5);
12. Socket incomingConnection = null;
13. while(true) {
14. incomingConnection = server.accept();
15. handleConnection(incomingConnection);
16. }
17. }
18. catch(BindException e) {
19. System.out.println("Unable to bind to port "+listenPort);
20. }
21. catch(IOException e) {
22. System.out.println("Unable to instantiate a ServerSocket on port: "+listenPort);
23. }
24. }
25. //與客戶機Socket交互以將客戶機所請求的文件的內容發送到客戶機
26. public void handleConnection(Socket connectionToHandle) {
27. new Thread(new ConnectionHandler(connectionToHandle)).start();
28. }
29. public static void main(String args[]) {
30. MultithreadedRemoteFileServer server = new MultithreadedRemoteFileServer(1001);
31. server.acceptConnections();
32. }
33.}

這裡我們實現改動過acceptConnections()方法,它將創建一個能夠處理待發請求的ServerSocket,並告訴ServerSocket接受連接。

新的server仍然需要acceptConnections(),所以這些代碼實際上是一樣的。突出顯示的行表示一個重大的不同。對這個多線程版,我們現在可以指定客戶機請求的最大數目,這些請求都能在實例化ServerSocket期間處於待發狀態。如果我們沒有指定客戶機請求的最大數目,則我們假設使用缺省值50。

這裡是它的工作機制。假設我們指定待發數(backlog值)是5並且有五台客戶機請求連接到我們的服務器。我們的服務器將著手處理第一個連接,但處理該連接需要很長時間。由於我們的待發值是5,所以我們一次可以放五個請求到隊列中。我們正在處理一個,所以這意味著還有其它五個正在等待。等待的和正在處理的一共有六個。當我們的服務器仍忙於接受一號連接(記住隊列中還有2?6號)時,如果有第七個客戶機提出連接申請,那麼,該第七個客戶機將遭到拒絕。我們將在帶有連接池服務器示例中說明如何限定能同時連接的客戶機數目。

處理連接:

public void handleConnection(Socket connectionToHandle) {
new Thread(new ConnectionHandler(connectionToHandle)).start();
}

我們對RemoteFileServer所做的大改動就體現在這個方法上。我們仍然在服務器接受一個連接之後調用handleConnection(),但現在我們把該Socket傳遞給ConnectionHandler的一個實例,它是Runnable的。我們用ConnectionHandler創建一個新Thread並啟動它。ConnectionHandler的run()方法包Socket讀/寫和讀File的代碼,這些代碼原來在RemoteFileServer的handleConnection()中。

創建ConnectionHandler類

1.import java.io.*;
2.import java.net.*;
3.public class ConnectionHandler implements Runnable {
4. protected Socket socketToHandle;
5. public ConnectionHandler(Socket socketToHandle) {
6. this.socketToHandle=socketToHandle;
7. }
8. public void run() {
9. try {
10. PrintWriter streamWriter = new PrintWriter(socketToHandle.getOutputStream());
11. BufferedReader streamReader = new BufferedReader(new InputStreamReader(socketToHandle.getInputStream()));
12. String fileToRead = streamReader.readLine();
13. BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead));
14. String line =null;
15. while((line=fileReader.readLine())!=null) {
16. streamWriter.println(line);
17. }
18. fileReader.close();
19. streamWriter.close();
20. streamReader.close();
21. }
22. catch(Exception e) {
23. System.out.println("Error handling a client: "+e);
24. e.printStackTrace();
25. }
26. }
27.}

這個助手類相當簡單。跟我們到目前為止的其它類一樣,我們導入java.net和java.io。該類只有一個實例變量socketToHandle,它保存由該實例處理的Socket。

類的構造器用一個Socket實例作參數並將它賦給socketToHandle。

請注意該類實現了Runnable接口。實現這個接口的類都必須實現run()方法。這裡我們實現run()方法,它將攫取我們的連接的流,用它來讀寫該連接,並在任務完成之後關閉它。ConnectionHandler的run()方法所做的事情就是RemoteFileServer上的handleConnection()所做的事情。首先,我們把InputStream和OutputStream分別包裝(用Socket的getOutputStream()和getInputStream())進BufferedReader和PrintWriter。然後我們用這些代碼逐行地讀目標文件:

1.PrintWriter streamWriter = new PrintWriter(socketToHandle.getOutputStream());
2. BufferedReader streamReader = new BufferedReader(new InputStreamReader(socketToHandle.getInputStream()));
3. String fileToRead = streamReader.readLine();
4. BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead));
5. String line =null;
6. while((line=fileReader.readLine())!=null) {
7. streamWriter.println(line);
8. }

請記住我們應該從客戶機獲取一條有效的文件路徑,這樣用該路徑名構造一個新File,把它包裝進FileReader以處理讀文件的操作,然後把它包裝進BufferedReader以讓我們逐行地讀該文件。我們while循環中調用BufferedReader上的readLine()直到不再有要讀的行。請記注,對readLine()的調用將造成阻塞,直到有字節來到為止。我們獲取一些字節之後就把它們放到本地的line變量中,然後寫出到客戶機上。完成讀寫操作之後,我們關閉打開的流。

總結一下多線程服務器

讓我們回顧一下創建和使用“多線程版”的服務器的步驟:

1.修改acceptConnections()以用缺省為50(或任何您想要的大於1的指定數字)實例化ServerSocket。

2.修改ServerSocket的handleConnection()以用ConnectionHandler的一個實例生成一個新的Thread。

3.借用RemoteFileServer的handleConnection()方法的代碼實現ConnectionHandler類。

7 創建帶有連接池的Socket服務器

我們現在已經擁有的MultithreadedServer每當有客戶機申請一個連接時都在一個新Thread中創建一個新ConnectionHandler。這意味著可能有一捆Thread“躺”在我們周圍。而且創建Thread的系統開銷並不是微不足道的。如果性能成為了問題(也請不要事到臨頭才意識到它),更高效地處理我們的服務器是件好事。那麼,我們如何更高效地管理服務器端呢?我們可以維護一個進入的連接池,一定數量的ConnectionHandler將為它提供服務。這種設計能帶來以下好處:

•它限定了允許同時連接的數目。

•我們只需啟動ConnectionHandler Thread一次。

幸運的是,跟在我們的多線程示例中一樣,往代碼中添加“池”不需要來一個大改動。事實上,應用程序的客戶機端根本就不受影響。在服務器端,我們在服務器啟動時創建一定數量的ConnectionHandler,我們把進入的連接放入“池”中並讓ConnectionHandler打理剩下的事情。這種設計中有很多我們不打算討論的可能存在的技巧。例如,我們可以通過限定允許在“池”中建立的連接的數目來拒絕客戶機。

請注意:我們將不會再次討論acceptConnections()。這個方法跟前面示例中的完全一樣。它無限循環地調用ServerSocket上的accept()並把連接傳遞到handleConnection()。

創建PooledRemoteFileServer類

1.import java.io.*;
2.import java.net.*;
3.import java.util.*;
4.public class PooledRemoteFileServer {
5. protected int maxConnections;
6. protected int listenPort;
7. protected ServerSocket serverSocket;
8. public PooledRemoteFileServer(int aListenPort, int maxConnections) {
9. listenPort= aListenPort;
10. this.maxConnections = maxConnections;
11. }
12. public void acceptConnections() {
13. try {
14. ServerSocket server = new ServerSocket(listenPort, 5);
15. Socket incomingConnection = null;
16. while(true) {
17. incomingConnection = server.accept();
18. handleConnection(incomingConnection);
19. }
20. }
21. catch(BindException e) {
22. System.out.println("");
23. }
24. catch(IOException e) {
25. System.out.println(""+listenPort);
26. }
27. }
28. protected void handleConnection(Socket connectionToHandle) {
29. PooledConnectionHandler.processRequest(connectionToHandle);
30. }
31. public void setUpHandlers() {
32. for(int i=0; i<maxConnections; i++) {
33. PooledConnectionHandler currentHandler = new PooledConnectionHandler();
34. new Thread(currentHandler, "Handler " + i).start();
35. }
36. }
37. public static void main(String args[]) {
38. PooledRemoteFileServer server = new PooledRemoteFileServer(1001, 3);
39. server.setUpHandlers();
40. server.acceptConnections();
41. }
42.}

請注意一下您現在應該熟悉了的import語句。我們給類以下實例變量以保存:

•我們的服務器能同時處理的活動客戶機連接的最大數目

•進入的連接的偵聽端口(我們沒有指定缺省值,但如果您想這樣做,並不會受到限制)

•將接受客戶機連接請求的ServerSocket

類的構造器用的參數是偵聽端口和連接的最大數目

我們的類有一個main()方法和三個其它方法。稍後我們將探究這些方法的細節。現在只須知道setUpHandlers()創建數目為maxConnections的大量PooledConnectionHandler,而其它兩個方法則與我們前面已經看到的相似:acceptConnections()在ServerSocket上偵聽傳入的客戶機連接,而handleConnection則在客戶機連接一旦被建立後就實際處理它。

實現main()

這裡我們實現需作改動的main()方法,該方法將創建能夠處理給定數目的客戶機連接的PooledRemoteFileServer,並告訴它接受連接:

1.public static void main(String args[]) {
2. PooledRemoteFileServer server = new PooledRemoteFileServer(1001, 3);
3. server.setUpHandlers();
4. server.acceptConnections();
5.}

我們的main()方法很簡單。我們實例化一個新的PooledRemoteFileServer,它將通過調用setUpHandlers()來建立三個PooledConnectionHandler。一旦服務器就緒,我們就告訴它acceptConnections()。

建立連接處理程序

1.public void setUpHandlers() {
2. for(int i=0; i<maxConnections; i++) {
3. PooledConnectionHandler currentHandler = new PooledConnectionHandler();
4. new Thread(currentHandler, "Handler " + i).start();
5. }
6. }

setUpHandlers()方法創建maxConnections(例如3)個PooledConnectionHandler並在新Thread中激活它們。用實現了Runnable的對象來創建Thread使我們可以在Thread調用start()並且可以期望在Runnable上調用了run()。換句話說,我們的PooledConnectionHandler將等著處理進入的連接,每個都在它自己的Thread中進行。我們在示例中只創建三個Thread,而且一旦服務器運行,這就不能被改變。

處理連接

這裡我們實現需作改動的handleConnections()方法,它將委派PooledConnectionHandler處理連接:

protected void handleConnection(Socket connectionToHandle) {
PooledConnectionHandler.processRequest(connectionToHandle);
}

我們現在叫PooledConnectionHandler處理所有進入的連接(processRequest()是一個靜態方法)。

創建PooledRemoteFileServer類

1.import java.io.*;
2.import java.net.*;
3.import java.util.*;
4.public class PooledConnectionHandler implements Runnable {
5. protected Socket connection;
6. protected static List pool = new LinkedList();
7. public PooledConnectionHandler() {}
8. public void handleConnection() {
9. try {
10. PrintWriter streamWriter = new PrintWriter(connection.getOutputStream());
11. BufferedReader streamReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
12. String fileToRead = streamReader.readLine();
13. BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead));
14. String line = null;
15. while((line=fileReader.readLine())!=null)
16. streamWriter.println(line);
17. fileReader.close();
18. streamWriter.close();
19. streamReader.close();
20. }
21. catch(FileNotFoundException e) {
22. System.out.println("");
23. }
24. catch(IOException e) {
25. System.out.println(""+e);
26. }
27. }
28. public static void processRequest(Socket requestToHandle) {
29. synchronized(pool) {
30. pool.add(pool.size(), requestToHandle);
31. pool.notifyAll();
32. }
33. }
34. public void run() {
35. while(true) {
36. synchronized(pool) {
37. while(pool.isEmpty()) {
38. try {
39. pool.wait();
40. }
41. catch(InterruptedException e) {
42. e.printStackTrace();
43. }
44. }
45. connection= (Socket)pool.remove(0);
46. }
47. handleConnection();
48. }
49. }
50.}

這個助手類與ConnectionHandler非常相似,但它帶有處理連接池的手段。該類有兩個實例變量:

•connection是當前正在處理的Socket

•名為pool的靜態LinkedList保存需被處理的連接

填充連接池

這裡我們實現PooledConnectionHandler上的processRequest()方法,它將把傳入請求添加到池中,並告訴其它正在等待的對象該池已經有一些內容:

public static void processRequest(Socket requestToHandle) {
synchronized(pool) {
pool.add(pool.size(), requestToHandle);
pool.notifyAll();
}
}

synchronized塊是個稍微有些不同的東西。您可以同步任何對象上的一個塊,而不只是在本身的某個方法中含有該塊的對象。在我們的示例中,processRequest()方法包含有一個pool(請記住它是一個LinkedList,保存等待處理的連接池)的synchronized塊。我們這樣做的原因是確保沒有別人能跟我們同時修改連接池。

既然我們已經保證了我們是唯一“涉水”池中的人,我們就可以把傳入的Socket添加到LinkedList的尾端。一旦我們添加了新的連接,我們就用以下代碼通知其它正在等待該池的Thread,池現在已經可用:

pool.notifyAll();

Object的所有子類都繼承這個notifyAll()方法。這個方法,連同我們下一屏將要討論的wait()方法一起,就使一個Thread能夠讓另一個Thread知道一些條件已經具備。這意味著該第二個Thread一定正在等待那些條件的滿足。

從池中獲取連接

這裡我們實現PooledConnectionHandler上需作改動的run()方法,它將在連接池上等待,並且池中一有連接就處理它:

1.public void run() {
2. while(true) {
3. synchronized(pool) {
4. while(pool.isEmpty()) {
5. try {
6. pool.wait();
7. }
8. catch(InterruptedException e) {
9. e.printStackTrace();
10. }
11. }
12. connection= (Socket)pool.remove(0);
13. }
14. handleConnection();
15. }
16.}

回想一下在前面講過的:一個Thread正在等待有人通知它連接池方面的條件已經滿足了。在我們的示例中,請記住我們有三個PooledConnectionHandler在等待使用池中的連接。每個PooledConnectionHandler都在它自已的Thread中運行,並通過調用pool.wait()產生阻塞。當我們的processRequest()在連接池上調用notifyAll()時,所有正在等待的PooledConnectionHandler都將得到“池已經可用”的通知。然後各自繼續前行調用pool.wait(),並重新檢查while(pool.isEmpty())循環條件。除了一個處理程序,其它池對所有處理程序都將是空的,因此,在調用pool.wait()時,除了一個處理程序,其它所有處理程序都將再次產生阻塞。恰巧碰上非空池的處理程序將跳出while(pool.isEmpty())循環並攫取池中的第一個連接:

connection=(Socket)pool.remove(0);

處理程序一旦有一個連接可以使用,就調用handleConnection()處理它。

在我們的示例中,池中可能永遠不會有多個連接,只是因為事情很快就被處理掉了。如果池中有一個以上連接,那麼其它處理程序將不必等待新的連接被添加到池。當它們檢查pool.isEmpty()條件時,將發現其值為假,然後就從池中攫取一個連接並處理它。

還有另一件事需注意。當run()擁有池的互斥鎖時,processRequest()如何能夠把連接放到池中呢?答案是對池上的wait()的調用釋放鎖,而wait()接著就在自己返回之前再次攫取該鎖。這就使得池對象的其它同步代碼可以獲取該鎖。

處理連接:再一次

這裡我們實現需做改動的handleConnection()方法,該方法將攫取連接的流,使用它們,並在任務完成之後清除它們:

1.public void  handleConnection() {
2. try {
3. PrintWriter streamWriter = new PrintWriter(connection.getOutputStream());
4. BufferedReader streamReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
5. String fileToRead = streamReader.readLine();
6. BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead));
7. String line = null;
8. while((line=fileReader.readLine())!=null)
9. streamWriter.println(line);
10. fileReader.close();
11. streamWriter.close();
12. streamReader.close();
13. }
14. catch(FileNotFoundException e) {
15. System.out.println("");
16. }
17. catch(IOException e) {
18. System.out.println(""+e);
19. }
20.}

跟在多線程服務器中不同,我們的PooledConnectionHandler有一個handleConnection()方法。這個方法的代碼跟非池式的ConnectionHandler上的run()方法的代碼完全一樣。首先,我們把OutputStream和InputStream分別包裝進(用Socket上的getOutputStream()和getInputStream())BufferedReader和PrintWriter。然後我們逐行讀目標文件,就象我們在多線程示例中做的那樣。再一次,我們獲取一些字節之後就把它們放到本地的line變量中,然後寫出到客戶機。完成讀寫操作之後,我們關閉FileReader和打開的流。

總結一下帶有連接池的服務器

讓我們回顧一下創建和使用“池版”服務器的步驟:

1.創建一個新種類的連接處理程序(我們稱之為PooledConnectionHandler)來處理池中的連接。

2.修改服務器以創建和使用一組PooledConnectionHandler。

Java語言簡化了套接字在應用程序中的使用。它的基礎實際上是java.net包中的Socket和ServerSocket類。一旦您理解了表象背後發生的情況,就能容易地使用這些類。在現實生活中使用套接字只是這樣一件事,即通過貫徹優秀的OO設計原則來保護應用程序中各層間的封裝。我們為您展示了一些有幫助的類。這些類的結構對我們的應用程序隱藏了Socket交互作用的低級細節?使應用程序能只使用可插入的ClientSocketFacade和ServerSocketFacade。在有些地方(在Facade內),您仍然必須管理稍顯雜亂的字節細節,但您只須做一次就可以了。更好的是,您可以在將來的項目中重用這些低級別的助手類。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved