android 通訊學習之路 socket udp tcp
阿新 • • 發佈:2018-12-06
原文 https://www.jianshu.com/p/61de9478c9aa
整體步驟流程
先來說一下整體的步驟思路吧:
- 傳送 UDP 廣播,大家都知道 UDP 廣播的特性是整個網段的裝置都可以收到這個訊息。
- 接收方收到了 UDP 的廣播,將自己的 ip 地址,和雙方約定的埠號,回覆給 UDP 的傳送方。
- 傳送方拿到了對方的 ip 地址以及埠號,就可以發起 TCP 請求了,建立 TCP 連線。
- 保持一個 TCP 心跳,如果發現對方不在了,超時重複 1 步驟,重新建立聯絡。
搭建 UDP 模組
public UDPSocket(Context context) { this.mContext = context; int cpuNumbers = Runtime.getRuntime().availableProcessors(); // 根據CPU數目初始化執行緒池 mThreadPool = Executors.newFixedThreadPool(cpuNumbers * Config.POOL_SIZE); // 記錄建立物件時的時間 lastReceiveTime = System.currentTimeMillis(); messageReceiveList = new ArrayList<>(); Log.d(TAG, "建立 UDP 物件"); // createUser(); }
首先進行一些初始化操作,準備執行緒池,記錄物件初始的時間等等。
public void startUDPSocket() { if (client != null) return; try { // 表明這個 Socket 在設定的埠上監聽資料。 client = new DatagramSocket(CLIENT_PORT); client.setReuseAddress(true); if (receivePacket == null) { // 建立接受資料的 packet receivePacket = new DatagramPacket(receiveByte, BUFFER_LENGTH); } startSocketThread(); } catch (SocketException e) { e.printStackTrace(); } }
緊接著就建立了真正的一個 UDP Socket 端,DatagramSocket,注意這裡傳入的埠號 CLIENT_PORT 的意思是這個 DatagramSocket 在此埠號接收訊息。
/** * 開啟發送資料的執行緒 */ private void startSocketThread() { clientThread = new Thread(new Runnable() { @Override public void run() { receiveMessage(); } }); isThreadRunning = true; clientThread.start(); Log.d(TAG, "開啟 UDP 資料接收執行緒"); startHeartbeatTimer(); }
我們都知道 Socket 中要處理資料的傳送和接收,並且傳送和接收都是阻塞的,應該放在子執行緒中,這裡就開啟了一個執行緒,來處理接收到的 UDP 訊息(UDP 模組上一篇文章講得比較詳細了,所以這裡就不詳細展開了)
/**
* 處理接受到的訊息
*/
private void receiveMessage() {
while (isThreadRunning) {
try {
if (client != null) {
client.receive(receivePacket);
}
lastReceiveTime = System.currentTimeMillis();
Log.d(TAG, "receive packet success...");
} catch (IOException e) {
Log.e(TAG, "UDP資料包接收失敗!執行緒停止");
stopUDPSocket();
e.printStackTrace();
return;
}
if (receivePacket == null || receivePacket.getLength() == 0) {
Log.e(TAG, "無法接收UDP資料或者接收到的UDP資料為空");
continue;
}
String strReceive = new String(receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength());
Log.d(TAG, strReceive + " from " + receivePacket.getAddress().getHostAddress() + ":" + receivePacket.getPort());
//解析接收到的 json 資訊
notifyMessageReceive(strReceive);
// 每次接收完UDP資料後,重置長度。否則可能會導致下次收到資料包被截斷。
if (receivePacket != null) {
receivePacket.setLength(BUFFER_LENGTH);
}
}
}
在子執行緒接收 UDP 資料,並且 notifyMessageReceive 方法通過介面來向外通知訊息。
/**
* 傳送心跳包
*
* @param message
*/
public void sendMessage(final String message) {
mThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
BROADCAST_IP = WifiUtil.getBroadcastAddress();
Log.d(TAG, "BROADCAST_IP:" + BROADCAST_IP);
InetAddress targetAddress = InetAddress.getByName(BROADCAST_IP);
DatagramPacket packet = new DatagramPacket(message.getBytes(), message.length(), targetAddress, CLIENT_PORT);
client.send(packet);
// 資料傳送事件
Log.d(TAG, "資料傳送成功");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
接著 startHeartbeatTimer
開啟一個心跳執行緒,每間隔五秒,就去廣播一個 UDP 訊息。注意這裡 getBroadcastAddress
是獲取的網段 ip,傳送這個 UDP 訊息的時候,整個網段的所有裝置都可以接收到。
到此為止,我們傳送端的 UDP 算是搭建完成了。