JAVA網絡通訊基礎【轉】

          java網絡通訊編程              html

       首先聲明一下,剛開始學習java網絡通訊編程就對他有一種畏懼感,由於本身對網絡一竅不通,因此。。。呵呵。。你懂得,昨天又仔細的學習了一遍,感受其實java網絡編程也沒想象的那麼難,不信,咱一塊兒看看。。。呵呵。。java

     網絡編程就是在兩個或兩個以上的設備(例如計算機)之間傳輸數據。程序員所做的事情就是把數據發送到指定的位置,或者接收到指定的數據,這個就是狹義的網絡編程範疇。在發送和接收數據時,大部分的程序設計語言都設計了專門的API實現這些功能,程序員只須要調用便可。因此,基礎的網絡編程能夠和打電話同樣簡單程序員

一:首先看一下網絡通信的兩種方式編程

1.TCP(傳輸控制協議)方式數組

TCP方式就相似於撥打電話,使用該種方式進行網絡通信時,須要創建專門的虛擬鏈接,而後進行可靠的數據傳輸,若是數據發送失敗,則客戶端會自動重發該數據服務器

2. UDP(用戶數據報協議)方式網絡

     UDP方式就相似於發送短信,使用這種方式進行網絡通信時,不須要創建專門的虛擬鏈接,傳輸也不是很可靠,若是發送失敗則客戶端沒法得到socket

 這兩種傳輸方式都是實際的網絡編程中進行使用,重要的數據通常使用TCP方式進行數據傳輸,而大量的非核心數據則都經過UDP方式進行傳遞,在一些程序中甚至結合使用這兩種方式進行數據的傳遞。因爲TCP須要創建專用的虛擬鏈接以及確認傳輸是否正確,因此使用TCP方式的速度稍微慢一些,並且傳輸時產生的數據量要比UDP稍微大一些。函數

總結一下UDPTCP協議的區別學習

-使用UDP時,每一個數據報中都給出了完整的地址信息,所以無須要創建發送方和接收方的鏈接。 

–對於TCP協議,因爲它是一個面向鏈接的協議,在socket之間進行數據傳輸以前必然要創建鏈接,因此在TCP中多了一個鏈接創建的時間 

–使用UDP傳輸數據時是有大小限制的,每一個被傳輸的數據報必須限定在64KB以內。 

–TCP沒有這方面的限制,一旦鏈接創建起來,雙方的socket就能夠按統一的格式傳輸大量的數據。 

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

–TCP是一個可靠的協議,它確保接收方徹底正確地獲取發送方所發送的所有數據 

-TCP在網絡通訊上有極強的生命力,例如遠程鏈接(Telnet)和文件傳輸(FTP)都須要不定長度的數據被可靠地傳輸。 

—相比之下UDP操做簡單,並且僅須要較少的監護,所以一般用於局域網高可靠性的分散系統中client/server應用程序 

二:基於url的網絡編程

1.建立一個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){   …  }

2. 解析一個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的錨

3.從URL讀取WWW網絡資源

當咱們獲得一個URL對象後,就能夠經過它讀取指定的WWW資源。這時咱們將使用URL的方法openStream(),其定義爲: InputStream openStream();      方法openSteam()與指定的URL創建鏈接並返回InputStream類的對象以從這一鏈接中讀取數據。 URL url = new URL("http://www.baidu.com");

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

BufferedReader br = new BufferedReader(new InputStreamReader( url.openStream()));

String line = null;

while(null != (line = br.readLine()))

{

System.out.println(line);

}

br.close();

三:客戶端網絡編程步驟

按照前面的基礎知識介紹,不管使用TCP方式仍是UDP方式進行網絡通信,網絡編程都是由客戶端和服務器端組成

1.客戶端網絡編程步驟

        客戶端(Client)是指網絡編程中首先發起鏈接的程序,客戶端通常實現程序界面和基本邏輯實現,在進行實際的客戶端編程時,不管客戶端複雜仍是簡單,以及客戶端實現的方式,客戶端的編程主要由三個步驟實現:

1、 創建網絡鏈接

客戶端網絡編程的第一步都是創建網絡鏈接。在創建網絡鏈接時須要指定鏈接到的服務器的IP地址和端口號,創建完成之後,會造成一條虛擬的鏈接,後續的操做就能夠經過該鏈接實現數據交換了。

2、 交換數據

鏈接創建之後,就能夠經過這個鏈接交換數據了。交換數據嚴格按照請求響應模型進行,由客戶端發送一個請求數據到服務器,服務器反饋一個響應數據給客戶端,若是客戶端不發送請求則服務器端就不響應。

