課前思考
1. 什麼是TCP/IP協議?
2. TCP/IP有哪兩種傳輸協議,各有什麼特點?
3. 什麼是URL?
4. URL和IP地址有什麼樣的關係?
5. 什麼叫通訊端(Socket)?
6. 通訊端(Socket)和TCP/IP協議的關係?
7. URL和通訊端(Socket)的關係?
8.1 網路編程基本概念,TCP/IP協議簡介
8.1.1 網路基礎知識
網路編程的目的就是指直接或間接地通過網路通訊協定與其他電腦進行通訊。網路編程中有兩個主要的問題,一個是如何準確的定位網路上一台或多台主機,另一個就是找到主機後如何可靠高效的進行資料轉送。在TCP/IP協議中IP層主要負責網路主機的定位,資料轉送的路由,由IP地址可以唯一地確定Internet上的一台主機。而TCP層則提供面嚮應用的可靠的或非可靠的資料轉送機制,這是網路編程的主要對象,一般不需要關心IP層是如何處理資料的。
目前較為流行的網路編程模型是客戶機/伺服器(C/S)結構。即通訊雙方一方作為伺服器等待客戶提出請求並予以響應。客戶則在需要服務時向伺服器提出申請。伺服器一般作為守護進程始終運行,監聽網路連接埠,一旦有客戶請求,就會啟動一個服務進程來響應該客戶,同時自己繼續監聽服務連接埠,使後來的客戶也能及時得到服務。
8.1.3兩類傳輸協議:TCP;UDP
儘管TCP/IP協議的名稱中只有TCP這個協議名,但是在TCP/IP的傳輸層同時存在TCP和UDP兩個協議。
TCP是Tranfer Control Protocol的簡稱,是一種連線導向的保證可靠傳輸的協議。通過TCP協議傳輸,得到的是一個順序的無差錯的資料流。發送方和接收方的成對的兩個socket之間必須建立串連,以便在TCP協議的基礎上進行通訊,當一個socket(通常都是server socket)等待建立串連時,另一個socket可以要求進行串連,一旦這兩個socket串連起來,它們就可以進行雙向資料轉送,雙方都可以進行發送或接收操作。
UDP是User Datagram Protocol的簡稱,是一種不需連線的協議,每個資料報都是一個獨立的資訊,包括完整的源地址或目的地址,它在網路上以任何可能的路徑傳往目的地,因此能否到達目的地,到達目的地的時間以及內容的正確性都是不能被保證的。
下面我們對這兩種協議做簡單比較:
使用UDP時,每個資料報中都給出了完整的地址資訊,因此無需要建立發送方和接收方的串連。對於TCP協議,由於它是一個連線導向的協議,在socket之間進行資料轉送之前必然要建立串連,所以在TCP中多了一個串連建立的時間。
使用UDP傳輸資料時是有大小限制的,每個被傳輸的資料報必須限定在64KB之內。而TCP沒有這方面的限制,一旦串連建立起來,雙方的socket就可以按統一的格式傳輸大量的資料。UDP是一個不可靠的協議,發送方所發送的資料報並不一定以相同的次序到達接收方。而TCP是一個可靠的協議,它確保接收方完全正確地擷取發送方所發送的全部資料。
總之,TCP在網路通訊上有極強的生命力,例如遠端連線(Telnet)和檔案傳輸(FTP)都需要不定長度的資料被可靠地傳輸。相比之下UDP操作簡單,而且僅需要較少的監護,因此通常用於區域網路高可靠性的分散系統中client/server應用程式。
讀者可能要問,既然有了保證可靠傳輸的TCP協議,為什麼還要非可靠傳輸的UDP協議呢?主要的原因有兩個。一是可靠的傳輸是要付出代價的,對資料內容正確性的檢驗必然佔用電腦的處理時間和網路的頻寬,因此TCP傳輸的效率不如UDP高。二是在許多應用中並不需要保證嚴格的傳輸可靠性,比如視頻會議系統,並不要求音頻視頻資料絕對的正確,只要保證連貫性就可以了,這種情況下顯然使用UDP會更合理一些。
8.2 基於URL的高層次Java網路編程
8.2.1一致資源定位器URL
URL(Uniform Resource Locator)是一致資源定位器的簡稱,它表示Internet上某一資源的地址。通過URL我們可以訪問Internet上的各種網路資源,比如最常見的WWW,FTP網站。瀏覽器通過解析給定的URL可以在網路上尋找相應的檔案或其他資源。
8.2.2 URL的組成
protocol://resourceName
協議名(protocol)指明擷取資源所使用的傳輸協議,如http、ftp、gopher、file等,資源名(resourceName)則應該是資源的完整地址,包括主機名稱、連接埠號碼、檔案名稱或檔案內部的一個引用。例如:
http://www.sun.com/ 協議名://主機名稱
http://home.netscape.com/home/welcome.html 協議名://機器名+檔案名稱
http://www.gamelan.com:80/Gamelan/network.html#BOTTOM 協議名://機器名+連接埠號碼+檔案名稱+內部引用
8.2.3 建立一個URL
為了表示URL, java.net中實現了類URL。我們可以通過下面的構造方法來初始化一個URL對象:
(1) public URL (String spec);
通過一個表示URL地址的字串可以構造一個URL對象。
URL urlBase=new URL("http://www. 263.net/")
(2) public URL(URL context, String spec);
通過基URL和相對URL構造一個URL對象。
URL net263=new URL ("http://www.263.net/");
URL index263=new URL(net263, "index.html")
(3) public URL(String protocol, String host, String file);
new URL("http", "www.gamelan.com", "/pages/Gamelan.net. html");
(4) public URL(String protocol, String host, int port, String file);
URL gamelan=new URL("http", "www.gamelan.com", 80, "Pages/Gamelan.network.html");
注意:類URL的構造方法都聲明拋棄非運行時例外(MalformedURLException),因此產生URL對象時,我們必須要對這一例外進行處理,通常是用try-catch語句進行捕獲。格式如下:
try{
URL myURL= new URL(…)
}catch (MalformedURLException e){
…
}
8.2.4 解析一個URL
一個URL對象產生後,其屬性是不能被改變的,但是我們可以通過類URL所提供的方法來擷取這些屬性:
public String getProtocol() 擷取該URL的協議名。
public String getHost() 擷取該URL的主機名稱。
public int getPort() 擷取該URL的連接埠號碼,如果沒有設定連接埠,返回-1。
public String getFile() 擷取該URL的檔案名稱。
public String getRef() 擷取該URL在檔案中的相對位置。
public String getQuery() 擷取該URL的查詢資訊。
public String getPath() 擷取該URL的路徑
public String getAuthority() 擷取該URL的許可權資訊
public String getUserInfo() 獲得使用者的資訊
public String getRef() 獲得該URL的錨
8.2.5 從URL讀取WWW網路資源
當我們得到一個URL對象後,就可以通過它讀取指定的WWW資源。這時我們將使用URL的方法openStream(),其定義為:InputStream openStream();方法openSteam()與指定的URL建立串連並返回InputStream類的對象以從這一串連中讀取資料。
package com.ljq.test;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
public class URLReader {
public static void main(String[] args) throws Exception {
URL url = new URL("http://www.cnblogs.com/linjiqin/");
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println(inputLine);
}
in.close();
}
}
8.2.6 通過URLConnetction串連WWW
通過URL的方法openStream(),我們只能從網路上讀取資料,如果我們同時還想輸出資料,例如向伺服器端的CGI程式發送一些資料,我們必須先與URL建立串連,然後才能對其進行讀寫,這時就要用到類URLConnection了。CGI是公用網關介面(Common Gateway Interface)的簡稱,它是使用者瀏覽器和伺服器端的應用程式進行串連的介面,有關CGI程式設計,請讀者參考有關書籍。
類URLConnection也在包java.net中定義,它表示Java程式和URL在網路上的通訊串連。當與一個URL建立串連時,首先要在一個URL對象上通過方法openConnection()產生對應的URLConnection對象。例如下面的程式段首先產生一個指向地址http://www.cnblogs.com/linjiqin/的對象,然後用openConnection()開啟該URL對象上的一個串連,返回一個URLConnection對象。如果串連過程失敗,將產生IOException
Try{
URL netchinaren = new URL ("http://edu.chinaren.com/index.shtml");
URLConnectonn tc = netchinaren.openConnection();
}catch(MalformedURLException e){ //建立URL()對象失敗
…
}catch (IOException e){ //openConnection()失敗
…
}
類URLConnection提供了很多方法來設定或擷取串連參數,程式設計時最常使用的是getInputStream()和getOurputStream(),其定義為:
InputSteram getInputSteram();
OutputSteram getOutputStream();
通過返回的輸入/輸出流我們可以與遠程對象進行通訊。看下面的例子:
package com.ljq.test;
import java.io.DataInputStream;
import java.io.PrintStream;
import java.net.URL;
import java.net.URLConnection;
public class URLReader {
public static void main(String[] args) throws Exception {
//建立URL對象
URL url=new URL("http://www.javasoft.com/cgi-bin/backwards");
//由URL對象擷取URLConnection對象
URLConnection conn=url.openConnection();
//由URLConnection擷取輸入資料流,並構造DataInputStream對象
DataInputStream dis=new DataInputStream(conn.getInputStream());
//由URLConnection擷取輸出資料流,並構造PrintStream對象
PrintStream ps=new PrintStream(conn.getOutputStream());
String line=dis.readLine();
ps.println("client…"); //向伺服器寫出字串 "client…"
}
}
其中backwards為伺服器端的CGI程式。實際上,類URL的方法openSteam()是通過URLConnection來實現的。它等價於openConnection().getInputStream();
基於URL的網路編程在底層其實還是基於下面要講的Socket介面的。WWW,FTP等標準化的網路服務都是基於TCP協議的,所以本質上講URL編程也是基於TCP的一種應用.
8.3 基於Socket的低層次Java網路編程
8.3.1 Socket通訊
網路上的兩個程式通過一個雙向的通訊串連實現資料的交換,這個雙向鏈路的一端稱為一個Socket。Socket通常用來實現客戶方和服務方的串連。Socket是TCP/IP協議的一個十分流行的編程介面,一個Socket由一個IP地址和一個連接埠號碼唯一確定。
在傳統的UNIX環境下可以操作TCP/IP協議的介面不止Socket一個,Socket所支援的協議種類也不光TCP/IP一種,因此兩者之間是沒有必然聯絡的。在Java環境下,Socket編程主要是指基於TCP/IP協議的網路編程。
8.3.2 Socket通訊的一般過程
使用Socket進行Client/Server程式設計的一般串連過程是這樣的:Server端Listen(監聽)某個連接埠是否有串連請求,Client端向Server端發出Connect(串連)請求,Server端向Client端發回Accept(接受)訊息。一個串連就建立起來了。Server端和Client端都可以通過Send,Write等方法與對方通訊。
對於一個功能齊全的Socket,都要包含以下基本結構,其工作過程包含以下四個基本的步驟:
(1) 建立Socket;
(2) 開啟串連到Socket的輸入/出流;
(3) 按照一定的協議對Socket進行讀/寫操作;
(4) 關閉Socket.
8.3.3 建立Socket
java在包java.net中提供了兩個類Socket和ServerSocket,分別用來表示雙向串連的用戶端和服務端。這是兩個封裝得非常好的類,使用很方便。其構造方法如下:
Socket(InetAddress address, int port);
Socket(InetAddress address, int port, boolean stream);
Socket(String host, int prot);
Socket(String host, int prot, boolean stream);
Socket(SocketImpl impl)
Socket(String host, int port, InetAddress localAddr, int localPort)
Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
ServerSocket(int port);
ServerSocket(int port, int backlog);
ServerSocket(int port, int backlog, InetAddress bindAddr)
其中address、host和port分別是雙向串連中另一方的IP地址、主機名稱和連接埠號碼,stream指明socket是流socket還是資料報socket,localPort表示本地主機的連接埠號碼,localAddr和bindAddr是本地機器的地址(ServerSocket的主機地址),impl是socket的父類,既可以用來建立serverSocket又可以用來建立Socket。count則表示服務端所能支援的最大串連數。例如:
Socket client = new Socket("127.0.01.", 80);
ServerSocket server = new ServerSocket(80);
注意,在選擇連接埠時,必須小心。每一個連接埠提供一種特定的服務,只有給出正確的連接埠,才能獲得相應的服務。0~1023的連接埠號碼為系統所保留,例如http服務的連接埠號碼為80,telnet服務的連接埠號碼為21,ftp服務的連接埠號碼為23, 所以我們在選擇連接埠號碼時,最好選擇一個大於1023的數以防止發生衝突。
在建立socket時如果發生錯誤,將產生IOException,在程式中必須對之作出處理。所以在建立Socket或ServerSocket是必須捕獲或拋出例外
8.3.8 簡單的Client/Server程式設計
下面我們給出一個用Socket實現的客戶和伺服器互動的典型的C/S結構的示範程式,讀者通過仔細閱讀該程式,會對前面所討論的各個概念有更深刻的認識。程式的意義請參考注釋。
1、用戶端程式
2、伺服器端程式