該示例的功能是實現將客戶端程序的系統時間發送給服務器端,服務器端接收到時間以後,向客戶端反饋字符串“OK”。實現該功能的客戶端代碼如下所示:
package udp;
import java.net.*;
import java.util.*;
/**
* 簡單的UDP客戶端,實現向服務器端發生系統時間功能
*/
public class SimpleUDPClient {
public static void main(String[] args) {
DatagramSocket ds = null; //連接對象
DatagramPacket sendDp; //發送數據包對象
DatagramPacket receiveDp; //接收數據包對象
String serverHost = "127.0.0.1"; //服務器IP
int serverPort = 10010; //服務器端口號
try{
//建立連接
ds = new DatagramSocket();
//初始化發送數據
Date d = new Date(); //當前時間
String content = d.toString(); //轉換為字符串
byte[] data = content.getBytes();
//初始化發送包對象
InetAddress address = InetAddress.getByName(serverHost);
sendDp = new DatagramPacket(data,data.length,address,serverPort);
//發送
ds.send(sendDp);
//初始化接收數據
byte[] b = new byte[1024];
receiveDp = new DatagramPacket(b,b.length);
//接收
ds.receive(receiveDp);
//讀取反饋內容,並輸出
byte[] response = receiveDp.getData();
int len = receiveDp.getLength();
String s = new String(response,0,len);
System.out.println("服務器端反饋為:" + s);
}catch(Exception e){
e.printStackTrace();
}finally{
try{
//關閉連接
ds.close();
}catch(Exception e){}
}
}
}
在該示例代碼中,首先建立UDP方式的網絡連接,然後獲得當前系統時間,這裡獲得的系統時間是客戶端程序運行的本地計算機的時間,然後將時間字符串以及服務器端的IP和端口,構造成發送數據包對象,調用連接對象ds的send方法發送出去。在數據發送出去以後,構造接收數據的數據包對象,調用連接對象ds的receive方法接收服務器端的反饋,並輸出在控制台。最後在finally語句塊中關閉客戶端網絡連接。
和下面將要介紹的服務器端一起運行時,客戶端程序的輸出結果為:
服務器端反饋為:OK
下面是該示例程序的服務器端代碼實現:
package udp;
import java.net.*;
/**
* 簡單UDP服務器端,實現功能是輸出客戶端發送數據,
並反饋字符串“OK"給客戶端
*/
public class SimpleUDPServer {
public static void main(String[] args) {
DatagramSocket ds = null; //連接對象
DatagramPacket sendDp; //發送數據包對象
DatagramPacket receiveDp; //接收數據包對象
final int PORT = 10010; //端口
try{
//建立連接,監聽端口
ds = new DatagramSocket(PORT);
System.out.println("服務器端已啟動:");
//初始化接收數據
byte[] b = new byte[1024];
receiveDp = new DatagramPacket(b,b.length);
//接收
ds.receive(receiveDp);
//讀取反饋內容,並輸出
InetAddress clientIP = receiveDp.getAddress();
int clientPort = receiveDp.getPort();
byte[] data = receiveDp.getData();
int len = receiveDp.getLength();
System.out.println("客戶端IP:" + clientIP.getHostAddress());
System.out.println("客戶端端口:" + clientPort);
System.out.println("客戶端發送內容:" + new String(data,0,len));
//發送反饋
String response = "OK";
byte[] bData = response.getBytes();
sendDp = new DatagramPacket(bData,bData.length,clientIP,clientPort);
//發送
ds.send(sendDp);
}catch(Exception e){
e.printStackTrace();
}finally{
try{
//關閉連接
ds.close();
}catch(Exception e){}
}
}
}
在該服務器端實現中,首先監聽10010號端口,和TCP方式的網絡編程類似,服務器端的receive方法是阻塞方法,如果客戶端不發送數據,則程序會在該方法處阻塞。當客戶端發送數據到達服務器端時,則接收客戶端發送過來的數據,然後將客戶端發送的數據內容讀取出來,並在服務器端程序中打印客戶端的相關信息,從客戶端發送過來的數據包中可以讀取出客戶端的IP以及客戶端端口號,將反饋數據字符串“OK”發送給客戶端,最後關閉服務器端連接,釋放占用的系統資源,完成程序功能示例。
和前面TCP方式中的網絡編程類似,這個示例也僅僅是網絡編程的功能示例,也存在前面介紹的客戶端無法進行多次數據交換,以及服務器端不支持多個客戶端的問題,這兩個問題也需要對於代碼進行處理才可以很方便的進行解決。
在解決該問題以前,需要特別指出的是UDP方式的網絡編程由於不建立虛擬的連接,所以在實際使用時和TCP方式存在很多的不同,最大的一個不同就是“無狀態”。該特點指每次服務器端都收到信息,但是這些信息和連接無關,換句話說,也就是服務器端只是從信息是無法識別出是誰發送的,這樣就要求發送信息時的內容需要多一些,這個在後續的示例中可以看到。
下面是實現客戶端多次發送以及服務器端支持多個數據包同時處理的程序結構,實現的原理和TCP方式類似,在客戶端將數據的發送和接收放入循環中,而服務器端則將接收到的每個數據包啟動一個專門的線程進行處理。實現的代碼如下:
package udp;
import java.net.*;
import java.util.*;
/**
* 簡單的UDP客戶端,實現向服務器端發生系統時間功能
* 該程序發送3次數據到服務器端
*/
public class MulUDPClient {
public static void main(String[] args) {
DatagramSocket ds = null; //連接對象
DatagramPacket sendDp; //發送數據包對象
DatagramPacket receiveDp; //接收數據包對象
String serverHost = "127.0.0.1"; //服務器IP
int serverPort = 10012; //服務器端口號
try{
//建立連接
ds = new DatagramSocket();
//初始化
InetAddress address = InetAddress.getByName(serverHost);
byte[] b = new byte[1024];
receiveDp = new DatagramPacket(b,b.length);
System.out.println("客戶端准備完成");
//循環30次,每次間隔0.01秒
for(int i = 0;i < 30;i++){
//初始化發送數據
Date d = new Date(); //當前時間
String content = d.toString(); //轉換為字符串
byte[] data = content.getBytes();
//初始化發送包對象
sendDp = new DatagramPacket(data,data.length,address, serverPort);
//發送
ds.send(sendDp);
//延遲
Thread.sleep(10);
//接收
ds.receive(receiveDp);
//讀取反饋內容,並輸出
byte[] response = receiveDp.getData();
int len = receiveDp.getLength();
String s = new String(response,0,len);
System.out.println("服務器端反饋為:" + s);
}
}catch(Exception e){
e.printStackTrace();
}finally{
try{
//關閉連接
ds.close();
}catch(Exception e){}
}
}
}
在該示例中,將和服務器端進行數據交換的邏輯寫在一個for循環的內部,這樣就可以實現和服務器端的多次交換了,考慮到服務器端的響應速度,在每次發送之間加入0.01秒的時間間隔。最後當數據交換完成以後關閉連接,結束程序。
實現該邏輯的服務器端程序代碼如下:
package udp;
import java.net.*;
/**
* 可以並發處理數據包的服務器端
* 功能為:顯示客戶端發送的內容,並向客戶端反饋字符串“OK”
*/
public class MulUDPServer {
public static void main(String[] args) {
DatagramSocket ds = null; //連接對象
DatagramPacket receiveDp; //接收數據包對象
final int PORT = 10012; //端口
byte[] b = new byte[1024];
receiveDp = new DatagramPacket(b,b.length);
try{
//建立連接,監聽端口
ds = new DatagramSocket(PORT);
System.out.println("服務器端已啟動:");
while(true){
//接收
ds.receive(receiveDp);
//啟動線程處理數據包
new LogicThread(ds,receiveDp);
}
}catch(Exception e){
e.printStackTrace();
}finally{
try{
//關閉連接
ds.close();
}catch(Exception e){}
}
}
}
該代碼實現了服務器端的接收邏輯,使用一個循環來接收客戶端發送過來的數據包,當接收到數據包以後啟動一個LogicThread線程處理該數據包。這樣服務器端就可以實現同時處理多個數據包了。
實現邏輯處理的線程代碼如下:
package udp;
import java.net.*;
/**
* 邏輯處理線程
*/
public class LogicThread extends Thread {
/**連接對象*/
DatagramSocket ds;
/**接收到的數據包*/
DatagramPacket dp;
public LogicThread(DatagramSocket ds,DatagramPacket dp){
this.ds = ds;
this.dp = dp;
start(); //啟動線程
}
public void run(){
try{
//獲得緩沖數組
byte[] data = dp.getData();
//獲得有效數據長度
int len = dp.getLength();
//客戶端IP
InetAddress clientAddress = dp.getAddress();
//客戶端端口
int clientPort = dp.getPort();
//輸出
System.out.println("客戶端IP:" + clientAddress.getHostAddress());
System.out.println("客戶端端口號:" + clientPort);
System.out.println("客戶端發送內容:" + new String(data,0,len));
//反饋到客戶端
byte[] b = "OK".getBytes();
DatagramPacket sendDp = new DatagramPacket(b,b.length,clientAddress,clientPort);
//發送
ds.send(sendDp);
}catch(Exception e){
e.printStackTrace();
}
}
}
在該線程中,只處理一次UDP通訊,當通訊結束以後線程死亡,在線程內部,每次獲得客戶端發送過來的信息,將獲得的信息輸出到服務器端程序的控制台,然後向客戶端反饋字符串“OK”。
由於UDP數據傳輸過程中可能存在丟失,所以在運行該程序時可能會出現程序阻塞的情況。如果需要避免該問題,可以將客戶端的網絡發送部分也修改成線程實現。
關於基礎的UDP網絡編程就介紹這麼多了,下面將介紹一下網絡協議的概念。