網絡編程技術


                                                     網絡編程技術

Java 網絡編程

                                                                    網絡基礎知識

  網絡編程的目的:直接或間接地通過網絡協議與其他計算機進行通訊。

  網絡編程中有兩個主要的問題:

  1.如何准確地定位網絡上一台或多台主機。

  2.找到主機后如何可靠高效地進行數據傳輸。

  目前較為流行的網絡編程模型是客戶端/服務器(C/S)結構

  即通信雙方一方作為服務器等待客戶提出請求並予以相應。客戶則在需要服務時向服務器提出申請。

  服務器始終運行,監聽網絡端口,一旦有客戶請求,就會啟動一個服務線程來響應該客戶,同時自己繼續監聽服務窗口,使后來的客戶也能及時得到服務。

 

IP地址

  IP網絡中每台主機都必須有一個唯一的IP地址,IP地址是一個邏輯地址。

  英特網上的IP地址具有全球唯一性。

  32位,四個字節,常用點分十進制的格式表示。

  例如:192.168.0.200

 

協議

  為進行網絡中的數據交換(通信)而建立的規則、標准或約定。(=語義+語法+規則)。

  不同層具有各自不同的協議。

 

ISO/OSI七層參考模型

  網絡體系結構解決異質性問題采用的是分層的方法——把復雜的網絡互聯問題划分為若干個較小的、單一的問題,在不同層上予以解決。

  OSI(Open System Interconnection)參考模型將網絡的不同功能划分為7層:

  應用層:處理網絡應用

  表示層:數據表示

  會話層:主機間通信

  傳輸層:端到端的連接

  網絡層:尋址和最短路徑

  數據鏈路層:介質訪問(接入)

  物理層:二進制傳輸

  

  

  通信實體的對等層之間不允許直接通信,各層之間是嚴格的單向依賴,上層(Service user)使用下層提供的服務,下層(Service provider)向上層提供服務。

  對等層通信的實質對等層實體之間虛擬通信,下層向上層提供服務,實際通信在最底層完成。

 

OSI各層所使用的協議:

  應用層:Telnet、FTP、HTTP、DNS、SMTP、POP3

  傳輸層:TCP、UDP

  TCP:面向連接的可靠的傳輸協議。

  UDP:是無連接的,不可靠的傳輸協議。

  網絡層:IP、ICMP、IGMP

 

