中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

菜鳥初學Java的備忘錄(六) (轉)

發布時間:2020-08-18 01:37:55 來源:ITPUB博客 閱讀:116 作者:worldblog 欄目:編程語言
菜鳥初學Java的備忘錄(六) (轉)[@more@]

2003年1月21日 星期二 晴
通過程序建立了實際的概念之后,現在應該回到最開始的問題,Socket是什么?是實現計算機通信的一種方式,這毫無疑問.但如何能夠用最容易理解的語言比較形象而又不偏頗的描述它的原理呢?

Bruce Eckel 在他的《Java 編程思想》一書中這樣描述套接字:
套接字是一種軟件抽象,用于表達兩臺機器之間的連接“終端”。對于一個給定的連接,每臺機器上都有一個套接字,您也可以想象它們之間有一條虛擬的“電纜”,“電纜”的每一端都插入到套接字中。當然,機器之間的物理硬件和電纜連接都是完全未知的。抽象的全部目的是使我們無須知道不必知道的細節.

 
按我的理解,抽象點來說,一個Socket就是一個電話聽筒,你有一個,和你通話的人也有一個,只不過其中有一個人的聽筒叫ServerSocket,另一個人的聽筒叫Socket.至于誰是ServerSocket,誰是Socket,這不重要,因為客戶端和服務器端本來就是相對的,可以互相轉化的.通話的兩個人通過拿起兩個聽筒建立了一條通道,這條通道通不通就要看是不是雙方都拿起聽筒了,假如只有一方拿起聽筒,那就只能聽到一些嘟嘟的聲音,證明通道不同.這里,拿起聽筒的過程就是Socket初始化的過程.建立了通道之后,也就是大家都拿起聽筒之后,通道兩端的人就可以開始通話了.這里又有兩個過程,即A對B說話,B接聽,和B對A說話,A收聽,這兩個過程是通過兩條線路完成的.傳輸在這兩條線路上的,就是流.流隱藏了所有傳輸的細節,使得通信雙方都認為,他們傳過去的是聲音,而不是編碼.


前面寫的服務器端的程序實際上是單任務版本,服務器對客戶機的處理機制是在同一時間段內只能處理一個連接,因為handleConnection中采取的是不斷循環的阻塞方法,檢測到一個,就處理一個,然后再檢測到一個,就再處理一個,如果有多個連接同時請求,那只能排隊等候.這樣的程序是無法在網絡中應付多個連接的,因為你無法保證在同一時間內只有一個客戶提出與服務器的連接請求,而用阻塞的方法應付多客戶連接其速度之慢是可想而知的.

這樣就催生了面向多連接的版本.顯然,通過多線程可以來實現我們的要求.

由于要解決的是處理客戶連接的問題,因此我們的工作只是在服務器端的程序當中修改.其原理不難推出,就是在檢測到一個連接請求之后,馬上建立一個線程去處理它,然后繼續兼聽下一個連接請求.所以,我們只需要將原來在handleConnection中的代碼原封不動的放到線程的執行代碼中,而在handleConnection中添加上新建線程的代碼就可以了,十分簡單.

同上一篇的風格一樣,我們來觀察各個部分的代碼細節.
首先為這個多線程的版本創建類MultiThreadRemoteFileServer

看看這個類的定義
import java.io.*;
import java.NET.*;

public class MultiThreadRemoteFileServer{
  protected int listenPort;
  public MultiThreadRemoteFileServer(int aListenPort){
  }
  public static void main(String[] args) {
  }
  public void acceptConnections() {
  }
  public void handleConnection(Socket incomingConnection) {
  }
}

幾乎和RemoteFileServer是一樣的,唯一的區別是在我們現在創建的這個類中增加了一個構造函數,這是為了能夠使得監聽的端口號由我們自己來定.定義如下

public MultithreadedRemoteFileServer(int aListenPort) {
  listenPort = aListenPort;
}


先來看main()
public static void main(String[] args) {
  MultithreadedRemoteFileServer server = new MultithreadedRemoteFileServer(3000);
  server.acceptConnections();
}

沒有區別吧,和RemoteFileServer的main()函數,只是端口號在創建的時候由主程序指定而已。

我們主要關心的改動都在后面
現在看acceptConnection監聽程序
public void acceptConnections() {
  try {
  ServerSocket server = new ServerSocket(listenPort, 5);//注意到沒有,建立服務器Socket的時候多了一個參數,這個參數是用來指定能夠同時申請連接的最大數目,缺省值是50
  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);
  }
}

改動的地方就一個,多了個參數.這里是它的工作機制。假設我們指定待發數(backlog 值)是5并且有五臺客戶機請求連接到我們的服務器。我們的服務器將著手處理第一個連接,但處理該連接需要很長時間。由于我們的待發值是 5,所以我們一次可以放五個請求到隊列中。我們正在處理一個,所以這意味著還有其它五個正在等待。等待的和正在處理的一共有六個。當我們的服務器仍忙于接受一號連接(記住隊列中還有 2—6 號)時,如果有第七個客戶機提出連接申請,那么,該第七個客戶機將遭到拒絕


接著看,我們的下一個改動顯然是在處理監聽到的線程的方法handleConnection中,前面已經說了,在多線程的版本中,我們檢測到一個連接請求,就馬上生成一個線程,然后就不用理它了,那么在這里就是新建線程的一句話.

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

我們注意到有一個新的類ConnectionHandler,這個類是Runnable的,即是一個接口類(這是用接口實現的一個線程,要是有不明白的話,可以去看看17號的關于線程的東西).我們用 ConnectionHandler 創建一個新 Thread 并啟動它。正如我們剛才所說的,原來在RemoteFileServer的handleConnection中的代碼統統原封不動的轉移到了這個接口類ConnectionHandler的run()方法中來了.

那么我們來看看整個ConnectionHandler類的定義吧。

class ConnectionHandler implements Runnable {
  protected Socket socketToHandle;
  public ConnectionHandler(Socket aSocketToHandle) {
  socketToHandle = aSocketToHandle;//通過構造函數,將待處理的Socket實例作為參數傳送進來
  }
  public void run() {//原來對Socket的讀/寫的代碼都在這里了
  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);
  }
  }
}


ConnectionHandler 的 run() 方法所做的事情就是 RemoteFileServer 上的 handleConnection() 所做的事情。首先把 InputStream 和 OutputStream 分別包裝(用 Socket 的 getOutputStream() 和 getInputStream())進 BufferedReader 和 PrintWriter。然后我們用這些代碼逐行地讀目標文件.由于InputStream中裝的是文件路徑,所以中間還需要使用FileReader流將文件路徑包裝,再經由BufferedReader包裝讀出.


我們的多線程服務器研究完了,同樣,我們回顧一下創建和使用“多線程版”的服務器的步驟:

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

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

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


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

青神县| 昆山市| 温州市| 出国| 静乐县| 泰来县| 五峰| 闽清县| 沁水县| 通江县| 松原市| 益阳市| 德令哈市| 信阳市| 平利县| 嵊州市| 岳阳市| 普格县| 和田市| 秦安县| 奇台县| 石泉县| 海晏县| 绥江县| 丰顺县| 闵行区| 和田市| 汉川市| 文成县| 日喀则市| 邻水| 扶余县| 河东区| 黔西县| 海城市| 凤山县| 普兰店市| 宜良县| 丰城市| 社会| 东乡县|