OMG,到底在尋找什么..................
          (構(gòu)造一個(gè)完美的J2EE系統(tǒng)所需要的完整知識(shí)體系)
          posts - 198,  comments - 37,  trackbacks - 0

          原貼地址: http://jeffchen.cnblogs.com/archive/2005/12/29/307269.aspx

          前一段時(shí)間看了IBM developerWork上的一篇關(guān)于socket的tutorial,由淺入深的實(shí)現(xiàn)了server-client程序,現(xiàn)在這里整理一下。
          ??? 什么是socket?
          ??? think in java 里給的回答:套接字是一種軟件抽象,用于表達(dá)兩臺(tái)機(jī)器之間的連接“終端”。對(duì)于一個(gè)給定的連接,每臺(tái)機(jī)器上都有一個(gè)套接字,您也可以想象它們之間有一條虛擬的“電纜”,“電纜”的每一端都插入到套接字中。當(dāng)然,機(jī)器之間的物理硬件和電纜連接都是完全未知的。抽象的全部目的是使我們無須知道不必知道的細(xì)節(jié)。簡(jiǎn)言之,一臺(tái)機(jī)器上的套接字與另一臺(tái)機(jī)器上的套接字交談就創(chuàng)建一條通信通道.
          ??? Socket 和 ServerSocket例子
          ???
          一.客戶端步驟:
          ??? 1)用您想連接的機(jī)器的 IP 地址和端口實(shí)例化 Socket(如有問題則拋出 Exception)。
          ??? 2)獲取 Socket 上的流。
          ??? 3)把流包裝進(jìn) BufferedReader/PrintWriter 的實(shí)例,如果這樣做能使事情更簡(jiǎn)單的話。
          ??? 4)對(duì) Socket 進(jìn)行讀寫。
          ??? 5)關(guān)閉打開的流。
          ???
          ??? RemoteFileClient類
          ??? import java.io.*;
          ??? import java.net.*;
          ???? public class RemoteFileClient {
          ??? protected String hostIp;
          ??? protected int hostPort;
          ??? protected BufferedReader socketReader;
          ??? protected PrintWriter socketWriter;

          ??? public RemoteFileClient(String aHostIp, int aHostPort) {
          ??????? hostIp = aHostIp;
          ??????? hostPort = aHostPort;
          ??? }
          ??? public static void main(String[] args) {
          ??? }
          ??? public void setUpConnection() {????????????? //連接到遠(yuǎn)程服務(wù)器
          ??? }
          ??? public String getFile(String fileNameToGet) {//向遠(yuǎn)程服務(wù)器請(qǐng)求 fileNameToGet 的內(nèi)容,在服務(wù)器傳回其內(nèi)容時(shí)接收該內(nèi)容
          ??? }
          ??? public void tearDownConnection() {?????????? //從遠(yuǎn)程服務(wù)器上斷開
          ??? }
          }

          ?????? main()函數(shù):
          ?? public static void main(String[] args) {
          ??? RemoteFileClient remoteFileClient = new RemoteFileClient("127.0.0.1", 3000);
          ??? remoteFileClient.setUpConnection();
          ??? String fileContents =
          ??????? remoteFileClient.getFile("C:\\WINNT\\Temp\\RemoteFile.txt");
          ??? remoteFileClient.tearDownConnection();

          ??? System.out.println(fileContents);
          }

          ???? setUpConnection()函數(shù)實(shí)現(xiàn):
          ?? public void setUpConnection() {
          ??? try {
          ??????? Socket client = new Socket(hostIp, hostPort);

          ??????? socketReader = new BufferedReader(
          ??????? ???? new InputStreamReader(client.getInputStream()));//使我們能夠讀取流的行
          ??????? socketWriter = new PrintWriter(client.getOutputStream());//使我們能夠發(fā)送文件請(qǐng)求到服務(wù)器:

          ??? } catch (UnknownHostException e) {
          ??????? System.out.println("Error setting up socket connection: unknown host at " + hostIp + ":" + hostPort);
          ??? } catch (IOException e) {
          ??????? System.out.println("Error setting up socket connection: " + e);
          ??? }
          }

          ???? getFile() 的實(shí)現(xiàn):
          ??? public String getFile(String fileNameToGet) {
          ??? StringBuffer fileLines = new StringBuffer();

          ??? try {
          ??????? socketWriter.println(fileNameToGet);? //把請(qǐng)求發(fā)送到主機(jī),PrintWriter 是我們?cè)趧?chuàng)建連接期間建立的???????????????
          ??????? socketWriter.flush();

          ??????? String line = null;
          ??????? while ((line = socketReader.readLine()) != null)
          ??????????? fileLines.append(line + "\n");
          ??? } catch (IOException e) {
          ??????? System.out.println("Error reading from file: " + fileNameToGet);
          ??? }

          ??? return fileLines.toString();
          }

          ??? tearDownConnection()方法的實(shí)現(xiàn):
          public void tearDownConnection() {
          ??? try {
          ??????? socketWriter.close();
          ??????? socketReader.close();
          ??? } catch (IOException e) {
          ??????? System.out.println("Error tearing down socket connection: " + e);
          ??? }
          }
          ??? 在上次做的聊天服務(wù)器程序中,因?yàn)?清除"的方法不對(duì),導(dǎo)致占用大量?jī)?nèi)存:(

          二.服務(wù)器步驟:
          ??? 1)用一個(gè)您想讓它偵聽傳入客戶機(jī)連接的端口來實(shí)例化一個(gè) ServerSocket(如有問題則拋出 Exception)。
          ??? 2)調(diào)用 ServerSocket 的 accept() 以在等待連接期間造成阻塞。
          ??? 3)獲取位于該底層 Socket 的流以進(jìn)行讀寫操作。
          ??? 4)按使事情簡(jiǎn)單化的原則包裝流。
          ??? 5)對(duì) Socket 進(jìn)行讀寫。
          ??? 6)關(guān)閉打開的流(并請(qǐng)記住,永遠(yuǎn)不要在關(guān)閉 Writer 之前關(guān)閉 Reader)。
          ???
          ??? RemoteFileServer 類
          ??? import java.io.*;
          ??? import java.net.*;
          ??? public class RemoteFileServer {
          ??? protected int listenPort = 3000;
          ??? public static void main(String[] args) {
          ??? }
          ??? public void acceptConnections() {???????????????????????? //允許客戶機(jī)連接到服務(wù)器
          ??? }
          ??? public void handleConnection(Socket incomingConnection) { //與客戶機(jī) Socket 交互以將您所請(qǐng)求的文件的內(nèi)容發(fā)送到客戶機(jī)
          ??? }
          }
          ???
          ???? main() 方法的實(shí)現(xiàn):
          ???? public static void main(String[] args) {
          ??? RemoteFileServer server = new RemoteFileServer();
          ??? server.acceptConnections();
          }

          ??? acceptConnections()方法的實(shí)現(xiàn):
          ??? public void acceptConnections() {
          ??? try {
          ??????? ServerSocket server = new ServerSocket(listenPort);
          ??????? Socket incomingConnection = null;
          ??????? while (true) {
          ??????????? incomingConnection = server.accept();
          ??????????? handleConnection(incomingConnection);
          ??????? }
          ??? } catch (BindException e) {
          ??????? System.out.println("Unable to bind to port " + listenPort);
          ??? } catch (IOException e) {
          ??????? System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort);
          ??? }
          }
          ??? 我們通過調(diào)用該 ServerSocket 的 accept() 來告訴它開始偵聽。accept() 方法將造成阻塞直到來了一個(gè)連接請(qǐng)求。此時(shí),accept()???? 返回一個(gè)新的 Socket,這個(gè) Socket 綁定到服務(wù)器上一個(gè)隨機(jī)指定的端口,返回的 Socket 被傳遞給 handleConnection()。

          ??? handleConnection() 方法的實(shí)現(xiàn):
          ??? public void handleConnection(Socket incomingConnection) {
          ??? try {
          ??????? OutputStream outputToSocket = incomingConnection.getOutputStream();
          ??????? InputStream inputFromSocket = incomingConnection.getInputStream();

          ??????? BufferedReader streamReader =
          ??????????? new BufferedReader(new InputStreamReader(inputFromSocket));

          ??????? FileReader fileReader = new FileReader(new File(streamReader.readLine()));//獲取一條有效的文件路徑

          ??????? BufferedReader bufferedFileReader = new BufferedReader(fileReader);
          ??????? PrintWriter streamWriter =
          ??????????? new PrintWriter(incomingConnection.getOutputStream());
          ??????? String line = null;
          ??????? while ((line = bufferedFileReader.readLine()) != null) {
          ??????????? streamWriter.println(line);
          ??????? }

          ??????? fileReader.close();
          ??????? streamWriter.close();
          ??????? streamReader.close();
          ??? } catch (Exception e) {
          ??????? System.out.println("Error handling a client: " + e);
          ??? }
          }
          ???? 如果您在關(guān)閉 streamWriter 之前關(guān)閉 streamReader,則您可以往 Socket 寫任何東西,但卻沒有任何數(shù)據(jù)能通過通道(通道被關(guān)?????? 閉了)。
          ???
          三.使服務(wù)器支持多線程
          ?? 一般步驟:
          ?? 1)修改 acceptConnections() 以用缺省為 50(或任何您想要的大于 1 的指定數(shù)字)實(shí)例化 ServerSocket。
          ?? 2)修改 ServerSocket 的 handleConnection() 以用 ConnectionHandler 的一個(gè)實(shí)例生成一個(gè)新的 Thread。
          ?? 3)借用 RemoteFileServer 的 handleConnection() 方法的代碼實(shí)現(xiàn) ConnectionHandler 類
          ??? public void acceptConnections() {
          ??????? try {
          ??????? ServerSocket server = new ServerSocket(listenPort, 5);
          ??????? Socket incomingConnection = null;
          ??????? while (true) {
          ??????????? incomingConnection = server.accept();
          ??????????? handleConnection(incomingConnection);
          ??????? }
          ??? } catch (BindException e) {
          ??? System.out.println("Unable to bind to port " + listenPort);
          ??? } catch (IOException e) {
          ??? System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort);
          ??? }
          }
          ??? 假設(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ī)將遭到拒絕。
          ???
          ?? public void handleConnection(Socket connectionToHandle) {
          ???? new Thread(new ConnectionHandler(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 讀/寫和讀 File 的代碼,這些代碼原來在 RemoteFileServer 的 handleConnection() 中。
          ???
          ??? ConnectionHandler 類
          ??? import java.io.*;
          ??? import java.net.*;

          public class ConnectionHandler implements Runnable{
          ?? Socket socketToHandle;

          ?? public ConnectionHandler(Socket aSocketToHandle) {
          ????? socketToHandle = aSocketToHandle;
          ?? }

          ?? public void run() {
          ?? }
          }

          ??? run() 方法的實(shí)現(xiàn),同RemoteFileServer 的 handleConnection():
          ??? public void run() {
          ??????? try {
          ??????????? PrintWriter streamWriter = new PrintWriter(socketToHandle.getOutputStream());
          ??????????? BufferedReader streamReader =
          ??????????????? new BufferedReader(new InputStreamReader(socketToHandle.getInputStream()));

          ??????????? String fileToRead = streamReader.readLine();
          ??????????? BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead));

          ??????????? String line = null;
          ??????????? while ((line = fileReader.readLine()) != null)
          ??????????????? streamWriter.println(line);

          ??????????? fileReader.close();
          ??????????? streamWriter.close();
          ??????????? streamReader.close();
          ??????? } catch (Exception e) {
          ??????????? System.out.println("Error handling a client: " + e);
          ??????? }
          ??? }

          四.更高效地管理服務(wù)器端
          ??? 我們可以維護(hù)一個(gè)進(jìn)入的連接池,一定數(shù)量的 ConnectionHandler 將為它提供服務(wù)。這種設(shè)計(jì)能帶來以下好處:
          ??? 1)它限定了允許同時(shí)連接的數(shù)目。
          ??? 2)我們只需啟動(dòng) ConnectionHandler Thread 一次。
          ??? 步驟:
          ??? 1)創(chuàng)建一個(gè)新種類的連接處理程序(我們稱之為 PooledConnectionHandler)來處理池中的連接。
          ??? 2)修改服務(wù)器以創(chuàng)建和使用一組 PooledConnectionHandler
          ??? 在服務(wù)器端,我們?cè)诜?wù)器啟動(dòng)時(shí)創(chuàng)建一定數(shù)量的 ConnectionHandler,我們把進(jìn)入的連接放入“池”中并讓 ConnectionHandler 打理剩下的事情。這種設(shè)計(jì)中有很多我們不打算討論的可能存在的技巧。例如,我們可以通過限定允許在“池”中建立的連接的數(shù)目來拒絕客戶機(jī)。
          ??? PooledRemoteFileServer類
          ??? import java.io.*;
          ??? import java.net.*;
          ??? import java.util.*;
          ??? public class PooledRemoteFileServer {
          ??? protected int maxConnections;???????? //我們的服務(wù)器能同時(shí)處理的活動(dòng)客戶機(jī)連接的最大數(shù)目
          ??? protected int listenPort;???????????? //進(jìn)入的連接的偵聽端口
          ??? protected ServerSocket serverSocket;? //接受客戶機(jī)連接請(qǐng)求的 ServerSocket

          ??? public PooledRemoteFileServer(int aListenPort, int maxConnections) {
          ??????? listenPort = aListenPort;
          ??????? this.maxConnections = maxConnections;
          ??? }
          ??? public static void main(String[] args) {
          ??? }
          ??? public void setUpHandlers() {??????? //創(chuàng)建數(shù)目為 maxConnections 的大量 PooledConnectionHandler
          ??? }
          ??? public void acceptConnections() {
          ??? }
          ??? protected void handleConnection(Socket incomingConnection) {
          ??? }
          }

          ?? main() 方法的實(shí)現(xiàn):
          ?? public static void main(String[] args) {
          ??? PooledRemoteFileServer server = new PooledRemoteFileServer(3000, 3);
          ??? server.setUpHandlers();
          ??? server.acceptConnections();
          }
          ?? 我們實(shí)例化一個(gè)新的 PooledRemoteFileServer,它將通過調(diào)用 setUpHandlers() 來建立三個(gè) PooledConnectionHandler。一旦服務(wù)器就緒,我們就告訴它 acceptConnections()。
          ??
          ?? public void setUpHandlers() {
          ??? for (int i = 0; i < maxConnections; i++) {
          ??????? PooledConnectionHandler currentHandler = new PooledConnectionHandler();
          ??????? new Thread(currentHandler, "Handler " + i).start();
          ??? }
          }
          ??? setUpHandlers() 方法創(chuàng)建 maxConnections(例如 3)個(gè) PooledConnectionHandler 并在新 Thread 中激活它們。用實(shí)現(xiàn)了 Runnable 的對(duì)象來創(chuàng)建 Thread 使我們可以在 Thread 調(diào)用 start() 并且可以期望在 Runnable 上調(diào)用了 run()。換句話說,我們的 PooledConnectionHandler 將等著處理進(jìn)入的連接,每個(gè)都在它自己的 Thread 中進(jìn)行。我們?cè)谑纠兄粍?chuàng)建三個(gè) Thread,而且一旦服務(wù)器運(yùn)行,這就不能被改變。
          ???
          ??? 我們實(shí)現(xiàn)需作改動(dòng)的 handleConnections() 方法,它將委派 PooledConnectionHandler 處理連接:
          ??? protected void handleConnection(Socket connectionToHandle) {
          PooledConnectionHandler.processRequest(connectionToHandle);
          }
          ???
          ??? PooledConnectionHandler 類:
          ???
          ??? import java.io.*;
          ??? import java.util.*;
          ??? public class PooledConnectionHandler implements Runnable {
          ??? protected Socket connection;????????????????????? //當(dāng)前正在處理的 Socket
          ??? protected static List pool = new LinkedList();??? //名為 pool 的靜態(tài) LinkedList 保存需被處理的連接

          ??? public PooledConnectionHandler() {
          ??? }
          ??? public void handleConnection() {
          ??? }
          ??? public static void processRequest(Socket requestToHandle) {
          ??? }
          ??? public void run() {
          ??? }
          }
          ??
          ?? processRequest() 方法,它將把傳入請(qǐng)求添加到池中,并告訴其它正在等待的對(duì)象該池已經(jīng)有一些內(nèi)容:
          ?? public static void processRequest(Socket requestToHandle) {
          ??? synchronized (pool) {
          ??????? pool.add(pool.size(), requestToHandle);
          ??????? pool.notifyAll();
          ??? }
          }

          ?? 實(shí)現(xiàn) PooledConnectionHandler 上需作改動(dòng)的 run()方法,它將在連接池上等待,并且池中一有連接就處理它:
          ?? public void run() {
          ??????? while (true) {
          ???????????? synchronized (pool) {
          ????????????????? while (pool.isEmpty()) {
          ?????????????????????? try {
          ??????????????????????????? pool.wait();
          ?????????????????????? } catch (InterruptedException e) {
          ??????????????????????????? return;
          ?????????????????????? }
          ?????????????????? }
          ?????????????????? connection = (Socket) pool.remove(0);
          ???????????? }
          ???????????? handleConnection();
          ??????? }
          }
          ???
          ??? 實(shí)現(xiàn)需做改動(dòng)的 handleConnection() 方法,該方法將攫取連接的流,使用它們,并在任務(wù)完成之后清除它們:
          ??? public void handleConnection() {
          ??? try {
          ??????? PrintWriter streamWriter = new PrintWriter(connection.getOutputStream());
          ??????? BufferedReader streamReader =
          ??????????? new BufferedReader(new InputStreamReader(connection.getInputStream()));

          ??????? String fileToRead = streamReader.readLine();
          ??????? BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead));

          ??????? String line = null;
          ??????? while ((line = fileReader.readLine()) != null)
          ??????????? streamWriter.println(line);

          ??????? fileReader.close();
          ??????? streamWriter.close();
          ??????? streamReader.close();
          ??? } catch (FileNotFoundException e) {
          ??????? System.out.println("Could not find requested file on the server.");
          ??? } catch (IOException e) {
          ??????? System.out.println("Error handling a client: " + e);
          ??? }
          }
          ??? 總結(jié):在現(xiàn)實(shí)生活中使用套接字只是這樣一件事,即通過貫徹優(yōu)秀的 OO 設(shè)計(jì)原則來保護(hù)應(yīng)用程序中各層間的封裝。

          posted on 2007-01-23 09:55 OMG 閱讀(323) 評(píng)論(0)  編輯  收藏 所屬分類: Soket

          <2007年1月>
          31123456
          78910111213
          14151617181920
          21222324252627
          28293031123
          45678910

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          IT風(fēng)云人物

          文檔

          朋友

          相冊(cè)

          經(jīng)典網(wǎng)站

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 印江| 墨江| 平利县| 溧阳市| 鸡东县| 正蓝旗| 廉江市| 灌南县| 兴安县| 乳源| 航空| 彭水| 博湖县| 榆社县| 翼城县| 巴彦淖尔市| 郁南县| 洛宁县| 涟水县| 宁陕县| 郑州市| 张家口市| 大荔县| 安康市| 望江县| 桃园县| 商水县| 扎赉特旗| 宁南县| 湄潭县| 滦南县| 阿克| 凤翔县| 施秉县| 峨山| 江陵县| 揭东县| 宜兰县| 同仁县| 汾阳市| 沛县|