端口

  在互聯網上傳輸的數據都包含有用來識別目的地的IP地址和端口號。

  IP地址用來標識網絡上的計算機,而端口號用來指明該計算機上的應用程序。

  端口是一種抽象的軟件結構(包括一些數據結構和I/O緩沖區)。

  應用程序通過系統調用與某端口建立連接(binding)后,傳輸層傳給該端口的數據都被相應的進程所接收,相應進程發給傳輸層的數據都通過該端口輸出。

  端口用一個整數型標識符來表示,即端口號。

  端口號跟協議相關,TCP/IP傳輸層的兩個協議TCP和UDP是完全獨立的兩個軟件模塊,因此各自的端口號也相互獨立,端口通常稱為協議端口(protocol port,簡稱端口。

  端口使用一個16位的數字來表示,它的范圍是0~65535,1024以下的端口號保留給預定義的服務。例如,http使用80端口。

 

 

Java網絡編程基礎知識

 

一、網絡基本概念

 

1、計算機網絡,就是把分布在不同地理區域的計算機與專門的外部設備用通信線路互連成一個規模大、功能強的網絡系統,從而使眾多的計算機可以方便地互相傳遞信息,共享硬件、軟件、數據信息等資源。

 

2、網絡體系結構:國際標准化組織ISO於l978年提出“開放系統互連參考模型”,即著名的OSI(Open System Interconnection)模型。該模型把計算機網絡分成物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層、應用層等七層。

 

 3、通信協議:計算機網絡中實現通信必須有一些約定,即通信協議。對速率、傳輸代碼、代碼結構、傳輸控制步驟、出錯控制等制定標准。

 

TCP協議:提供可靠的數據傳輸服務;(傳輸層)

 

IP協議進行IP數據包的分割和組裝。(應用層)

 

二、IP地址和端口號

 

1、IP地址:為實現網絡中不同的計算機之間的通信,在網絡中的每台機器都必須有一個與眾不同事標識,這就是IP地址(IP Address)。

 

格式:數字型、32位、由4段8位的二進制數組成。一般表示為十進制形式(4個0~255的十進制整數),中間用圓點隔開。如:166.111.78.98

 

域名地址:也是分段表示的,便於記憶的、字符串形式。

 

2、端口:一個16位的整數,用於表示數據交給哪個通信程序處理。因此,端口就是應用程序與外界交流的出入口,它是一種抽象的軟件結構,包括一些數據結構和I/O(基本輸入/輸出)緩沖區。

 

不同的應用程序處理不同端口上的數據,同一台機器上不能有兩個程序使用同一個端口,端口號可以從0到65535,通常將它分為三類:

 

*、公認端口(Well Known Ports):從0到1023,它們緊密綁定(Binding)一些服務。

 

*、注冊端口(Registered Ports):從1024到49151。它們松散地綁定一些服務。

 

*、動態和/或私有端口(Dynamic and/or Private Ports):從49152到65535,這些端口是應用程序使用的動態端口,應用程序一般不會主動使用這些端口。

 

三、兩類傳輸協議:TCP;UDP

 

盡管TCP/IP協議的名稱中只有TCP這個協議名,但是在TCP/IP的傳輸層同時存在TCP和UDP兩個協議。

 

   1、TCP是Tranfer Control Protocol的簡稱,是一種面向連接的保證可靠傳輸的協議。通過TCP協議傳輸,得到的是一個順序的無差錯的數據流。發送方和接收方的成對的兩個 socket之間必須建立連接,以便在TCP協議的基礎上進行通信,當一個socket(通常都是server socket)等待建立連接時,另一個socket可以要求進行連接,一旦這兩個socket連接起來,它們就可以進行雙向數據傳輸,雙方都可以進行發送或接收操作。TCP是Tranfer Control Protocol的 簡稱,是一種面向連接的保證可靠傳輸的協議。通過TCP協議傳輸,得到的是一個順序的無差錯的數據流。發送方和接收方的成對的兩個socket之間必須建立連接,以便在TCP協議的基礎上進行通信,當一個socket(通常都是server socket)等待建立連接時,另一個socket可以要求進行連接,一旦這兩個socket連接起來,它們就可以進行雙向數據傳輸,雙方都可以進行發送 或接收操作。

 

2、UDP是User Datagram Protocol的簡稱,是一種無連接的協議,每個數據報都是一個獨立的信息,包括完整的源地址或目的地址,它在網絡上以任何可能的路徑傳往目的地,因此能否到達目的地,到達目的地的時間以及內容的正確性都是不能被保證的。

 

比較:

 

UDP:

 

1,     每個數據報中都給出了完整的地址信息,因此無需要建立發送方和接收方的連接。

 

2,     UDP傳輸數據時是有大小限制的,每個被傳輸的數據報必須限定在64KB之內。

 

3,     UDP是一個不可靠的協議,發送方所發送的數據報並不一定以相同的次序到達接收方

 

TCP:

 

1,  面向連接的協議,在socket之間進行數據傳輸之前必然要建立連接,所以在TCP中需要連接時間。

 

2,  TCP傳輸數據大小限制,一旦連接建立起來,雙方的socket就可以按統一的格式傳輸大的數據。

 

3,  TCP是一個可靠的協議,它確保接收方完全正確地獲取發送方所發送的全部數據。

 

應用:

 

1,TCP在網絡通信上有極強的生命力,例如遠程連接(Telnet)和文件傳輸(FTP)都需要不定長度的數據被可靠地傳輸。但是可靠的傳輸是要付出代價的,對數據內容正確性的檢驗必然占用計算機的處理時間和網絡的帶寬,因此TCP傳輸的效率不如UDP高。

 

2,UDP操作簡單,而且僅需要較少的監護,因此通常用於局域網高可靠性的分散系統中client/server應用程序。例如視頻會議系統,並不要求音頻視頻數據絕對的正確,只要保證連貫性就可以了,這種情況下顯然使用UDP會更合理一些。

 

補充:UDP協議的幾個特性

 

 

 

(1) UDP是一個無連接協議,傳輸數據之前源端和終端不建立連接,當

 

  

 

UDP

 

它想傳送時就簡單地去抓取來自應用程序的數據,並盡可能快地把它扔到網絡上。在發送端,UDP傳送數據的速度僅僅是受應用程序生成數據的速度、計算機的能力和傳輸帶寬的限制;在接收端,UDP把每個消息段放在隊列中,應用程序每次從隊列中讀一個消息段。

 

  (2) 由於傳輸數據不建立連接,因此也就不需要維護連接狀態,包括收發狀態等,因此一台服務機可同時向多個客戶機傳輸相同的消息。

 

  (3) UDP信息包的標題很短,只有8個字節,相對於TCP的20個字節信息包的額外開銷很小。

 

  (4) 吞吐量不受擁擠控制算法的調節,只受應用軟件生成數據的速率、傳輸帶寬、源端和終端主機性能的限制。

 

(5)UDP使用盡最大努力交付,即不保證可靠交付,因此主機不需要維持復雜的鏈接狀態表(這里面有許多參數)。

 

(6)UDP是面向報文的。發送方的UDP對應用程序交下來的報文,在添加首部后就向下交付給IP層。既不拆分,也不合並,而是保留這些報文的邊界,因此,應用程序需要選擇合適的報文大小。

 

雖然UDP是一個不可靠的協議,但它是分發信息的一個理想協議。例如,在屏幕上報告股票市場、在屏幕上顯示航空信息等等。UDP也用在路由信息協議RIP(Routing Information Protocol)中修改路由表。在這些應用場合下,如果有一個消息丟失,在幾秒之后另一個新的消息就會替換它。UDP廣泛用在多媒體應用中,例如,Progressive Networks公司開發的RealAudio軟件,它是在因特網上把預先錄制的或者現場音樂實時傳送給客戶機的一種軟件,該軟件使用的RealAudio audio-on-demand protocol協議就是運行在UDP之上的協議,大多數因特網電話軟件產品也都運行在UDP之上

 

四、在Java中操縱UDP
使用位於JDK中Java.NET包下的DatagramSocket和DatagramPacket類,可以非常方便地控制用戶數據報文。

 

DatagramSocket類:創建接收和發送UDP的Socket實例

 

DatagramSocket():創建實例。通常用於客戶端編程,它並沒有特定監聽的端口,僅僅使用一個臨時的。

 

DatagramSocket(int port):創建實例,並固定監聽Port端口的報文。

 

DatagramSocket(int port, InetAddress localAddr):這是個非常有用的構建器,當一台機器擁有多於一個IP地址的時候,由它創建的實例僅僅接收來自LocalAddr的報文。

 

receive(DatagramPacket d):接收數據報文到d中。receive方法產生一個“阻塞”。

 

send(DatagramPacket d):發送報文d到目的地。

 

setSoTimeout(int timeout):設置超時時間,單位為毫秒。

 

close():關閉DatagramSocket。在應用程序退出的時候,通常會主動釋放資源,關閉Socket,但是由於異常地退出可能造成資源無法回收。所以,應該在程序完成時,主動使用此方法關閉Socket,或在捕獲到異常拋出后關閉Socket。

 

DatagramPacket:用於處理報文,將byte數組、目標地址、目標端口等數據包裝成報文或者將報文拆卸成byte數組。

 

DatagramPacket(byte[] buf, int length, InetAddress addr, int port):從buf數組中,取出length長的數據創建數據包對象,目標是addr地址,port端口。

 

DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):從buf數組中,取出offset開始的、length長的數據創建數據包對象,目標是addr地址,port端口。

 