根據邏輯須要,能夠屢次交換數據,可是仍是必須遵循請求響應模型。

3、 關閉網絡鏈接

在數據交換完成之後,關閉網絡鏈接,釋放程序佔用的端口、內存等系統資源,結束網絡編程。

        最基本的步驟通常都是這三個步驟,在實際實現時,步驟2會出現重複,在進行代碼組織時,因爲網絡編程是比較耗時的操做,因此通常開啓專門的現場進行網絡通信。

2.服務器端網絡編程步驟

        服務器端(Server)是指在網絡編程中被動等待鏈接的程序,服務器端通常實現程序的核心邏輯以及數據存儲等核心功能。服務器端的編程步驟和客戶端不一樣,是由四個步驟實現,依次是:

1、 監聽端口

服務器端屬於被動等待鏈接,因此服務器端啓動之後,不須要發起鏈接,而只須要監聽本地計算機的某個固定端口便可。

這個端口就是服務器端開放給客戶端的端口,服務器端程序運行的本地計算機的IP地址就是服務器端程序的IP地址。

2、 得到鏈接

當客戶端鏈接到服務器端時,服務器端就能夠得到一個鏈接,這個鏈接包含客戶端的信息,例如客戶端IP地址等等,服務器端和客戶端也經過該鏈接進行數據交換。

通常在服務器端編程中,當得到鏈接時,須要開啓專門的線程處理該鏈接,每一個鏈接都由獨立的線程實現。

3、 交換數據

服務器端經過得到的鏈接進行數據交換。服務器端的數據交換步驟是首先接收客戶端發送過來的數據,而後進行邏輯處理,再把處理之後的結果數據發送給客戶端。簡單來講,就是先接收再發送,這個和客戶端的數據交換數序不一樣。

其實,服務器端得到的鏈接和客戶端鏈接是同樣的,只是數據交換的步驟不一樣。

固然,服務器端的數據交換也是能夠屢次進行的。

在數據交換完成之後,關閉和客戶端的鏈接。

4、 關閉鏈接

當服務器程序關閉時,須要關閉服務器端,經過關閉服務器端使得服務器監聽的端口以及佔用的內存能夠釋放出來,實現了鏈接的關閉。

四:一個基礎的網絡類——InetAddress類

    該類的功能是表明一個IP地址,而且將IP地址和域名相關的操做方法包含在該類的內部。

    關於該類的使用,下面經過一個基礎的代碼示例演示該類的使用,代碼以下:

  1. <span style="rgb(51, 51, 51);">public class InetAddressDemo {  
  2.   
  3. public static void main(String[] args) {  
  4.   
  5. try {  
  6.   
  7. // 使用域名建立對象  
  8.   
  9. InetAddress inet1 = InetAddress.getByName("www.163.com");  
  10.   
  11. System.out.println(inet1);  
  12.   
  13. // 使用IP建立對象  
  14.   
  15. InetAddress inet2 = InetAddress.getByName("127.0.0.1");  
  16.   
  17. System.out.println(inet2);  
  18.   
  19. // 得到本機地址對象  
  20.   
  21. InetAddress inet3 = InetAddress.getLocalHost();  
  22.   
  23. System.out.println(inet3);  
  24.   
  25. // 得到對象中存儲的域名  
  26.   
  27. String host = inet3.getHostName();  
  28.   
  29. System.out.println("域名:" + host);  
  30.   
  31. // 得到對象中存儲的IP  
  32.   
  33. String ip = inet3.getHostAddress();  
  34.   
  35. System.out.println("IP:" + ip);  
  36.   
  37. catch (Exception e) {  
  38. }  
  39. }  
  40. }  
  41.   </span>  
public class InetAddressDemo {

public static void main(String[] args) {

try {

// 使用域名建立對象

InetAddress inet1 = InetAddress.getByName("www.163.com");

System.out.println(inet1);

// 使用IP建立對象

InetAddress inet2 = InetAddress.getByName("127.0.0.1");

System.out.println(inet2);

// 得到本機地址對象

InetAddress inet3 = InetAddress.getLocalHost();

System.out.println(inet3);

// 得到對象中存儲的域名

String host = inet3.getHostName();

System.out.println("域名:" + host);

// 得到對象中存儲的IP

String ip = inet3.getHostAddress();

System.out.println("IP:" + ip);

} catch (Exception e) {
}
}
}
  

注:InetAddress 類沒有明顯的構造函數。爲生成一個InetAddress對象,必須運用一個可用的工廠方法。 

–工廠方法(factory method)僅是一個類中靜態方法返回一個該類實例的約定。對於InetAddress,三個方法 getLocalHost( )、getByName( )以及getAllByName( )能夠用來建立InetAddress的實例 

•若是這些方法不能解析主機名,它們引起一個UnknownHostException異常。 

五:TCP編程

在Java語言中,對於TCP方式的網絡編程提供了良好的支持,在實際實現時,以java.net.Socket類表明客戶端鏈接,以java.net.ServerSocket類表明服務器端鏈接。在進行網絡編程時,底層網絡通信的細節已經實現了比較高的封裝,因此在程序員實際編程時,只須要指定IP地址和端口號碼就能夠創建鏈接了。

在客戶端網絡編程中,首先須要創建鏈接,在Java API中以java.net.Socket類的對象表明網絡鏈接

客戶端

1) 創建Socket鏈接 

