## Java編程那些事兒106——網絡編程技術5
陳躍峰
出自:[http://blog.csdn.net/mailbomb](http://blog.csdn.net/mailbomb)
該示例的功能是實現將客戶端程序的系統時間發送給服務器端,服務器端接收到時間以后,向客戶端反饋字符串“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網絡編程就介紹這么多了,下面將介紹一下網絡協議的概念。
- 前言
- (1)序言
- (2)程序設計是什么?
- (3)你適合學習程序設計嗎?
- (4)如何學好程序設計?
- (5)程序設計介紹小結
- (6)計算機軟件基本概念
- (7)進制的概念
- (8)計算機內部的數據表達
- (9)網絡編程基礎
- (10)Java語言簡介
- (11)JDK的獲得、安裝和配置
- (12)第一個HelloWorld程序
- (13)Eclipse基本使用
- (14)Eclipse基礎使用進階
- (15)如何學好Java語法
- (16)代碼框架、關鍵字和標識符
- (17)基本數據類型
- (18)變量和常量
- (19)數據類型轉換
- (20)空白、語句結束和注釋
- (21)算術運算符
- (22)比較運算符
- (23)邏輯運算符
- (24)賦值運算符
- (25)位運算符
- (26)移位運算符
- (27)其它運算符
- (28)運算符優先級
- (29)表達式
- (30)流程控制基礎
- (31)if語句語法(1)
- (32)if語句語法(2)
- (33)if語句語法(3)
- (34)switch語句語法
- (35)while語句語法
- (36)do-while語句語法
- (37)for語句語法
- (38)break和continue語句
- (39)流程控制綜合示例1
- (40)流程控制綜合示例2
- (41)流程控制綜合示例3
- (42)流程控制綜合練習
- (43)數組概述
- (44)數組基礎語法
- (45)數組使用示例1
- (46)數組使用示例2
- (47)數組使用示例3
- (48)多維數組基礎
- (49)多維數組使用示例1
- (50)多維數組使用示例2
- (51)多維數組練習
- (52)方法聲明
- (53)方法聲明示例
- (54)方法調用
- (55)方法重載和參數傳遞
- (56)方法練習
- (57)面向對象基礎
- (58)類(一)
- (59)類(二)
- (60)對象
- (61)面向對象設計方法和面向對象特性(一)
- (62)繼承(二)
- (63)多態性
- (64)訪問控制符、修飾符和其它關鍵字
- (65)static修飾符
- (66)final修飾符
- (67)this和super
- (68)抽象類和接口(一)
- (69)抽象類和接口(二)
- (70)抽象類和接口(三)
- (71)內部類簡介
- (72)包的概念
- (73)JDK文檔使用
- (74)java.lang包介紹1
- (75)String類使用
- (76)StringBuffer類和System類
- (77)包裝類
- (78)時間和日期處理
- (79)Random隨機處理
- (80)集合框架簡述
- (81)異常處理概述
- (82)異常處理語法1
- (83)異常處理語法2
- (84)IO簡介
- (85)IO類體系
- (86)文件操作之File類使用
- (87)文件操作之讀取文件
- (88)文件操作之寫文件
- (89)讀取控制臺輸入
- (90)裝飾流使用1
- (91)裝飾流使用2
- (92)IO使用注意問題
- (93)多線程基礎
- (94)多線程實現方式1
- (95)多線程實現方式2
- (96)多線程使用示例1
- (97)多線程使用示例2
- (98)多線程問題及處理1
- (99)多線程問題及處理2
- (100)多線程問題及處理3
- (101)網絡編程概述
- (102)網絡編程技術1
- (103)網絡編程技術2
- (104)網絡編程技術3
- (105)網絡編程技術4
- (106)網絡編程技術5
- (107)網絡協議概念
- (108)網絡編程示例1
- (109)網絡編程示例2
- (110)網絡編程小結