Java實現的網絡編程——基礎篇

這幾天一直在學習java的網絡編程,晚上閒下來也沒什麼事幹。就想寫點自己的認識吧。考慮的可能不周到,希望日後有機會在添加吧。對於網絡編程,什麼TCPIPhttpURL等等之類剛開始很陌生。不過隨着這幾天的學習,逐漸有了一些瞭解了。

首先我想說的是網絡模型,很多人立刻就想到了7個層。其實網絡模型有兩種:OSI(開放系統模型)模型和TCP/IP模型(這種常用)。

OSI模型主要包括7層,從下到上依次是:物理層、數據鏈層、網絡層、傳輸層、會話層、表示層、應用層。

具體的說明如下:

物理層:最底層的數據流,傳輸的數據是2進制的0101之類的。

數據鏈層:此時涉及的硬件是交換機,並且完成對MAC地址的封裝,傳輸的數據是按幀算的。

網絡層:此時好涉及的硬件是路由器,這一層主要完成的是對IP地址的封裝和解封裝。這一層傳輸的數據是數據包。

傳輸層:這次主要涉及到傳輸數據的協議(如TCPUDP)和端口號。這一層主要完成數據的分段傳輸,這一層傳輸的數據是段。

會話層:通過傳輸層的端口號建立數據的傳輸通道。主要是在系統中發送請求或者接受請求。

表示層:主要是對接受的數據進行解壓、解密之類的操作,或者是對發送的數據進行壓縮、加密的操作等。

應用層:主要是一些終端上的應用。比如web瀏覽器、QQ等。

 

至於TCP模型,主要分爲4層,從下到上依次是:數據至網絡層、網絡層、傳輸層、應用層。

這裏我補充幾個協議規則:http ftp(用於文件的上傳和下載)是應用層的規則,TCPUDP是傳輸層的使用的規則,IP是網絡層使用的規則。

之後來介紹一下通訊需要的一些必備的要素:IP地址、端口號、傳輸協議。

1、IP地址:InetAddress

• 網絡中設備的標識

• 不易記憶,可用主機名

• 本地迴環地址: 127.0.0.1 主機名: localhost

2、端口號

• 用於標識進程的邏輯地址,不同進程的標識

• 有效端口: 0~65535,其中0~1024系統使用或保留端口。

3、傳輸協議

• 通訊的規則

• 常見協議: TCP, UDP

4、TCPUDP

接下來說說TCPUDP之間的差別。

UDP:它是將數據源和目的全部封裝成數據包中,無需建立連接就可以進行通訊,所以速度快,但是可靠性不高。同時每個數據的數據包大小都必須在64k內。所以在小數據和不需要高可靠性的情況下使用。(比如:QQ聊天工具)

TCP:必須建立連接,並且生成一個數據傳輸通道。可支持大數據量的傳輸,要通過三次握手來進行連接,所以協議的可靠性較高。但是相應的效率較低。

 

5、Socket(套接字)

接下來就不得不提一個關鍵的技術叫做Socket(套接字)。Socket它是一種爲網絡服務提供的機制,通信的兩端都必須要有Socket纔可以完成通訊。網絡通訊實際上就是Socket套接字的通訊,數據是在Socket上完成IO操作的。有了套接字技術我們就可以進行數據的傳輸。這裏我們必須補充說明,要進行數據的傳輸。就必須要有兩端,一個是客戶端(主要是發送請求的),另一個是服務器端(主要是接受請求的)。


6、代碼實例

6.1  首先是UDP傳輸,這裏要介紹兩個類DatagramSocketDatagramPacket。這兩個類都在java.net包裏面。

DatagramSocket:表示用來接收和發送數據報包的套接字。

DatagramPaket:表示數據報包。

客戶端:(這裏給出部分源碼和創建思路)

注意:在發送的數據包中要明確的指出目的地的端口號。

public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		System.out.println("發送端啓動了.....");
		/*
		 *創建udp傳輸的發送端
		 * 思路:
		 * 1、建立udp的socket服務。
		 * 2、將要發送的數據封裝到數據包中
		 * 3、通過udp的socket服務將數據發送出去
		 * 4、關閉socket服務
		 */
		
		//1、upd的socket服務,使用DatagramSocket對象
		DatagramSocket ds=new DatagramSocket(8888);//給自己的Socket設的端口號
		//2、將要發送的數據封裝到數據包中
		String str="我是來自客戶端的lady小白";
		//使用DatagramPacket將數據封裝到數據包中
		byte[] buf=str.getBytes();//注意數據是以字節的方式發送的,所以要轉換爲字節數組
		DatagramPacket dp=new DatagramPacket(buf, 0, buf.length,//這裏定義一個數據包對象,該對象包含了客戶端的信息
				InetAddress.getByName("172.19.207.247"), 10000);//向172.19.207.247的ip地址的主機的10000端口發送數據
		
		//3、通過upd的socket服務將數據包發送出去,是用send()方法
		ds.send(dp);
		
		//4、關閉資源
		ds.close();
	}

服務器端:(這裏給出部分源碼和創建思路)

確保定義的端口號就是源數據包指定的端口後,否則源數據包被接受到。

/*
	 * 創建udp傳輸的接收端
	 * 思路:
	 * 1、建立udp的Socket服務,因爲是要接受數據,所以必須要
	 * 明確端口號。
	 * 2、創建數據包,用於存儲接收到的數據。方便用數據包對象的方法
	 * 來解析這些數據。
	 * 3、使用Socket服務的receive方法將接收到的數據存儲在數據
	 * 包中。
	 * 4、通過數據包的方法解析數據包中的數據
	 * 5、關閉資源。
	 */
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		System.out.println("接收端啓動......");
		//1、建立udp的Socket服務
		DatagramSocket ds=new DatagramSocket(10000);//將Socket服務的端口號設爲10000
		//2、創建數據包
		byte[] buf=new byte[1024];
		DatagramPacket dp=new DatagramPacket(buf, 0, buf.length);
		
		//3、使用接受方法將數據存儲到數據包中
		ds.receive(dp);
		
		//4、通過數據包對象的方法,解析其中的數據。比如:地址、端口、數據內容
		String ip=dp.getAddress().getHostAddress();
		int port=dp.getPort();
		String text=new String(dp.getData(),0,dp.getLength());
		System.out.println(ip+":"+port+":"+text);
		
		//關閉資源
		ds.close();
	}


  圖解 UDP傳輸


6.2  講完UDP傳輸,我們來介紹一下TCP的傳輸。這裏同樣涉及到兩個類SocketServerSocket類。這兩個類都在java.net包中。

Socket類:是實現客戶端的套接字類。

ServerSocket類:是實現此類實現服務器套接字。服務器套接字等待請求通過網絡傳入。它基於該請求執行某些操作,然後可能向請求者返回結果。 

TCP傳輸的思路如下:

客戶端:(源碼和思路如下)

注意:在客戶端中要明確的指出目的地的端口號。

public static void main(String[] args) throws IOException, IOException {
		// TODO Auto-generated method stub
		//1、創建客戶端socket服務
		Socket socket =new Socket("172.19.207.247",10002);//設置客戶端的端口號
		
		//2、獲取socket流中的輸出流
		OutputStream out=socket.getOutputStream();
		//3、使用輸出流將指定的數據寫出來
		out.write("tcp演示:哥們來了".getBytes());//注意輸出流是按字節來寫
		//4、關閉資源
		socket.close();
	}

服務器端(代碼與思路)

注意服務器端是通過accept()方法來獲取客戶端的對象來進行進一步的操作。