DatagramPacket(byte[] buf, int offset, int length):將數據包中從offset開始、length長的數據裝進buf數組。

 

DatagramPacket(byte[] buf, int length):將數據包中length長的數據裝進buf數組。

 

getData():它從實例中取得報文的byte數組編碼。

 

五、Socket的java網絡編程

 

(1)基於Socket的java網絡編程

 

1,什么是Socket

 

網絡上的兩個程序通過一個雙向的通訊連接實現數據的交換,這個雙向鏈路的一端稱為一個Socket。Socket通常用來實現客戶方和服務方的連接。Socket是TCP/IP協議的一個十分流行的編程界面,一個Socket由一個IP地址和一個端口號唯一確定。

 

但是,Socket所支持的協議種類也不光TCP/IP一種,因此兩者之間是沒有必然聯系的。在Java環境下,Socket編程主要是指基於TCP/IP協議的網絡編程。

 

SOCKET(套接字/管套/插口):標識連接的端點,IP地址 + 端口號。

 

應用程序與網絡之間的接口

 

       應用程序創建socket

 

       socket 類型 決定了通信的類型

 

       可靠的 vs. 盡最大努力的

 

        面向連接的 vs. 無連接的

 

一旦socket配置完成,應用程序就可以

 

          把數據傳給socket,從而進行網絡傳輸

 

         從socket接收數據(其他主機通過網絡發送過來的)

 

 

 

