通過Socket實現UDP編程
UDP通信:
1、UDP協議(用戶數據報協議)是無連接、不可靠、無序的。
2、UDP協議以數據報作為數據傳輸的載體。
3、使用UDP進行數據傳輸時,首先需要將要傳輸的數據定義成數據報(Datagram),在數據報中指明所要達到的Socket(主機地址和端口號),然后在將數據報發生出去。
4、相關操作類:
- DatagramPacket:表示數據報包
- DatagramSocket:進行端到端通信的類
一、DatagramPacket&DatagramSocket類的常用方法
<DatagramPacket類>
構造方法:
1 DatagramPacket(byte[] buf,int length)//接受長度為length的數據包 3 DatagramPacket(byte[] buf,int length,InetAddress address,int port)//將指定長度的字節發生到指定主機的指定端口
<DatagramSocket類>
構造方法:
1 DatagramSocket(); 2 DatagramSocket(int port,InetAddress laddr);
常用方法:
1 close();//關閉DatagramSocket 2 getInetAddress();//獲取地址 3 getPort();//獲取端口號 4 send(DatagramPacket p);//從此套接字發送數據包 5 recrive(DatagramPacket p);//從此套接字接收數據包
二、編程實現基於UDP的用戶登錄小程序
通過寫一個用戶登錄的小程序,來直觀了解基於UDP的Socket通信的工作過程,首先我們得知道在客戶端和服務器通信時,兩者的運作流程是如何的,先從服務器入手。
服務器端實現步驟:
1、創建DatagramSocket,指定端口號
2、創建DatagramPacket
3、接收客戶端發送的數據信息
4、讀取數據
那么用戶登錄的測試案例的服務器端類可以這樣(待完善):
1 public static void main(String[] args) throws IOException { 2 // TODO Auto-generated method stub 3 4 try { 5 //1.創建服務器端DatagramSocket,指定端口 6 DatagramSocket ds=new DatagramSocket(8080); 7 //2.創建數據報DatagramPacket,用於接收客戶端發送的數據 8 byte[] data=new byte[1024];//創建字節數組,指定接受的數據報大小 9 DatagramPacket dp=new DatagramPacket(data, data.length); 10 //3.接收客戶端發送的數據 11 ds.receive(dp);//此方法在接收到數據報之前會一直阻塞 12 //4.讀取數據 13 //String info=Arrays.toString(data); 14 String info=new String(data, 0, dp.getLength()); 15 System.out.println("我是服務器,客戶端說:"+info); 16 } catch (SocketException e) { 17 // TODO Auto-generated catch block 18 e.printStackTrace(); 19 } 20 }
接着我們看一下客戶端的運作過程,其實和服務器端差不多,但是其中的區別還是要清楚的。
客戶端實現步驟:
1、定義發送信息
2、創建DatagramPacket:包含將要發送信息
3、創建DatagramSocket
4、發送數據
那么用戶登錄的測試案例的客戶端類可以這樣(待完善):
1 public static void main(String[] args) throws IOException { 2 // TODO Auto-generated method stub 3 //1.定義服務器的地址、端口號、數據 4 InetAddress add=InetAddress.getByName("localhost"); 5 int port=8080; 6 byte[] data="用戶名:admin;密碼:123".getBytes(); 7 //2.創建數據報,包含了發送的相關信息 8 DatagramPacket ap=new DatagramPacket(data, data.length, add, port); 9 //3.創建DatagramSocket對象 10 DatagramSocket ds=new DatagramSocket(); 11 //4.向服務器端發送數據報 12 ds.send(ap); 13 14 }
這樣服務端和客戶端就能進行最簡單的通信了,目前這個還不能實現交互,下面會講兩者的交互。
運行一下,看看服務器端是否能接收到客戶端發送的信息了呢...
分別啟動服務器和客戶端,注意必須先啟動服務器再啟動客戶端,否則客戶端找不到資源報錯:
完善用戶登錄之服務器響應客戶端
在上述的例子中,服務器和客戶端僅僅只是相互可以通信,但服務器對於接收到的客戶端信息並沒有向客戶端響應,客戶端沒有接收到服務器端的任何信息,這明顯是不完善不友好的,在這里將對剛剛的例子進行完善,完成服務器對客戶端的響應。
修改后的服務器端:
1 public static void main(String[] args) throws IOException { 2 // TODO Auto-generated method stub 3 4 try { 5 /* 6 * 接收客戶端發送的數據 7 */ 8 //1.創建服務器端DatagramSocket,指定端口 9 DatagramSocket ds=new DatagramSocket(8080); 10 //2.創建數據報DatagramPacket,用於接收客戶端發送的數據 11 byte[] data=new byte[1024];//創建字節數組,指定接受的數據報大小 12 DatagramPacket dp=new DatagramPacket(data, data.length); 13 //3.接收客戶端發送的數據 14 System.out.println("服務器端已啟動,等待客戶端發送數據..."); 15 ds.receive(dp);//此方法在接收到數據報之前會一直阻塞 16 //4.讀取數據 17 //String info=Arrays.toString(data); 18 String info=new String(data, 0, dp.getLength()); 19 System.out.println("我是服務器,客戶端說:"+info); 20 /* 21 * 響應客戶端 22 */ 23 //1.定義客戶端的地址、端口號、數據 24 InetAddress add=dp.getAddress(); 25 int port=dp.getPort(); 26 byte[] data2="歡迎您!".getBytes(); 27 //2.創建數據報,包含響應的數據信息 28 DatagramPacket dp2=new DatagramPacket(data2, data2.length, add, port); 29 //3.響應客戶端 30 ds.send(dp2); 31 //4.關閉資源 32 ds.close(); 33 34 35 } catch (SocketException e) { 36 // TODO Auto-generated catch block 37 e.printStackTrace(); 38 } 39 }
修改后的客戶端:
1 public static void main(String[] args) throws IOException { 2 // TODO Auto-generated method stub 3 /* 4 * 向服務器發送數據 5 */ 6 //1.定義服務器的地址、端口號、數據 7 InetAddress add=InetAddress.getByName("localhost"); 8 int port=8080; 9 byte[] data="用戶名:admin;密碼:123".getBytes(); 10 //2.創建數據報,包含了發送的相關信息 11 DatagramPacket ap=new DatagramPacket(data, data.length, add, port); 12 //3.創建DatagramSocket對象 13 DatagramSocket ds=new DatagramSocket(); 14 //4.向服務器端發送數據報 15 ds.send(ap); 16 /* 17 * 接收服務器端的響應 18 */ 19 //1.創建數據報DatagramPacket,用於接收服務器發送的數據 20 byte[] data2=new byte[1024]; 21 DatagramPacket dp2= new DatagramPacket(data2, data2.length); 22 //2.接收響應 23 ds.receive(dp2); 24 //3.讀取數據 25 String info=new String(data2, 0, dp2.getLength()); 26 System.out.println("我是客戶端,服務器說:"+info); 27 //4.關閉資源 28 ds.close(); 29 }
運行結果:
三、使用多線程實現多客戶端的通信
應用多線程來實現服務器與多客戶端之間的通信。<關於多線程更多的內容以后再總結>
多線程基本步驟:
1.服務器端創建DatagramSocket,循環調用receive()等待客戶端發送數據報。
2.客戶端創建一個DatagramSocket發送數據報到服務器端。
3.服務器端接收客戶端的數據報,創建DatagramPacket與該客戶建立專線接收數據報。
4.建立交互數據報在一個單獨的線程上對話。
5.服務器端繼續等待新的數據報。
------------------------------------------------------------------------------
下面再將上述的例子修改成多線程的通信
新建一個服務器線程處理類UDPServerThread,該類繼承Thread類:
1 /* 2 * 服務器線程處理類 3 */ 4 public class UDPServerThread extends Thread { 5 DatagramSocket ds=null; 6 DatagramPacket dp=null; 7 public UDPServerThread(DatagramSocket ds,DatagramPacket dp){ 8 this.ds=ds; 9 this.dp=dp; 10 } 11 12 13 public void run(){ 14 15 16 try { 17 /* 18 * 接收客戶端發送的數據 19 */ 20 21 //4.讀取數據 22 //String info=Arrays.toString(data); 23 byte[] data=dp.getData(); 24 String info=new String(data, 0, dp.getLength()); 25 System.out.println("我是服務器,客戶端說:"+info); 26 /* 27 * 響應客戶端 28 */ 29 //1.定義客戶端的地址、端口號、數據 30 InetAddress add=dp.getAddress(); 31 int port=dp.getPort(); 32 byte[] data2="歡迎您!".getBytes(); 33 //2.創建數據報,包含響應的數據信息 34 DatagramPacket dp2=new DatagramPacket(data2, data2.length, add, port); 35 //3.響應客戶端 36 ds.send(dp2); 37 38 39 } catch (IOException e) { 40 // TODO Auto-generated catch block 41 e.printStackTrace(); 42 } 43 44 } 45 }
服務器端:
1 /* 2 * 服務器端 3 */ 4 public class UDPSever { 5 6 public static void main(String[] args) throws IOException { 7 // TODO Auto-generated method stub 8 9 try { 10 11 // 1.創建服務器端DatagramSocket,指定端口 12 DatagramSocket ds = new DatagramSocket(8080); 13 System.out.println("服務器即將啟動,等待客戶端的連接..."); 14 byte[] data = new byte[1024]; 15 // 記錄客戶端的數量 16 int count = 0; 17 // 循環偵聽等待客戶端的連接 18 while (true) { 19 DatagramPacket dp = new DatagramPacket(data, data.length); 20 ds.receive(dp); 21 // 創建一個新的線程 22 UDPServerThread udp = new UDPServerThread(ds, dp); 23 // 啟動線程,執行與客戶端的交互 24 udp.start(); 25 count++; 26 System.out.println("此時客戶端數量為:" + count); 27 InetAddress add = dp.getAddress(); 28 System.out.println("當前客戶端的ip地址為" + add.getHostAddress()); 29 } 30 31 } catch (SocketException e) { 32 // TODO Auto-generated catch block 33 e.printStackTrace(); 34 } 35 } 36 37 }
多個客戶端的運行結果:
這樣一個簡單的多線程UDP通信就完成了。
四、UDP和TCP
<解 惑>TCP和UDP編程都用在什么地方?
<回 答>網絡通信,例如QQ客戶端和服務器間的通信。
TCP是面向連接的、可靠的,如果對通信數據的可靠性要求比較高,確保數據對方可以收到,可以使用TCP。
UDP是無連接的,在通信前不會預先建立連接,無法保證可靠性,一般用於對數據可靠性要求不是那么高的場合。
<解 惑>UDP和TCP相比較有什么優缺點?
<回 答>UDP相較於TCP,不需要進行復雜的設定輸入輸出流,只需要設定數據報,即DatagramPacket。而TCP的發送以及接收消息,是通過socket.getInputStream()或者getOutputStream()方法,而UDP是直接設置了,DatagramSocket,通過其send()或者receive()方法來接收或發送消息。TCP的關鍵組成有server端的ServerSocket.accept()方法,這個方法是直到接收到了客戶端的連接才會返回一個Socket,用於接下來的輸入和輸出。
所以說,TCP的數據傳輸是需要提前連接、三方握手,數據傳輸非常安全;
而UDP是不需要提前連接的,無需等待對方回答,所以不保證數據不丟失。
---------------點擊查看更多關於Socket信息------------------