Socket socket2 = new Socket(www.sohu.com,80);

2)按照「請求-響應」模型進行網絡數據交換

在Java語言中,數據傳輸功能由Java IO實現,也就是說只須要從鏈接中得到輸入流和輸出流便可,而後將須要發送的數據寫入鏈接對象的輸出流中,在發送完成之後從輸入流中讀取數據便可。示例代碼以下:

 OutputStream os = socket1.getOutputStream(); //得到輸出流

 InputStream is = socket1.getInputStream();     //得到輸入流

這裏得到的只是最基本的輸出流和輸入流對象,還能夠根據前面學習到的IO知識,使用流的嵌套將這些得到到的基本流對象轉換成須要的裝飾流對象,從而方便數據的操做。

3)關閉網絡鏈接

 socket1.close();

服務器端

首先須要說明的是,客戶端的步驟和服務器端的編寫步驟不一樣,因此在學習服務器端編程時注意不要和客戶端混淆起來。

1)監聽端口

ServerSocket ss = new ServerSocket(10000);

2)得到鏈接

當有客戶端鏈接到達時,創建一個和客戶端鏈接對應的Socket連 接對象,從而釋放客戶端鏈接對於服務器端端口的佔用

Socket socket = ss.accept();

該代碼實現的功能是得到當前鏈接到服務器端的客戶端鏈接。須要說明的是accept和前面IO部分介紹的read方法同樣,都是一個阻塞方法,也就是當無鏈接時,該方法將阻塞程序的執行,直到鏈接到達時才執行該行代碼。另外得到的鏈接會在服務器端的該端口註冊,這樣之後就能夠經過在服務器端的註冊信息直接通訊,而註冊之後服務器端的端口就被釋放出來,又能夠繼續接受其它的鏈接了。

3)按照「請求-響應」模型進行網絡數據交換

這裏得到的Socket類型的鏈接就和客戶端的網絡鏈接同樣了,只是服務器端須要首先讀取發送過來的數據,而後進行邏輯處理之後再發送給客戶端,也就是交換數據的順序和客戶端交換數據的步驟恰好相反

 InputStream is = ss.getInputStream();     //得到輸入流

OutputStream os = ss.getOutputStream(); //得到輸出流

4)關閉服務器端鏈接

ss.close();

以上就是基本的TCP類型的服務器和客戶端代碼實現的步驟,下面以一個簡單的echo(回聲)服務實現爲例子,介紹綜合使用示例,實現的代碼以下:

  1. public class Constants {  
  2.   
  3.     public static void main(String[] args) {  
  4.         ServerSocket serverSocket = null;  
  5.         Socket socket = null;  
  6.         OutputStream os = null;  
  7.         InputStream is = null;  
  8.         // 監聽端口號  
  9.         int port = 10000;  
  10.         try {  
  11.             // 創建鏈接  
  12.         serverSocket = new ServerSocket(port);  
  13.             // 得到鏈接  
  14.             socket = serverSocket.accept();  
  15.             // 接收客戶端發送內容  
  16.             is = socket.getInputStream();  
  17.             byte[] b = new byte[1024];  
  18.             int n = is.read(b);  
  19.             // 輸出  
  20.             System.out.println("客戶端發送內容爲:" + new String(b, 0, n));  
  21.             // 向客戶端發送反饋內容  
  22.             os = socket.getOutputStream();  
  23.             os.write(b, 0, n);  
  24.         } catch (Exception e) {  
  25.         e.printStackTrace();  
  26.         } finally {  
  27.             try {  
  28.                 // 關閉流和鏈接  
  29.                 os.close();  
  30.                 is.close();  
  31.                 socket.close();  
  32.                 serverSocket.close();  
  33.             } catch (Exception e) {  
  34.             }  
  35.         }  
  36.     }  
  37. }  
public class Constants {

	public static void main(String[] args) {
		ServerSocket serverSocket = null;
		Socket socket = null;
		OutputStream os = null;
		InputStream is = null;
		// 監聽端口號
		int port = 10000;
		try {
			// 創建鏈接
		serverSocket = new ServerSocket(port);
			// 得到鏈接
			socket = serverSocket.accept();
			// 接收客戶端發送內容
			is = socket.getInputStream();
			byte[] b = new byte[1024];
			int n = is.read(b);
			// 輸出
			System.out.println("客戶端發送內容爲:" + new String(b, 0, n));
			// 向客戶端發送反饋內容
			os = socket.getOutputStream();
			os.write(b, 0, n);
		} catch (Exception e) {
		e.printStackTrace();
		} finally {
			try {
				// 關閉流和鏈接
				os.close();
				is.close();
				socket.close();
				serverSocket.close();
			} catch (Exception e) {
			}
		}
	}
}

UDP編程

     UDP(User Datagram Protocol),中文意思是用戶數據報協議使用該種方式無需創建專用的虛擬鏈接,因爲無需創建專用的鏈接,因此對於服務器的壓力要比TCP小不少,因此也是一種常見的網絡編程方式。可是使用該種方式最大的不足是傳輸不可靠,固然也不是說常常丟失,就像你們發短信息同樣,理論上存在收不到的可能

     在Java API中,實現UDP方式的編程,包含客戶端網絡編程和服務器端網絡編程,主要由兩個類實現,分別是:

l DatagramSocket

DatagramSocket類實現「網絡鏈接」,包括客戶端網絡鏈接和服務器端網絡鏈接。雖然UDP方式的網絡通信不須要創建專用的網絡鏈接,可是畢竟仍是須要發送和接收數據,DatagramSocket實現的就是發送數據時的發射器,以及接收數據時的監聽器的角色。類比於TCP中的網絡鏈接,該類既能夠用於實現客戶端鏈接,也能夠用於實現服務器端鏈接。

l DatagramPacket

DatagramPacket類實現對於網絡中傳輸的數據封裝,也就是說,該類的對象表明網絡中交換的數據。在UDP方式的網絡編程中,不管是須要發送的數據仍是須要接收的數據,都必須被處理成DatagramPacket類型的對象,該對象中包含發送到的地址、發送到的端口號以及發送的內容等。其實DatagramPacket類的做用相似於現實中的信件,在信件中包含信件發送到的地址以及接收人,還有發送的內容等,郵局只須要按照地址傳遞便可。在接收數據時,接收到的數據也必須被處理成DatagramPacket類型的對象,在該對象中包含發送方的地址、端口號等信息,也包含數據的內容。和TCP方式的網絡傳輸相比,IO編程在UDP方式的網絡編程中變得不是必須的內容,結構也要比TCP方式的網絡編程簡單一些。

UDP客戶端編程涉及的步驟也是4個部分:創建鏈接、發送數據、接收數據和關閉鏈接。

1)創建鏈接:

DatagramSocket ds = new DatagramSocket();

該客戶端鏈接使用系統隨機分配的一個本地計算機的未用端口號

 固然,能夠經過制定鏈接使用的端口號來建立客戶端鏈接。

  DatagramSocket ds = new DatagramSocket(5000);

通常在創建客戶端鏈接時沒有必要指定端口號碼。

2)發送數據

在發送數據時,須要將須要發送的數據內容首先轉換爲byte數組,而後將數據內容、服務器IP和服務器端口號一塊兒構形成一個DatagramPacket類型的對象,這樣數據的準備就完成了了,發送時調用網絡鏈接對象中的send方法發送該對象便可

代碼示例:

  1. <span style="color: rgb(51, 51, 51); rgb(102, 102, 102);">   String s = "Hello";  
  2.     String host = "127.0.0.1";  
  3.     int port = 10001;  
  4.    //將發送的內容轉換爲byte數組  
  5.     byte[] b = s.getBytes();  
  6.     //將服務器IP轉換爲InetAddress對象  
  7.     InetAddress server = InetAddress.getByName(host);  
  8.     //構造發送的數據包對象  
  9.     DatagramPacket sendDp = new DatagramPacket(b,b.length,server,port);  
  10.     //發送數據  
  11.     ds.send(sendDp);</span>  
   String s = "Hello";
    String host = "127.0.0.1";
    int port = 10001;
   //將發送的內容轉換爲byte數組
    byte[] b = s.getBytes();
    //將服務器IP轉換爲InetAddress對象
    InetAddress server = InetAddress.getByName(host);
    //構造發送的數據包對象
    DatagramPacket sendDp = new DatagramPacket(b,b.length,server,port);
    //發送數據
    ds.send(sendDp);

 

      在該示例代碼中,無論發送的數據內容是什麼,都須要轉換爲byte數組,而後將服務器端的IP地址構形成InetAddress類型的對象,在準備完成之後,將這些信息構形成一個DatagramPacket類型的對象,在UDP編程中,發送的數據內容、服務器端的IP和端口號,都包含在DatagramPacket對象中。在準備完成之後,調用鏈接對象ds的send方法把DatagramPacket對象發送出去便可。