2,Socket通訊的過程

 

Server端Listen(監聽)某個端口是否有連接請求,Client端向Server 端發出Connect(連接)請求,Server端向Client端發回Accept(接受)消息。一個連接就建立起來了。Server端和Client 端都可以通過Send,Write等方法與對方通信。

 

對於一個功能齊全的Socket,都要包含以下基本結構,其工作過程包含以下四個基本的步驟:

 

  (1) 創建Socket;

 

  (2) 打開連接到Socket的輸入/出流;

 

  (3) 按照一定的協議對Socket進行讀/寫操作;

 

  (4) 關閉Socket.(在實際應用中,並未使用到顯示的close,雖然很多文章都推薦如此,不過在我的程序中,可能因為程序本身比較簡單,要求不高,所以並未造成什么影響。)

 

3,創建Socket

 

創建Socket

 

ServerSocket:編寫TCP網絡服務程序,首先要用到java.Net.ServerSocket類用以創建服務器Socket

 

構造方法:

 

ServerSocket(int port):創建綁定到特定端口的服務器套接字

 

ServerSocket(int port, int backlog):利用指定的backlog(服務器忙時保持連接請求的等待客戶數量),創建服務器套接字並將其綁定到指定的本地端口號。

 

ServerSocket(int port, int backlog, InetAddress bindAddr):使用指定的端口、偵聽 backlog 和要綁定到的本地 IP 地址創建服務器。

 

Socket:客戶端要與服務器建立連接,必須先創建一個Socket對象

 

常用構造方法

 

Socket(String host, int port):創建一個流套接字並將其連接到指定主機上的指定端口號。

 

Socket(InetAddress address, int port):創建一個流套接字並將其連接到指定 IP 地址的指定端口號。

 

 

 

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是必須捕獲或拋出例外。

 

4,簡單的Client/Server程序

 

1. 客戶端程序

 

  import java.io.*;

 

  import java.net.*;

 

  public class TalkClient {

 

    public static void main(String args[]) {

 

      try{

 

        Socket socket=new Socket("127.0.0.1",4700);

 

        //向本機的4700端口發出客戶請求

 

        BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));

 

        //由系統標准輸入設備構造BufferedReader對象

 

        PrintWriter os=new PrintWriter(socket.getOutputStream());

 

        //由Socket對象得到輸出流,並構造PrintWriter對象

 

        BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));

 

        //由Socket對象得到輸入流,並構造相應的BufferedReader對象

 

        String readline;

 

        readline=sin.readLine(); //從系統標准輸入讀入一字符串

 

        while(!readline.equals("bye")){

 

        //若從標准輸入讀入的字符串為 "bye"則停止循環

 

          os.println(readline);

 

          //將從系統標准輸入讀入的字符串輸出到Server

 

          os.flush();

 

          //刷新輸出流,使Server馬上收到該字符串

 

          System.out.println("Client:"+readline);

 

          //在系統標准輸出上打印讀入的字符串

 

          System.out.println("Server:"+is.readLine());

 

          //從Server讀入一字符串,並打印到標准輸出上

 

          readline=sin.readLine(); //從系統標准輸入讀入一字符串

 

        } //繼續循環

 

        os.close(); //關閉Socket輸出流

 

        is.close(); //關閉Socket輸入流

 

        socket.close(); //關閉Socket

 

      }catch(Exception e) {

 

        System.out.println("Error"+e); //出錯,則打印出錯信息

 

      }

 

  }

 

}

 

 2. 服務器端程序

 

  import java.io.*;

 

  import java.net.*;

 

  import java.applet.Applet;

 

  public class TalkServer{

 

    public static void main(String args[]) {

 

      try{

 

        ServerSocket server=null;

 

        try{

 

          server=new ServerSocket(4700);

 

        //創建一個ServerSocket在端口4700監聽客戶請求

 

        }catch(Exception e) {

 

          System.out.println("can not listen to:"+e);

 

        //出錯,打印出錯信息

 

        }

 

        Socket socket=null;

 

        try{

 

          socket=server.accept();

 

          //使用accept()阻塞等待客戶請求,有客戶

 

          //請求到來則產生一個Socket對象,並繼續執行

 

        }catch(Exception e) {

 

          System.out.println("Error."+e);

 

          //出錯,打印出錯信息

 

        }

 

        String line;

 

        BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));

 

         //由Socket對象得到輸入流,並構造相應的BufferedReader對象

 

        PrintWriter os=newPrintWriter(socket.getOutputStream());

 

         //由Socket對象得到輸出流,並構造PrintWriter對象

 

        BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));

 

         //由系統標准輸入設備構造BufferedReader對象

 

        System.out.println("Client:"+is.readLine());

 

        //在標准輸出上打印從客戶端讀入的字符串

 

        line=sin.readLine();

 

        //從標准輸入讀入一字符串

 

        while(!line.equals("bye")){

 

        //如果該字符串為 "bye",則停止循環

 

          os.println(line);

 

          //向客戶端輸出該字符串

 

          os.flush();

 

          //刷新輸出流,使Client馬上收到該字符串

 

          System.out.println("Server:"+line);

 

          //在系統標准輸出上打印讀入的字符串

 

          System.out.println("Client:"+is.readLine());

 

          //從Client讀入一字符串,並打印到標准輸出上

 

          line=sin.readLine();

 

          //從系統標准輸入讀入一字符串

 

        }  //繼續循環

 

        os.close(); //關閉Socket輸出流

 

        is.close(); //關閉Socket輸入流

 

        socket.close(); //關閉Socket

 

        server.close(); //關閉ServerSocket

 

      }catch(Exception e){

 

        System.out.println("Error:"+e);

 

        //出錯,打印出錯信息

 

      }

 

    }

 

  }

 

3、支持多客戶的client/server程序

 

前面的Client/Server程序只能實現Server和一個客戶的對話。在實際應用 中,往往是在服務器上運行一個永久的程序,它可以接收來自其他多個客戶端的請求,提供相應的服務。為了實現在服務器方給多個客戶提供服務的功能,需要對上面的程序進行改造,利用多線程實現多客戶機制。服務器總是在指定的端口上監聽是否有客戶請求,一旦監聽到客戶請求,服務器就會啟動一個專門的服務線程來響 應該客戶的請求,而服務器本身在啟動完線程之后馬上又進入監聽狀態,等待下一個客戶的到來。

 

