Java 網路編程之UDP通訊和簡單的群聊程式
UDP通訊需要明確的幾點:
- UDP通訊不是連線導向的,發送端不管接收端是否啟動是否能接收,發完資料報就結束。
- 無論是發送端還是接收端,都需要描述兩個對象:通訊端和資料報。
- 接收端的通訊端對象中必須明確接收埠,且必須和發送端指定的目標連接埠一致。而發送端的通訊端中則一般採用隨機分配的傳送埠。
- 無論是發送端還是接收端,資料報中都記錄了自己和對方的socket資訊(ip+port),還提供了用於發送或接收的資料緩衝區。這些資料只有資料報對象自己最清楚,如
getPort()
,getAddress()
,getData()
等。
- (1).只不過對於發送端來說,建立發送報文對象需要指定目標通訊端資訊(ip+port),還需明確資料發送緩衝區。
- (2).而對於接收端來說,則只需明確一個資料接收緩衝區即可。
- 接收端應該不斷迴圈地負責接收。
- UDP通訊端類DatagramSocket,UDP資料報類DatagramPacket。
UDPSender端:
import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.net.SocketException;import java.net.UnknownHostException;public class UDPSender { public static void main(String[] args) { DatagramSocket dgs = null; try { // 1. 建立udp發送端的socket,一般使用隨機傳送埠 dgs = new DatagramSocket(); // 2. 建立udp報文包對象 // 2.1 建立資料發送緩衝區 String text = "Hello World! I'm coming"; byte[] buf = text.getBytes(); // 2.2 建立發送資料報文對象 InetSocketAddress isa = new InetSocketAddress("192.168.0.124",8888); DatagramPacket dgp = new DatagramPacket(buf,buf.length,isa); //DatagramPacket dgp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.0.124"), 8888); // 3.發送udp報文 dgs.send(dgp); } catch (SocketException e1) { e1.printStackTrace(); } catch (UnknownHostException e2) { e2.printStackTrace(); } catch (IOException e3) { e3.printStackTrace(); } finally { // 4. 關閉通訊端 dgs.close(); } }}
UDPRecver端:
import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.SocketException;public class UDPRecver { public static void main(String[] args) { while(true){ // 1.建立udp接收通訊端,接收端必須指定正確的連接埠 DatagramSocket dgs = null; try { dgs = new DatagramSocket(8888); // 2. 建立udp接收資料包對象 byte[] buf = new byte[1024]; DatagramPacket dgp = new DatagramPacket(buf, buf.length); // 3.從通訊端中接收資料到資料包中 dgs.receive(dgp); // 4.展示udp發送端相關資訊,包括髮送的資料 String data = new String(dgp.getData(), 0, dgp.getLength()); String ip = dgp.getAddress().getHostAddress(); int port = dgp.getPort(); System.out.println("Data: "+data+" ip: "+ip+" port: "+port); } catch (SocketException s) { s.printStackTrace(); } catch (IOException i) { i.printStackTrace(); } finally { dgs.close(); } } }}
UDP實現群聊:
思路:
- 一個線程負責發,一個線程負責收,因此使用多線程。
- 發送端的資料報目標端應該指定為廣播目標。且發送的資料來源於鍵盤輸入。
- 接收端要無限迴圈接收資料,但應該提供下線離開功能。(假設收到了"bye",就表示下線)
import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetSocketAddress;import java.net.SocketException;public class QunChat { public static void main(String[] args) throws SocketException { //發送端和接收端通訊端,接收端通訊端連接埠為8888,需要傳遞給發送端的報文對象 DatagramSocket send_socket = new DatagramSocket(); DatagramSocket recv_socket = new DatagramSocket(8888); Sender sender = new Sender(send_socket,8888); Recver recver = new Recver(recv_socket); Thread send_thread1 = new Thread(sender); Thread recv_thread1 = new Thread(recver); send_thread1.start(); recv_thread1.start(); }}class Sender implements Runnable { private DatagramSocket send_sock; private int dest_port; Sender(DatagramSocket s,int port){ //初始化時就指定目標連接埠 this.send_sock = s; this.dest_port = port; } public void run() { while(true) { try { //群聊發送目標,以廣播為例 InetSocketAddress isa = new InetSocketAddress("192.168.0.255", dest_port); //從鍵盤接收資料 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); String line; while((line=bufr.readLine())!=null) { //如果發送的是bye,則斷開,且不發送給接收端 if(line.equals("bye")) break; byte[] buf = line.getBytes(); DatagramPacket dp = new DatagramPacket(buf,buf.length, isa); send_sock.send(dp); } } catch (IOException e) { e.printStackTrace(); } finally { send_sock.close(); } } }}class Recver implements Runnable { private DatagramSocket recv_sock; Recver(DatagramSocket socket){ this.recv_sock = socket; } public void run() { while(true) { try { //接收報文對象 byte[] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf, buf.length); recv_sock.receive(dp); String src_ip = dp.getAddress().getHostAddress(); String data = new String(dp.getData(),0,dp.getLength()); if(data.equals("bye")) System.out.println(src_ip +" leaving");; System.out.println("Recviving data: "+ data+" from "+src_ip ); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }}