1. 程式人生 > >基於UDP協議的網路程式設計

基於UDP協議的網路程式設計

        UDP協議即使用者資料報協議是一種面向無連線的協議,由於不需要建立連線,它的通訊效率高,實時性好,同時可靠性相對於TCP協議較低。UDP協議的主要作用是完成網路資料流和資料報之間的轉換--在資訊的傳送端,UDP協議將網路資料流封裝成資料報,然後將資料報傳送出去;在資訊的接收端,UDP協議將資料報轉換成實際資料內容。UDP在客戶端和服務端之間沒有虛擬鏈路,兩端各建立一個Socket只是用於傳送、接收資料報的物件。Java提供了DatagramSocket物件作為基於UDP協議的Socket,使用DatagramPacket代表DatagramSocket傳送、接收資料的資料報。

1.DatagramSocket類

         DatagramSocket不維護狀態,不能產生IO流,它的唯一作用就是接收和傳送資料報。通過構造器建立UDP的Socket。以下是DatagramSocket的構造器。

DatagramSocket():建立一個DatagramSocket例項,並將該物件繫結到本機預設IP地址、本機所有可用埠中隨機選擇某個埠。

DatagramSocket(int port):建立一個DatagramSocket例項,並將該物件繫結在本機預設IP地址、指定埠。

DatagramSocket(int port,InetAddress laddr):建立一個DatagramSocket例項,並將該物件繫結到指定IP地址、指定埠。

        DatagramSocket例項有兩個方法:

receive(DatagramPacket p):從該DatagramSocket中接收資料報

send(DatagramPacket p):以該DatagramSocket向外傳送資料報 

注意:由於DatagramSocket本身沒有狀態,所以不知道資料報發往何處,而是由DatagramPacket自身決定資料報發往何處。

2.DatagramPacket類 

         以下是DatagramPacket的構造器:

DatagramPacket(byte[] buf,int length):以一個空陣列來建立DatagramPacket物件,該物件的作用是接收DatagramPacket中的資料。

DatagramPacket(byte[] buf,int length,InetAddress addr,int port):以一個包含資料的陣列來建立DatagramPacket物件,建立該DatagramPacket物件時還指定了IP地址和埠,決定了該資料報的目的地。

DatagramPacket(byte[] buf,int offset,int length):以一個空陣列來建立DatagramPacket物件,並指定接收到的資料放入buf陣列中時從offset開始,最多放length個位元組。

DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port):建立一個用於傳送的DatagramPacket物件,指定傳送buf陣列中從offset開始,總共length個位元組。

注意:在接收資料之前,應該採用上面的第一個或第三個構造器生成一個DatagramPacket物件,給出接收資料的位元組陣列及其長度。然後呼叫DatagramSocket的receive()方法等待資料報的到來,receive()將一直等待,直到收到一個數據報為止。在傳送資料之前,呼叫第二個或第四個構造器建立DatagramPacket物件,此時的位元組數組裡存放了想傳送的資料。除此之外,還要給出完整的目的地址。傳送資料通過DatagramS的send()方法實現的。

 

3.示例

        這個例子主要是通過客戶端控制檯輸入資訊傳送請求查詢結果,而服務端接收到資訊之後,響應結果併發送給客戶端。這個過程中,DatagramPacket是傳遞資訊的介質,並且資料報本身確定了目標地址。

服務端:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;



public class UdpServer {
	public static final int PORT = 30000;
	//定義每個資料報大小為4KB
	private static final int DATA_LEN = 4096;
	//定義接收網路陣列的位元組陣列
	byte[] inBuff = new byte[DATA_LEN];
	//以指定位元組陣列建立準備接收資料的DatagramPacket物件
	private DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length);
	//定義一個用於傳送的DatagramPacket物件
	private DatagramPacket outPacket;
	
	//定義一個map,接收到客戶端查詢字元之後,可在map中查詢是否有相應結果
	HashMap<String, String> map = new HashMap<>();
	{
		map.put("最喜歡的水果", "蘋果");
		map.put("最喜歡的蔬菜", "西藍花");
		map.put("最不喜歡的蔬菜", "土豆");
		map.put("最不喜歡的水果", "榴蓮");
		map.put("最喜歡的動物", "狗");
		map.put("最不喜歡的動物", "蛇");
		map.put("最喜歡的畫家", "齊白石");
		map.put("最喜歡的城市", "杭州");
	}
	
	public void init() throws IOException {
		try(DatagramSocket socket = new DatagramSocket(PORT)){
			//採用迴圈接收資料
			for(int i=0;i<1000;i++) {
				//讀取socket中的資料,讀到的資料放入inPacket封裝的數組裡
				socket.receive(inPacket);
				//判斷inPacket.getData()和inBuff是否是同一個陣列
				System.out.println(inBuff==inPacket.getData());
				
				//將接收到的資料轉換為字串用於後續查詢
				String string = new String(inBuff,0,inPacket.getLength());
				
				
				//從map中查詢結果
				String anString = null;
				if(map.containsKey(string))
					anString ="服務端:"+map.get(string);
				else
					anString="服務端:您要找的資訊不存在!";
				byte[] sendData = anString.getBytes();
				//建立傳送DatagramPacket物件
				outPacket = new DatagramPacket(sendData, sendData.length, inPacket.getSocketAddress());
				socket.send(outPacket);
			}
		}
	}

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		new UdpServer().init();

	}

}

客戶端:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;




public class UdpClient {
	//定義傳送資料報的目的地
	public static final int DEST_PORT = 30000;
	public static final String DEST_IP = "127.0.0.1";
	//定義每個資料報大小最大為4KB
	private static final int DATA_LEN = 4096;
	//定義接收網路資料的位元組陣列
	byte[] inBUff = new byte[DATA_LEN];
	//以指定的位元組陣列建立準備接收資料的DatagramP物件
	private DatagramPacket inPacket = new DatagramPacket(inBUff, inBUff.length);
	//定義一個用於傳送的DatagramPacket物件
	private DatagramPacket outPacket = null;
	public void init() throws IOException {
		try(
			//建立一個客戶端DatagramSocket,隨機埠
			DatagramSocket socket = new DatagramSocket()){
			//初始化傳送用的DatagramPacket,它包含一個長度為0的位元組陣列
			outPacket = new DatagramPacket(new byte[0], 0, InetAddress.getByName(DEST_IP), DEST_PORT);
			//建立鍵盤輸入流
			Scanner scan = new Scanner(System.in);
			//不斷地讀取鍵盤輸入
			while(scan.hasNextLine()) {
				//將鍵盤輸入的一行字串轉換成位元組陣列
				byte[] buff = scan.nextLine().getBytes();
				//設定傳送用的DatagramPacket中的位元組資料
				outPacket.setData(buff);
				socket.send(outPacket);
				
				//讀取socket中的資料,讀到的資料放在inPacket鎖封裝的位元組陣列中
				socket.receive(inPacket);
				System.out.println(new String(inBUff,0,inPacket.getLength()));
			}
		}
	}

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		new UdpClient().init();

	}

}

執行結果:

 

注意:在基於UDP程式設計中,需要注意的是:

1.Socket沒有狀態,不知道資料報發往什麼位置,僅提供receive、send兩個方法

2.傳送的資料報本身綁定了目標地址

3.服務端的DatagramSocket物件初始化時需要指定IP和埠,而客戶端可以是隨機埠