ServerSocket serverSocket=null;

 

    boolean listening=true;

 

    try{

 

      serverSocket=new ServerSocket(4700);

 

      //創建一個ServerSocket在端口4700監聽客戶請求

 

    }catch(IOException e) {  }

 

    while(listening){ //永遠循環監聽

 

      new ServerThread(serverSocket.accept(),clientnum).start();

 

      //監聽到客戶請求,根據得到的Socket對象和

 

       客戶計數創建服務線程,並啟動之

 

      clientnum++; //增加客戶計數

 

    }

 

    serverSocket.close(); //關閉ServerSocket

 

設計ServerThread類

 

 public class ServerThread extends Thread{

 

   Socket socket=null; //保存與本線程相關的Socket對象

 

   int clientnum; //保存本進程的客戶計數

 

   public ServerThread(Socket socket,int num) { //構造函數

 

    this.socket=socket; //初始化socket變量

 

    clientnum=num+1; //初始化clientnum變量

 

   }

 

   public void run() { //線程主體

 

    try{//在這里實現數據的接受和發送}

 

 4、   DatagramSocket 類
       要收發DatagramPacket必須打開一個數據報socket ,當服務器構造DatagramSocket時。

 

 

 

1.1 服務器和客戶端的服務器 

 

 

 

      兩者使用的socket都是一樣的,區別僅僅在於服務器端的端口是已知端口,而客戶端的端口是系統分配的。

 

     TCP端口和UDP端口之間沒有關聯,所以兩者可以共同綁定在同一個端口上,而不會有相互影響。

 

 

 

5、 DatagramSocket 類的構造函數   

 

 

 

      DatagramSocket創建一個在指定端口監聽的入站數據報的 socket  ,使用此構造函數可以編寫出在指導的端口監聽的服務器。

 

      如果服務器在匿名端口監聽,客戶端就無法與之聯系。

 

     DatagramSocket 中的receive 方法,是阻塞方法,只有當接收到數據的時候,才會進行下面的代碼,否則只會阻塞當前的進程。

 

利用UDP協議來收發數據,都是將數據放在DatagramPacket 中,而TCP協議都是放在流中,通過getInputStream 和 getOutputStream 函數來獲得流。

 

       在服務器端UDP需要調用含有端口參數的DatagramSocket 構造函數 , 在客戶端設置DatagramSocket時,調用匿名端口構造函數。

 

       然后在構造DatagramPacket 構造函數的時候,發送端需要制定發送主機的 主機名 和 端口 。

 

基於URL的高層次Java網絡編程

 

 8.2.1一致資源定位器URL

 

  URL(Uniform Resource Locator)是一致資源定位器的簡稱,它表示Internet上某一資源的地址。通過URL我們可以訪問Internet上的各種網絡資源,比如最常見的WWW,FTP站點。瀏覽器通過解析給定的URL可以在網絡上查找相應的文件或其他資源。

 

  URL是最為直觀的一種網絡定位方法。使用 URL符合人們的語言習慣,容易記憶,所以應用十分廣泛。而且在目前使用最為廣泛的TCP/IP中對於URL中主機名的解析也是協議的一個標准,即所謂的域名解析服務。使用URL進行網絡編程,不需要對協議本身有太多的了解,功能也比較弱,相對而言是比較簡單的,所以在這里我們先介紹在Java中如何使用 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  協議名://機器名+端口號+文件名+內部引用

 

  

 

  端口號是和Socket編程相關的一個概念,初學者不必在此深究,在后面會有詳細講解。內部引用是HTML中的標記,有興趣的讀者可以參考有關HTML的書籍。

 

 

 

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){

 

  …

 

  //exception handler code here

 

  …

 

  }

 

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的錨

 

 

 

  下面的例子中,我們生成一個URL 對象,並獲取它的各個屬性。

 

  import java.net.*;

 

  import java.io.*;

 

  public class ParseURL{

 

  public static void main (String [] args) throws Exception{

 

  URL Aurl=new URL(" http://java.sun.com:80/docs/books/ ");

 

  URL tuto=new URL(Aurl,"tutorial.intro.html#DOWNLOADING");

 

   System.out.println("protocol="+ tuto.getProtocol());

 

   System.out.println("host ="+ tuto.getHost());

 

   System.out.println("filename="+ tuto.getFile());

 

   System.out.println("port="+ tuto.getPort());

 

   System.out.println("ref="+tuto.getRef());

 

   System.out.println("query="+tuto.getQuery());

 

   System.out.println("path="+tuto.getPath());

 

   System.out.println("UserInfo="+tuto.getUserInfo());

 

   System.out.println("Authority="+tuto.getAuthority());

 

  }

 

  }

 

  執行結果為:

 

    protocol=http host =java.sun.com filename=/docs/books/tutorial.intro.html

 

   port=80

 

    ref=DOWNLOADING

 

   query=null

 

    path=/docs/books/tutorial.intro.html

 

   UserInfo=null

 

    Authority=java.sun.com:80

 

 

 

 

 

8.2.5 從URL讀取WWW網絡資源

 

  當我們得到一個URL對象后,就可以通過它讀取指定的WWW資源。這時我們將使用URL的方法openStream(),其定義為:

 

         InputStream openStream();

 

  

 

  方法openSteam()與指定的URL建立連接並返回InputStream類的對象以從這一連接中讀取數據。

 

  public class URLReader {

 

  public static void main(String[] args) throws Exception {

 

                      //聲明拋出所有例外

 

    URL tirc = new URL(" http://www.tirc1.cs.tsinghua.edu.cn/ ");

 

                      //構建一URL對象

 

    BufferedReader in = new BufferedReader(new InputStreamReader(tirc.openStream()));

 

    //使用openStream得到一輸入流並由此構造一個BufferedReader對象

 

    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://edu.chinaren.com/index.shtml 的對象,然后用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();

 

  通過返回的輸入/輸出流我們可以與遠程對象進行通信。看下面的例子:

 

  URL url =new URL (" http://www.javasoft.com/cgi-bin/backwards ");

 

  //創建一URL對象

 

  URLConnectin con=url.openConnection();

 

  //由URL對象獲取URLConnection對象

 

   DataInputStream dis=new DataInputStream (con.getInputSteam());

 

  //由 URLConnection獲取輸入流,並構造DataInputStream對象

 

  PrintStream ps=new PrintSteam(con.getOutupSteam());

 

  //由URLConnection獲取輸出流,並構造 PrintStream對象

 

  String line=dis.readLine(); //從服務器讀入一行

 

   ps.println("client…"); //向服務器寫出字符串 "client…"

 

  其中backwards為服務器端的CGI程序。實際上,類URL的方法openSteam()是通過URLConnection來實現的。它等價於

 

     openConnection().getInputStream();

  基於URL的網絡編程在底層其實還是基於下面要講的 Socket接口的。WWW,FTP等標准化的網絡服務都是基於TCP協議的,所以本質上講URL編程也是基於TCP的一種應用。

 

 

java網絡編程之socket網絡編程示例(服務器端/客戶端)

 

 

 

Java為TCP協議提供了兩個類,分別在客戶端編程和服務器端編程中使用它們。在應用程序開始通信之前,需要先創建一個連接,由客戶端程序發起;而服務器端的程序需要一直監聽着主機的特定端口號,等待客戶端的連接。在客戶端中我們只需要使用Socket實例,而服務端要同時處理ServerSocket實例和Socket實例;二者並且都使用OutputStream和InpuStream來發送和接收數據。

 

學習一種知識最好的方式就是使用它,通過前面的筆記,我們已經知道如何獲取主機的地址信息,現在我們通過一個簡單的程序來初步學習傳輸層使用了TCP協議的Socket編程。

 

TCP服務器端

 

在Socket編程中,服務器端遠比客戶端要復雜得多。服務器端的工作就是建立一個通信終端,被動的等待客戶端的連接。下面這個服務器端程序的示例的作用是:監聽從控制台輸入獲取的端口號,並且將客戶端發送過來的消息,再發送回去。

 

復制代碼 代碼如下:

 


importjava.net.*;
importjava.text.MessageFormat;
importjava.io.*;

publicclassTCPEchoServer{

privatestaticfinalintBUFSIZE=32;

publicstaticvoidmain(String[]args)throwsIOException{
//TODOAuto-generatedmethodstub
    //從控制台獲取需要監聽的端口號
if(args.length!=1)
thrownewIllegalArgumentException("Parameter(s):<Port>");
//獲取端口號
intservPort=Integer.parseInt(args[0]);
//實例化一個ServerSocket對象實例
ServerSocketservSocket=newServerSocket(servPort);
System.out.println(MessageFormat.format("開始啟動監聽,端口號:{0}",args[0]));

//初始接收數據的總字節數
intrecvMsgSize;
//接收數據的緩沖區
byte[]receiveBuf=newbyte[BUFSIZE];

//循環迭代,監聽端口號,處理新的連接請求
while(true){
//阻塞等待,每接收到一個請求就創建一個新的連接實例
SocketclntSocket=servSocket.accept();
//獲取連接的客戶端的SocketAddress
SocketAddressclientAddress=clntSocket.getRemoteSocketAddress();
//打印輸出連接客戶端地址信息
System.out.println("Handlingclientat"+clientAddress);
//從客戶端接收數據的對象
InputStreamin=clntSocket.getInputStream();
//向客戶端發送數據的對象
OutputStreamout=clntSocket.getOutputStream();
//讀取客戶端發送的數據后,再發送到客戶端
while((recvMsgSize=in.read(receiveBuf))!=-1){
out.write(receiveBuf,0,recvMsgSize);
}
//客戶端關閉連接時,關閉連接
System.out.println("客戶端關閉連接");
clntSocket.close();
}
}
}

 


TCP客戶端

在Socket編程中,首先客戶端需要向服務器端發送,然后被動的等待服務器端的響應。下面的示例中:我們向服務器端發送信息,等待服務器端發送的消息,並打印顯示出來。 

 

復制代碼 代碼如下:

 


importjava.io.*;
importjava.net.Socket;
importjava.net.SocketException;

publicclassTCPEchoClient{
publicstaticvoidmain(String[]args)throwsIOException{
//TODOAuto-generatedmethodstub
    //判斷從控制台接受的參數是否正確
if((args.length<2)||(args.length>3))
thrownewIllegalArgumentException(
"Parameter(s):<Server><Word>[<Port>]]");
//獲取服務器地址
Stringserver=args[0];
//獲取需要發送的信息
byte[]data=args[1].getBytes();
//如果有三個從參數那么就獲取發送信息的端口號,默認端口號為8099
intservPort=(args.length==3)?Integer.parseInt(args[2]):8099;
//根據服務器地址和端口號實例化一個Socket實例
Socketsocket=newSocket(server,servPort);
System.out.println("Connectedtoserver...sendingechostring");
//返回此套接字的輸入流,即從服務器接受的數據對象
InputStreamin=socket.getInputStream();
//返回此套接字的輸出流,即向服務器發送的數據對象
OutputStreamout=socket.getOutputStream();
//向服務器發送從控制台接收的數據
out.write(data);
//接收數據的計數器,將寫入數據的初始偏移量
inttotalBytesRcvd=0;
//初始化接收數據的總字節數
intbytesRcvd;
while(totalBytesRcvd<data.length){
//服務器關閉連接,則返回-1,read方法返回接收數據的總字節數
if((bytesRcvd=in.read(data,totalBytesRcvd,data.length
-totalBytesRcvd))==-1)
thrownewSocketException("與服務器的連接已關閉");
totalBytesRcvd+=bytesRcvd;
}
//打印服務器發送來的數據
System.out.println("Received:"+newString(data));
//關閉連接
socket.close();
}
}

 

首先運行服務器端,監聽8099端口:

 

接着運行客戶端程序,並且向服務器端發送消息:

 

再次查看我們的服務器端控制台,我們可以看到前面客戶端連接的地址信息:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM