1. 程式人生 > >UDP網路程式設計簡單實現

UDP網路程式設計簡單實現

由於最近編寫的遊戲涉及到了網路程式設計這塊,所以特意記錄下。

UDP簡介

UDP協議的全稱是使用者資料報,在網路中它與TCP協議一樣用於處理資料報。在OSI模型中,UDP位於第四層——傳輸層,處於IP協議額上一層。UDP有不提供資料報分組、組裝以及不能對資料報排序的缺點。當報文傳送之後,是無法得知其是否安全完整到達的。

由於UDP不屬於連線性協議的特性,因此具有資源消耗小、處理速度快的優點,所以通過音訊、視訊和普通資料在傳送時使用UDP較多,因為它們即使偶爾丟失一兩個資料包,也不會對接收結果產生太大影響,如人們聊天使用的ICQ和OICQ使用的就是UDP協議。

使用java.net包下的DatagramSocket和DatagramPacket類,可以非常方便地控制使用者資料報文。下面就對這兩個類進行介紹

DatagramPacket類

DatagramPacket類用於處理報文,他將byte陣列、目標地址和目標埠等資料包裝成報文或者將報文拆卸成byte陣列。

DatagramPacket有多個構造方法,通常情況下它們都有兩個共同的引數:byte[] buffer和int length。其中buffer引數包含了一個對儲存自定址資料報資訊的位元組陣列的引用,length表示位元組陣列的長度。

最簡單的構造方法是:

DatagramPacket(byte[] buffer,int length);

這個構造方法確定了資料報陣列和陣列的長度,但沒有任何資料報的地址和埠資訊,這些資訊可以通過呼叫方法setAddress(InetAddress addr)和setPort(int port)新增。下面程式碼示範這些方法的使用:

byte[] buffer = new byte[100];
DatagramPacket dgp = new DatagramPacket(buffer,buffer.length);
InetAddress address = InetAddress.getByName("www.disney.com");
dgp.setAddress(address);
dgp.setPort(6666);//設定資料報傳送埠

如果使用者更喜歡在呼叫構造方法時同時包括地址和埠號,則可以使用:

DatagramPacket(byte[] buffer,int length,InetAddress address,int port);

如果在建立DatagramPacket物件後想要改變位元組陣列和它的長度,可以通過setData()和setLength()方法進行修改:

setData(byte[] buffer);
setLength(int length);

DatagramPacket類的常用方法如下:

getAddress()、setAddress(InetAddress address):得到、設定資料博地址

getData()、setData(byte[] buffer):得到、設定資料報內容

getLength()、setLength(int length):得到、設定資料報長度

getPort()、setPort(int port)得到、設定埠號

 

DatagramSocket類

DatagramSocket類在客戶端建立資料報套接字與服務端進行通訊連線,併發送和接受資料報套接字。

客戶端套接字最常用的構造方法是DatagramSocket()函式,而伺服器端則是DatagramSocket(int port)函式。如果未能建立套接字或者繫結套接字到本地埠,那麼這兩個函式都將丟擲一個SocketException異常,一旦程式建立了DatagramSocket物件,那麼程式分別呼叫send(DatagramPacket p)和receive(DatagramPacket p)來發送和接受資料報。

DatagramSocket構造方法如下:

DatagramSocket():建立資料報套接字,繫結到本地任意存在的埠;

DatagramSocket(int port):建立資料報套接字,繫結到本地主機指定埠;

DatagramSocket(int port,InetAddress address):建立資料報套接字,繫結到指定本地地址。

常用方法如下:

connect(InetAddress address,int port):連線指定地址

disconnect():斷開套接字連線

close():關閉資料報套接字

getInetAddress():得到套接字所連線的主機地址

getLocalAddress():得到套接字繫結的主機地址

getLocalPort():得到套接字的埠號

receive(DatagramPacket p):接收資料報

send(DatagramPacket p):傳送資料報。

實踐

實現一個簡單的雙向聊天的功能:

建立兩個類:Client類和Server類

初始狀況下,雙方都處於空閒狀態,客戶端視窗和服務端視窗輸入文字按下回車後,對方的視窗能及時顯示對方傳送過來的訊息。

實現思路:

兩個類的程式碼可以大量複用,建立兩個資料報套接字sendSocket和receiveSocket,其中sendSocket用來發送訊息,而receiveSocket用來接收訊息;實現Runnable介面,建立執行緒持續監聽receiveSocket監聽的埠是否有傳送來訊息,如果有則控制檯進行訊息的列印。建立一個Scanner物件,持續讀取控制檯輸入,如果控制檯輸入完畢並回車,則呼叫send方法將輸入的訊息封裝到資料報然後傳送到對方監聽的埠。這裡服務端監聽3002埠,客戶端監聽3001,程式碼實現如下:

Server類:

package 網路程式設計;

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

public class Server implements Runnable{

	DatagramSocket sendSocket;
	DatagramSocket receiveSocket;
	
	public Server(){		
	
		try {
			receiveSocket = new DatagramSocket(3002);
			sendSocket = new DatagramSocket();
		} catch (Exception e) {
			e.printStackTrace();
		}
		new Thread(this).start();
	}
	
	
	
	public static void main(String[] args) {
		System.out.println("服務端|空閒中...");
		Server server = new Server();
		Scanner input = new Scanner(System.in);
		while(true){
			String s =  input.next();
			server.send(s);
		}
		
	}

	public void send(String str){

		try {
			byte[] buffer;
			buffer = str.getBytes();
			InetAddress ia;
			ia = InetAddress.getByName("127.0.0.1");
			DatagramPacket dgp = new DatagramPacket(buffer,buffer.length,ia,3001);
			sendSocket.send(dgp);

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	
	@Override
	public void run() {
	
		byte[] data = new byte[100];
		DatagramPacket dgp = new DatagramPacket(data,data.length);
		while(true){
		try {
			receiveSocket.receive(dgp);
			System.out.println("客戶端|"+new String(data));
			for(int i=0;i<100;i++)
				data[i] = 0;
		} catch (IOException e) {
			e.printStackTrace();
		}
		}
	}


	

}

Client類:

package 網路程式設計;

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

public class Client implements Runnable{

	DatagramSocket sendSocket;
	DatagramSocket receiveSocket;
	
	public Client(){

		try {
			receiveSocket = new DatagramSocket(3001);
			sendSocket = new DatagramSocket();
		} catch (Exception e) {
			e.printStackTrace();
		}
		Thread t = new Thread(this);
		t.start();
	}
	
	public static void main(String[] args) {
		System.out.println("客戶端|空閒中...");
		Client client =	new Client();
		Scanner input = new Scanner(System.in);
		while(true){
			String s =  input.next();
			client.send(s);
		}
	}
	
	public void send(String str){

		try {
			byte[] buffer;
			buffer = str.getBytes();
			InetAddress ia;
			ia = InetAddress.getByName("127.0.0.1");
			DatagramPacket dgp = new DatagramPacket(buffer,buffer.length,ia,3002);
			sendSocket.send(dgp);

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	@Override
	public void run() {
		byte[] data = new byte[100];
		DatagramPacket dgp = new DatagramPacket(data,data.length);
		while(true){
		try {
			receiveSocket.receive(dgp);
			System.out.println("服務端|"+new String(data));
			for(int i=0;i<100;i++)
				data[i] = 0;
		} catch (IOException e) {
			e.printStackTrace();
		}
		}
	}

}

執行效果如圖: