Posted on 2009-12-17 22:59
啥都寫點 閱讀(859)
評論(0) 編輯 收藏 所屬分類:
J2SE
上面的例子都用Socket建立連接,屬于TCP(Transmission Control Protocol)連接,本節實例實現UDP(User Data Protocol)編程,包括發送和接收UDP報文。 首先來看看UDP與TCP的區別:

TCP是基于連接的協議,也就是說,在正式收發數據前,必須和對方建立可靠的連接,這與打電話的機制相似。在前面幾節的編程中,客戶端都與服務器請求建立Socket連接,當服務器端的ServerSocket的accept方法接受連接時,客戶端與服務器的連接便確立了,由于要事先建立好連接,所以用TCP傳輸數據速度相對比較慢,但是比較穩定。

UDP是面向非連接的協議,也就是說,在正式通信前不必與對方先建立連接,不管對方狀態就直接發送,這與用手機發短信的機制非常相似。由于不用建立連接,所以傳輸速度比較快,但是傳輸的可靠性較差。
在Java中實現UDP編程的關鍵技術如下:

java.net.DatagramSocket和java.net.DatagramPacket類可以實現UDP編程,前者實現與目標機的連接(這種連接不需要目標主機的認可),后者用于封裝UDP包。

發送UDP包時,先將數據包裝成DatagramPacket對象,然后建立一個DatagramSocket,調用它的send方法,將DatagramPacket發送給目標主機

收取UDP包時,建立一個偵聽本地端口的DatagramSocket,創建一個空的DatagramPacket對象,以存放收到的報文,調用DatagramSocket的receive方法將收到的UDP包寫入到DatagramPacket對象中。

DatagramPacket 的getAddress方法能獲得UDP消息發送者的網絡地址信息。

/** *//**--------------------------------------------UDPReceive.java---------------------------------------------------*/

import java.net.DatagramPacket;
import java.net.DatagramSocket;


/** *//**
* 該程序接收來自指定端口得UDP報文。
**/

public class UDPReceive
{
// 幫助信息
public static final String usage = "Usage: java book.net.udp.UDPReceive <port>";


public static void main(String args[])
{

try
{

if (args.length != 1)
{
throw new IllegalArgumentException("Wrong number of args");
}
// 從命令行中獲取端口號參數
int port = Integer.parseInt(args[0]);

// 創建一個socket,偵聽這個端口。
DatagramSocket dsocket = new DatagramSocket(port);

// 保存接收到的UDP報文的字節數組
byte[] buffer = new byte[2048];

// 創建一個DatagramPacket,將收到的報文寫入buffer中。
// 注意,這里指定了報文的長度,如果收到的UDP報文比2048大,多余的信息被舍棄
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

// 不斷循環,接收數據

for ( ; ;)
{
// 等待收到一個數據包
dsocket.receive(packet);

// 將收到的報文的字節數組封裝成字符串。
String msg = new String(buffer, 0, packet.getLength());
// 從數據包中取得消息來源的地址
System.out.println("Receive: " + packet.getAddress().getHostAddress() + ": "
+ msg);

// 如果收到QUIT指令,則退出循環。

if (msg.equals("QUIT"))
{
System.out.println("Exit the UDPReceive!");
break;
}
// 重設數據包的長度
packet.setLength(buffer.length);
}
// 關閉socket
dsocket.close();

} catch (Exception e)
{
System.err.println(e);
System.err.println(usage);
}
}
}


/** *//**--------------------------------------------UDPSend.java---------------------------------------------------*/


import java.io.File;
import java.io.FileInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;


/** *//**
* 該實例實現一個發送UDP報文的類。
* UDP與TCP不同在于,UDP在發送報文前無需建立連接,直接發送;而TCP需要建立連接。
* 即TCP比UDP更可靠。另外,UDP報文會出現舍棄的情況,因為發送端和接收端的報文大小可能不一致。
* 體現在編程時,UDP編程無需ServerSocket和Socket,只需要DatagramSocket類即可。
**/

public class UDPSend
{
public static final String usage =
"Usage: java book.net.udp.UDPSend <hostname> <port> <msg>
\n" +
" or: java book.net.udp.UDPSend <hostname> <port> -f <file>";


public static void main(String args[])
{

try
{
// 檢查參數個數

if (args.length < 3)
{
throw new IllegalArgumentException("Wrong number of args");
}
// 域名和端口
String host = args[0];
int port = Integer.parseInt(args[1]);
// 下面構造待發送報文的字節數組
byte[] message;

if (args[2].equals("-f"))
{
// 如果第三個參數為 -f,則表示將文件的內容以UDP方式發送
// 獲得待發送的文件對象以及文件的長度
File f = new File(args[3]);
int len = (int)f.length();
// 創建一個足夠容納文件內容的字節數組
message = new byte[len];
FileInputStream in = new FileInputStream(f);
// 將文件內容以字節的方式讀到字節數組中
int bytes_read = 0, n;

do
{
n = in.read(message, bytes_read, len-bytes_read);
bytes_read += n;
} while((bytes_read < len)&& (n != -1));
}

else
{
// 如果第三個參數不是 -f,則將后面的參數當作消息發送
String msg = args[2];

for (int i = 3; i < args.length; i++)
{
msg += " " + args[i];
}
message = msg.getBytes();
}
// 根據域名獲取IP地址
InetAddress address = InetAddress.getByName(host);
// 初始化一個UDP包。
// DatagramPacket的構造方法中必須使用InetAddress,而不能是IP地址或者域名
DatagramPacket packet = new DatagramPacket(message, message.length,
address, port);
// 創建一個DatagramSocket,以發送UDP包
DatagramSocket dsocket = new DatagramSocket();
dsocket.send(packet);
System.out.println("send: " + new String(message));
dsocket.close();
// 注意:如果在構造DatagramPacket時,不提供IP地址和端口號,
// 則需要調用DatagramSocket的connect方法,否則無法發送UDP包
packet = new DatagramPacket(message, message.length);
dsocket = new DatagramSocket();
dsocket.connect(address, port);
dsocket.send(packet);
System.out.println("Send: " + new String(message));
dsocket.close();

} catch (Exception e)
{
System.err.println(e);
System.err.println(usage);
}
}
}

首先在命令行中輸入“java book.net.udp.UDPReceive 8888”,以啟動UDPReceive進程,接收來自8888端口的UDP報文,然后另開一個命令行,輸入"java book.net.udp.UDPSend 127.0.0.1 8888 This is the UDP message!" ,將發送一條消息給本地的8888端口。接著再發送一條“QUIT”消息,停止UDPReceive 進程。
--
學海無涯