局域網通信已經很少被他人所提及了,我曾經還嘗試過通過藍牙構建通信網絡,這次有機會嘗試UDP局域網通信,在這里把一些基本過程和在Android平台上的問題記錄一下。
1. UDP基礎知識
1.1 什么是UDP
Internet 協議集支持一個無連接的傳輸協議,該協議稱為用戶數據報協議(UDP,User Datagram Protocol)。UDP 為應用程序提供了一種無需建立連接就可以發送封裝的 IP 數據報的方法。RFC 768 描述了 UDP。
Internet 的傳輸層有兩個主要協議,互為補充。無連接的是 UDP,它除了給應用程序發送數據包功能並允許它們在所需的層次上架構自己的協議之外,幾乎沒有做什么特別的的事情。面向連接的是 TCP,該協議幾乎做了所有的事情。
——《百度百科》
根據百度百科的解釋,UDP是一個數據傳輸協議,面向無連接的數據傳輸方式,說明此協議丟包概率較高,不適合復雜的網絡環境。UDP報文沒有可靠性保證、順序保證和流量控制字段等,可靠性較差。但是正因為UDP協議的控制選項較少,在數據傳輸過程中延遲小、數據傳輸效率高,適合對可靠性要求不高的應用程序,或者可以保障可靠性的應用程序。在局域網中,數據的到達率幾乎是可以保證的,因此UDP在局域網通信中擁有比TCP更重要的地位。
1.2 UDP通信基本流程
-
設定好統一的端口號;
-
初始化綁定指定端口號的數據接收器;
-
指定接收方的IP地址;
-
准備好輕量數據;
-
發送數據至指定的IP地址;
-
數據接收器觸發后續邏輯。
2. UDP局域網通信的Java實現
2.1 UDP廣播
UDP廣播的實現較為簡單,其接收方的IP地址固定為255.255.255.255,端口號任選,保證發送方與接收方端口號一致且不與其他程序沖突即可,代碼示例如下:
public class UDPManager {
public static final int BUFFER_SIZE = 2048;
public DatagramSocket socket;
public void init() {
try {
//先創建一個綁定了端口號為9527的DatagramSocket
socket = new DatagramSocket(9527);
//開啟數據接收器
openReceiver();
//發送廣播消息
sendBroadcast("Hello World!");
} catch (Exception e) {
e.printStackTrace();
}
}
public void openReceiver() {
//在子線程中循環接收數據
new Thread(new Runnable() {
@override
public void run() {
byte[] buffer = new byte[BUFFER_SIZE];
DatagramPacket dp = new DatagramPacket(buffer, BUFFER_SIZE);
while(socket != null) {
try {
socket.receive(dp);
System.out.println(new String(buffer));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start;
}
public void sendBroadcast(String dataStr) throws IOException {
//發送廣播消息,消息內容為dataStr
if (socket != null) {
byte[] buffer = dataStr.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("255.255.255.255"), 9527);
socket.send(packet);
}
}
}
2.2 UDP單播
UDP單播的實現與廣播類似,其接收方的IP地址需發送消息時傳入,端口號任選,保證發送方與接收方端口號一致且不與其他程序沖突即可,在2.1中展示的UDPManager類中增加單播發送方法即可,代碼示例如下:
public class UDPManager {
//···
public void sendSingle(String dataStr, String targetIP) throws IOException {
//發送單播消息,消息內容為dataStr,接收方IP地址為targetIP
if (socket != null) {
byte[] buffer = dataStr.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName(targetIP), 9527);
socket.send(packet);
}
}
}
2.3 UDP多播
UDP多播的實現類似群聊,需要先加入一個指定IP的群組,之后消息往該IP發送即可,,端口號任選,單播與多播端口號不可相同,保證發送方與接收方端口號一致且不與其他程序沖突即可,並且需要在2.1中展示的UDPManager類中增加MulticastSocket類型的成員變量、修改init方法和openReceiver方法、增加對應的多播方法,代碼示例如下:
public class UDPManager {
//···
//多播地址自選,在224.0.1.0~238.255.255.255之間即可
public static final String MULICAST_ADDRESS = "224.255.0.1"
public MulticastSocket multiSocket;
public void init() {
try {
//先創建一個綁定了端口號為9527的DatagramSocket
socket = new DatagramSocket(9527);
//創建一個綁定端口號為9528的MulticastSocket
multiSocket = new MulticastSocket(9528);
//開啟數據接收器
openReceiver();
//加入多播群組
multiSocket.joinGroup(InetAddress.getByName(MULICAST_ADDRESS))
//發送廣播消息
sendBroadcast("Hello World!");
} catch (Exception e) {
e.printStackTrace();
}
}
public void openReceiver() {
//···
//新建子線程接收多播數據
new Thread(new Runnable() {
@override
public void run() {
byte[] buffer = new byte[BUFFER_SIZE];
DatagramPacket dp = new DatagramPacket(buffer, BUFFER_SIZE);
while(multiSocket != null) {
try {
multiSocket.receive(dp);
System.out.println(new String(buffer));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start;
}
//···
public void sendMultiple(String dataStr) throws IOException {
//發送多播消息,消息內容為dataStr
if (multiSocket != null) {
byte[] buffer = dataStr.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName(MULICAST_ADDRESS), 9528);
multiSocket.send(packet);
}
}
}
2.4 局域網通信基礎構想
在上述三種UDP通信方式的實現過程中,發現廣播方式並不能送達局域網中所有接收者,非同一網段下的接收者將無法收到廣播消息,而單播及多播是可以做到跨網段的。
初步設想,僅使用多播方式,局域網通信流程大致如下:
-
用戶程序啟動,UDP初始化完成;
-
加入組播,發送用戶上線消息,暴露本機信息(IP地址等);
-
接收到用戶上線消息的接收方將該用戶加入在線用戶列表,並再發送一次本機的用戶上線消息;
-
用戶觸發消息發送,消息中附帶本機信息(IP地址等)與指定接收方IP地址或是用戶名等(可以是數組,指定多個接收方);
-
接收方收到消息,判斷此消息指定接收方中是否有本機,若有則處理該消息,否則丟棄;
-
用戶關閉程序,發送用戶下線消息;
-
接收到用戶下線消息的接收方將該用戶移除在線用戶列表。
3. Android平台同一wifi環境下的嘗試
3.1 UDP通信方式上的問題
在Android平台上初步嘗試了UDP的各個通信方式,發現多播方式受到了極大的影響,經多方查證並多次嘗試多播的使用,最后放棄了在Android平台上使用多播方式,如讀者有興趣可以嘗試解決一下。
3.2 安卓wifi局域網通信基礎構想
參考之前的構想,多播方式無法使用的情況下,廣播結合單播的方式成為我的備用方案,大致流程如下:
-
用戶程序啟動,UDP初始化完成;
-
發送廣播,傳輸用戶上線消息,暴露本機信息(IP地址等);
-
接收到廣播的接收方將該用戶加入在線用戶列表,並向該用戶發送一次本機的用戶上線消息;
-
用戶觸發消息發送,以單播的方式發送給指定接收方的IP地址,消息中附帶本機信息(IP地址等);
-
接收方收到消息,處理該消息。
-
用戶關閉程序,發送用戶下線消息;
-
接收到用戶下線消息的接收方將該用戶移除在線用戶列表。