3)UDP客戶端編程中接收數據

     首先構造一個數據緩衝數組,該數組用於存儲接收的服務器端反饋數據,該數組的長度必須大於或等於服務器端反饋的實際有效數據的長度。而後以該緩衝數組爲基礎構造一個DatagramPacket數據包對象,最後調用鏈接對象的receive方法接收數據便可。接收到的服務器端反饋數據存儲在DatagramPacket類型的對象內部

示例代碼:

  1. <span style="rgb(102, 102, 102);">      //構造緩衝數組  
  2.     byte[] data = new byte[1024];  
  3.     //構造數據包對象  
  4.     DatagramPacket receiveDp = new DatagramPacket(data,data.length);  
  5.     //接收數據  
  6.     ds.receive(receiveDp);  
  7.     //輸出數據內容  
  8.     byte[] b = receiveDp.getData(); //得到緩衝數組  
  9.     int len = receiveDp.getLength(); //得到有效數據長度  
  10.     String s = new String(b,0,len);  
  11.     System.out.println(s);</span>  
	  //構造緩衝數組
    byte[] data = new byte[1024];
    //構造數據包對象
    DatagramPacket receiveDp = new DatagramPacket(data,data.length);
    //接收數據
    ds.receive(receiveDp);
    //輸出數據內容
    byte[] b = receiveDp.getData(); //得到緩衝數組
    int len = receiveDp.getLength(); //得到有效數據長度
    String s = new String(b,0,len);
    System.out.println(s);

 

代碼講解 :  首先構造緩衝數組data,這裏設置的長度1024是預估的接收到的數據長度,要求該長度必須大於或等於接收到的數據長度,而後以該緩衝數組爲基礎,構造數據包對象,使用鏈接對象ds的receive方法接收反饋數據,因爲在Java語言中,除String之外的其它對象都是按照地址傳遞,因此在receive方法內部能夠改變數據包對象receiveDp的內容,這裏的receiveDp的功能和返回值相似。數據接收到之後,只須要從數據包對象中讀取出來就能夠了,使用DatagramPacket對象中的getData方法能夠得到數據包對象的緩衝區數組,可是緩衝區數組的長度通常大於有效數據的長度,換句話說,也就是緩衝區數組中只有一部分數據是反饋數據,因此須要使用DatagramPacket對象中的getLength方法得到有效數據的長度,則有效數據就是緩衝數組中的前有效數據長度個內容,這些纔是真正的服務器端反饋的數據的內容

4)關閉鏈接

  ds.close();

UDP方式服務器端網絡編程

1)首先UDP方式服務器端網絡編程須要創建一個鏈接,該鏈接監聽某個端口:

 DatagramSocket ds = new DatagramSocket(10010);

因爲服務器端的端口須要固定,因此通常在創建服務器端鏈接時,都指定端口號

2)接收客戶端發送過來的數據

其接收的方法和客戶端接收的方法一直,其中receive方法的做用相似於TCP方式中accept方法的做用,該方法也是一個阻塞方法,其做用是接收數據。

ds.receive()

接收到客戶端發送過來的數據之後,服務器端對該數據進行邏輯處理,而後將處理之後的結果再發送給客戶端,在這裏發送時就比客戶端要麻煩一些,由於服務器端須要得到客戶端的IP和客戶端使用的端口號,這個均可以從接收到的數據包中得到。示例代碼以下:

     //得到客戶端的IP

     InetAddress clientIP = receiveDp.getAddress();

         //得到客戶端的端口號

         Int clientPort = receiveDp.getPort();

3)關閉鏈接

ds.close()

        好了,佔時就總結到這吧,總結的不是很全面,但很基礎,應該適合初學者學習,因爲本人也是初學者的小菜鳥,全部不少東西可能都涉及不到,但願你們見諒!

 

 本文來自:曹勝歡博客專欄。轉載請註明出處:http://blog.csdn.net/csh624366188

相關文章
相關標籤/搜索