一、UDP網絡編程概述
采用TCP協議通信時,客戶端的Socket必須先與服務器建立連接,連接建立成功后,服務器端也會持有客戶端連接的Socket,客戶端的Socket與服務器端的Socket是對應的,它們構成了兩個端點之間的虛擬通信鏈路。與TCP通信不同,UDP是面向無連接的、不可靠的基于數據包的傳輸協議。即應用進程(或程序)在使用UDP協議之前,不必先建立連接。自然,發送數據結束時也沒有連接需要釋放。因此,減少了開銷和發送數據之前的延時。UDP也采用端口來區分進程。
在java中,java.net.DatagramSocket負責接收和發送UDP數據報文,java.net.DatagramPacket表示UDP數據報。每個DatagramSocket與一個數據報套接字(包括本地主機的IP地址和本地UDP端口)綁定,每個DatagramSocket可以把UDP數據報發送給任意一個遠程DatagramSocket,也可以接收來自任意一個遠程DatagramSocket的數據報。在UDP數據報中包含了目的地址信息,DatagramSocket可以根據該信息把數據報發送的目的地。
UDP協議是無連接的協議。客戶端的DatagramSocket與服務端DatagramSocket不存在一一對應關系,兩者無需建立連接,就能交換數據報。每個DatagramSocket對象都會與一個本地端口綁定,在此端口監聽發送過來的數據報。在服務器程序中,一般由程序顯示地為DatagramSocket指定本地端口。在客戶程序中,一般由操作系統為DatagramSocket分配本地端口,這種端口也稱為匿名端口。
二、關于DatagramSocket類和DatagramPacket類
DatagramSocket類的構造方法如下:
DatagramSocket()throws SocketException
作用:構造數據報套接字并將其綁定到本地主機上任何可用的端口。套接字將被綁定到INADDR_ANY地址,IP地址由內核來選擇。
DatagramSocket(int port)throws SocketException
作用:創建數據報套接字并將其綁定到本地主機上的指定端口。套接字將被綁定到INADDR_ANY地址,IP地址由內核來選擇。
DatagramSocket類的常用方法如表所示:
void send(DatagramPacket p) throws IOException | 發送一個UDP數據包。一個UDP數據包就是一個DatagramPacket對象 |
void receive(DatagramPacket p) throws IOException | 接收一個UDP數據包。一個UDP數據包就是一個DatagramPacket對象 |
void connect(InetAddress address,int port) | 將該UDPSocket變成連接型的UDPSocket |
void disconnect() | 將該UDPSocket變成一個非連接型的UDPSocket |
void close() | 關閉UDPSocket連接 |
?
?
?
?
?
?
?
其中,UDPSocket分為“連接型”與“非連接型”兩種。默認UDPSocket是“非連接型”的,這個連接不是指向TCP那樣進行三步握手,而只是將對方信息與自己關聯在一起。
DatagramPacket類的對象代表了一個UDP數據報包。通過UDP發送數據時,先要根據發送的數據生成一個DatagramPacket對象,然后通過DatagramSocket對象的send()方法發送這個對象。接收時,先要根據要接收數據的緩沖區生成一個Datagrampacket對象,然后通過DatagramPacket對象的receive()方法接收這個對象的數據內容。
DatagramPacket類的構造方法分為兩類:
一類是創建DatagramPacket對象用來接收數據報包;
另一類是創建DatagramPacket對象用來發送數據報包。
它們的區別是,用于發送數據報包的構造方法需要設置數據報包達到的目的地址,若是“連接型”UDP,則不需要設定目的地址,而用于接收數據報包的構造方法無須設定地址。
用于接收數據報包的構造方法如下:
DatagramPacket(byte[] buf,int length)
作用:由接收緩沖區生成一個DatagramPacket對象。buf表示保存傳入數據報的緩沖區,length表示要讀取的字節數。
DatagramPacket(byte[] buf,int offset,int length)
作用:構造DatagramPacket對象。用來接收長度為length的數據包,并在緩沖區中指定了偏移量。
用于發送數據報包的構造方法如下:
DatagramPacket(byte[] buf,int length,InetAddress address,int port)
作用:構造數據報包發送的對象,用來將長度為length的包發送到指定主機上的指定端口號。length參數要小于等于buf的長度。
DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port)
作用:構造數據報包發送的對象,用來將長度為length且偏移量為offset的包發送到指定主機上的指定端口號。length參數要小于等于buf的長度。
DatagramPacket類的常用方法如下表:
byte[] getData() | 返回DatagramPacket對象中包含的數據 |
int getLength() | 返回發送/接收數據的長度 |
int getOffset() | 返回發送/接收數據在byte[]中的偏移 |
InetAddress getAddress() | 返回對方的IP地址。用InetAddress對象表示 |
int getPort() | 返回對方的端口號 |
void setData(byte[] buf,int offset,int length) | 設置該對象中包含的數據 |
void setAddress(inetaddress iaddr) | 設置該對象中包含的IP地址 |
void setPort() | 設置該對象中包含的端口號 |
?
?
?
?
?
?
?
通過UDP發送/接收數據步驟:
發送數據,先要根據發送的數據生成一個DatagramPacket對象,并指定發送長度和接收數據的IP地址和端口號,然后通過DatagramSocket對象的send()方法發送這個對象。
接收數據,根據要接受收數據的緩沖區及大小生成一個DatagramPacket對象,然后通過DatagramSocket對象的receive()方法接收這個對象的數據內容。
三、UDP網絡編程練習
練習代碼:
package com.ItHeima.WeekAct;/**服務器**/import java.net.*;public class ChatterClient extends Thread {private DatagramSocket socket;private InetAddress address;private byte[] buf = new byte[1000];private DatagramPacket packet = new DatagramPacket(buf, buf.length);//創建要發送的數據包private int id;//客戶端idpublic ChatterClient(int id) {this.id = id;try {socket = new DatagramSocket();//創建UDP套接字address = InetAddress.getByName(null);//取得本地地址} catch (SocketException e) {System.out.println("can not open socket");e.printStackTrace();System.exit(1);} catch (UnknownHostException e) {System.out.println("Can not find host");System.exit(1);}System.out.println("ChatterClient starting");start();//之后調用run() }public void run(){try {for(int i = 0 ; i < 25 ; i++){String outMsg = "服務器你好,這是我客戶端發過來的數據,請接收!" + id + ",消息" + i;//要發送到服務器的數據socket.send(new DatagramPacket(outMsg.getBytes(),outMsg.getBytes().length, address, ChatterServer.INPORT));//打包數據并將其發送到指定地址+端口的服務端socket.receive(packet);//接收服務端返回的數據包String msg=new String(packet.getData(),packet.getOffset(),packet.getLength());//獲取服務器返回的信息String rcvd = "客戶端--" + id +", 收到來自服務器的信息" + packet.getAddress() + "," + packet.getPort() + ":" + msg;//組合返回信息System.out.println(rcvd);//輸出到控制臺 }} catch (Exception e) {e.printStackTrace();System.exit(1);//出錯退出 }}public static void main(String[] args) {for(int i = 0 ; i < 10 ; i ++ ){new ChatterClient(i);}} }
package com.ItHeima.WeekAct;/**服務器**/import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException;public class ChatterServer {public static final int INPORT = 1711;//服務器端口private byte[] buf = new byte[1000];private DatagramPacket packet = new DatagramPacket(buf, buf.length);//創建數據包private DatagramSocket socket;//UDP套接字public ChatterServer(){try{socket = new DatagramSocket(INPORT);//啟動套接字System.out.println("Server started");while(true){socket.receive(packet);//接收數據包并將當前線程掛起String msg=new String(packet.getData(),packet.getOffset(),packet.getLength());//獲取客戶端發送的信息String rcvd ="服務器--收到來自客戶端的信息:"+ msg+ ", from adddress:" + packet.getAddress() + ",port:" + packet.getPort();//解析數據包System.err.println(rcvd);//打印數據信息String returnMasg = "服務器返回信息:你好客戶端,這是你發過來的數據:" + msg+",我將它原樣返回";DatagramPacket echo = new DatagramPacket(returnMasg.getBytes(), returnMasg.getBytes().length,packet.getAddress(), packet.getPort());//將接收到包重新包裝稱UDP數據包準備原封不動的返回給客戶端socket.send(echo);//反饋數據包 }}catch (SocketException e) {System.out.println("Can`t open socket");System.exit(1);}catch (IOException e) {System.out.println("Communication error");e.printStackTrace();}}public static void main(String[] args) {new ChatterServer();//運行服務器 } }
運行結果:
?