//服務端接收客戶端發送過來的數據,並打印在控制檯上
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		//1、創建服務端對象
		ServerSocket ss=new ServerSocket(10002);//這裏是待連接的客戶端的端口號
		//2、獲取連接過來的客戶端對象
		Socket s=ss.accept();//阻塞式的
		//獲取ip地址字符串
		String ip=s.getInetAddress().getHostAddress();
		//3、通過socket對象的獲取輸入流。要讀取客戶端發來的數據
		InputStream in=s.getInputStream();
		
		byte[] buf=new byte[1024];
		int len=in.read(buf);//返回字節數
		String text=new String(buf,0,len);
		System.out.println(ip+":"+text);
		
		//4、關閉資源和流
		s.close();
		ss.close();
	}

以上就是一個最基本的TCP傳輸的例子。接下來我們實現用按鍵輸入來進行數據的傳輸。

客戶端:

public static void main(String[] args) throws IOException, IOException {
		// TODO Auto-generated method stub
		/*
		 * 思路:
		 * 客戶端
		 * 1、需要先有socket端點
		 * 2、客戶端的數據源:鍵盤
		 * 3、客戶端的目的:socket
		 * 4、接受的數據,源:socket
		 * 5、將數據顯示在控制檯上
		 * 6、在這些流中操作的數據,都是文本數據。
		 * 
		 * 轉換客戶端:
		 * 1、創建socket客戶端對象
		 * 2、獲取鍵盤錄入
		 * 3、將錄入的信息發送給socket輸出流
		 */
		
		//1、創建socket客戶端對象
		Socket s=new Socket("172.19.207.247",10005);
		
		//2、獲取鍵盤的錄入
		BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
		
		//3、socket輸出流
		PrintWriter out=new PrintWriter(s.getOutputStream(),true);//true表示可以自動的刷新。//如果啓用了自動刷新,則只有在調用 println、printf 或 format 的其中一//個方法時纔可能完成刷新操作

		
		//4.socket輸入流,讀取服務器端返回的大寫數據
		BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));
		
		String line=null;
		while((line=bufr.readLine())!=null){
			//如果輸入的是over就結束
			if("over".equals(line))
				break;
			
			//輸出字符流
			out.println(line);
			
			//讀取服務器端輸出的大寫的字符
			String text=bufIn.readLine();
			System.out.println(text);
			
		}
		s.close();
	}

服務器端:

public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		/*
		 * 轉換服務器端
		 * 分析
		 * 1、創建serversocket服務
		 * 2、獲取socket對象
		 * 3、源:socket,讀取客戶端發過來的需要轉換的數據
		 * 4、目的:顯示在控制檯上
		 * 5、將數據轉換成大寫發送給客戶端
		 * */
		
		//1、創建serversocket對象
		ServerSocket ss=new ServerSocket(10005);
		
		//2、獲取socket對象
		Socket s=ss.accept();//阻塞式的
		
		//3獲取IP
		String ip=s.getInetAddress().getHostAddress();
		System.out.println(ip+"......connected");
		
		//4、獲取socket的讀取流,並裝飾
		BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));
		
		//5、獲取socket的輸出,並修飾
		PrintWriter out=new PrintWriter(s.getOutputStream(),true);
		String line=null;
		while((line=bufIn.readLine())!=null){
			//將讀取到的字符數據打印出來
			System.out.println(line);
			
			//將修飾好的字符輸出到客戶端
			out.println(line.toUpperCase());
		}
		
		s.close();
		ss.close();
				
	}

下圖是圖解TCP/IP傳輸過程:


但是,在本次傳輸中會出現一些問題:什麼時候服務器端知道客戶端的數據已經全部發送完畢了??什麼時候客戶端覺得自己的數據已經全部發送完,上面的代碼裏客戶端是不知道的?

在上面的代碼裏,我並沒有添加所謂的結束標誌符。而read方法或者readLine方法是阻塞式,所以客戶端始終無法自己判斷自己有沒有將數據全部傳輸過去。正確的解決方法是:在while循環外界添加s.shutdownOutputStream()。這樣就提示客戶端我的輸出流沒有輸出數據了,同時也會自己發給服務器端一個結束標誌。同理服務器端也就知道了。

本文由XXiaoLEI  整理






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