java知識總結(十三)網絡編程

原文來自搬磚工,如需轉載請註明出處


這篇文章簡單總結一下java網絡編程的相關知識

一、TCP和UDP

1.TCP(傳輸控制協議)方式
TCP方式就類似於撥打電話,使用該種方式進行網絡通訊時,需要建立專門的虛擬連接,然後進行可靠的數據傳輸,如果數據發送失敗,則客戶端會自動重發該數據
2. UDP(用戶數據報協議)方式
     UDP方式就類似於發送短信,使用這種方式進行網絡通訊時,不需要建立專門的虛擬連接,傳輸也不是很可靠,如果發送失敗則客戶端無法獲得
 這兩種傳輸方式都是實際的網絡編程中進行使用,重要的數據一般使用TCP方式進行數據傳輸,而大量的非核心數據則都通過UDP方式進行傳遞,在一些程序中甚至結合使用這兩種方式進行數據的傳遞。由於TCP需要建立專用的虛擬連接以及確認傳輸是否正確,所以使用TCP方式的速度稍微慢一些,而且傳輸時產生的數據量要比UDP稍微大一些。
總結一下UDP和TCP協議的區別
–使用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地址和域名相關的操作方法包含在該類的內部。
關於該類的使用,下面通過一個基礎的代碼示例演示該類的使用,代碼如下:

import java.net.InetAddress;

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(回聲)服務實現爲例子,介紹綜合使用示例,實現的代碼如下:

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

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方法發送該對象即可
代碼示例:

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類型的對象內部
示例代碼:

//構造緩衝數組  
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()

ps:總結的不是很全面,但很基礎,應該適合初學者學習,由於本人也是初學者的小菜鳥,所有很多東西可能都涉及不到,希望大家見諒!




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章