91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

UDP在Java中的使用方法是什么

發布時間:2021-12-18 16:16:46 來源:億速云 閱讀:131 作者:iii 欄目:移動開發

本篇內容介紹了“UDP在Java中的使用方法是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

UDP 在 Java 中的使用

我們都知道,開發一個 Android 應用程序,目前大多數還是使用的是 Java 語言。在 Java 語言中怎么去使用 UDP 協議呢?

其實 Socket 可以理解為對 TCP、UDP 協議在程序使用層面的封裝,提供出一些 api  來供程序員調用開發,這就是 Socket 最表層的含義。

在 Java 中,與 UDP 相關的類有 DatagramSocket、DatagramPacket  等,關于他們的使用,這里不著重介紹。

好了,假設大家對他們的使用都已大概了解,可以正式開始本文的內容了。

初始化一個 UDPSocket

首先創建一個叫 UDPSocket 的類。

public UDPSocket(Context context) {         this.mContext = context;         int cpuNumbers = Runtime.getRuntime().availableProcessors();         // 根據CPU數目初始化線程池         mThreadPool = Executors.newFixedThreadPool(cpuNumbers * POOL_SIZE);         // 記錄創建對象時的時間         lastReceiveTime = System.currentTimeMillis();     }

在構造方法里,我們進行下一些初始化操作,簡單來說就是創建一個線程池,記錄一下當前時間毫秒值,至于他們有什么用,再往下看:

 public void startUDPSocket() {         if (client != null) return;         try {             // 表明這個 Socket 在設置的端口上監聽數據。             client = new DatagramSocket(CLIENT_PORT);             if (receivePacket == null) {                 // 創建接受數據的 packet                 receivePacket = new DatagramPacket(receiveByte, BUFFER_LENGTH);             }             startSocketThread();         } catch (SocketException e) {             e.printStackTrace();         }     }

這里我們首先創建了一個 DatagramSocket 作為“客戶端”,其實 UDP  本身沒有客戶端和服務端的概念,只有發送方和接收方的概念,我們把發送方暫時當成是一個客戶端吧。

創建 DatagramSocket 對象時,傳入了一個端口號,這個端口號可以在一個范圍內自己定義,表示這個 DatagramSocket  在此端口上監聽數據。

然后又創建了一個 DatagramPacket 對象,作為數據的接收包。

***調用 startSocketThread 啟動發送和接收數據的線程。

/**      * 開啟發送數據的線程      */     private void startSocketThread() {         clientThread = new Thread(new Runnable() {             @Override             public void run() {                 Log.d(TAG, "clientThread is running...");                 receiveMessage();             }         });         isThreadRunning = true;         clientThread.start();         startHeartbeatTimer();     }

首先 clientThread 線程的目的是調用 DatagramSocket receive 方法,因為 receive  方法是阻塞的,不能放在主線程,所以自然開啟一個子線程了。receiveMessage 就是處理接受到的 UDP  數據報,我們先不看接受數據的這個方法,畢竟還沒人發消息呢,自然就談不上收了。

心跳包保持“長連接”

來到本文的***個重點,我們都知道 UDP 本身沒有連接的概念。在 Android 端應用 UDP 和 TCP  的場景是一個手機連接另一個手機的熱點,二者處在同一局域網中。在二者并不知道對方的存在時,怎么才能發現彼此呢?

通過心跳包的方式,雙方都每隔一段時間發一個 UDP 包,如果對方接收到了,那就能知道對方的 ip,建立起通信了。

private static final long TIME_OUT = 120 * 1000;     private static final long HEARTBEAT_MESSAGE_DURATION = 10 * 1000;     /**      * 啟動心跳,timer 間隔十秒      */     private void startHeartbeatTimer() {         timer = new HeartbeatTimer();         timer.setOnScheduleListener(new HeartbeatTimer.OnScheduleListener() {             @Override             public void onSchedule() {                 Log.d(TAG, "timer is onSchedule...");                 long duration = System.currentTimeMillis() - lastReceiveTime;                 Log.d(TAG, "duration:" + duration);                 if (duration > TIME_OUT) {//若超過兩分鐘都沒收到我的心跳包,則認為對方不在線。                     Log.d(TAG, "超時,對方已經下線");                     // 刷新時間,重新進入下一個心跳周期                     lastReceiveTime = System.currentTimeMillis();                 } else if (duration > HEARTBEAT_MESSAGE_DURATION) {//若超過十秒他沒收到我的心跳包,則重新發一個。                     String string = "hello,this is a heartbeat message";                     sendMessage(string);                 }             }         });         timer.startTimer(0, 1000 * 10);     }

這段心跳的目的就是每隔十秒通過 sendMessage 發送一個消息,看看對方能不能收到。若對方收到消息,則刷新下 lastReceiveTime  的時間。

這里我每隔十秒向對方發送了一個字符串。

private static final String BROADCAST_IP = "192.168.43.255";     /**      * 發送心跳包      *      * @param message      */     public void sendMessage(final String message) {         mThreadPool.execute(new Runnable() {             @Override             public void run() {                 try {                     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();                 }             }         });     }

這里就是發送一個消息的代碼。最初在填寫 DatagramPacket 的參數之時,我有一個疑問,那個 targetAddress 其實是自己的 ip  地址。問題來了,我填寫了自己的 ip 地址和對方的端口,怎么可能找得到對方呢?你可能有一個疑惑 "192.168.43.255" 這個自己的 ip  地址是怎么來的,為什么要這么定義?

首先 android 手機開啟熱點,可以理解成一個網關,有一個默認的 ip 地址:"192.168.43.1"

這個 ip 地址不是我瞎編的一個,在 Android 源碼之中,就是這么定義的:

WifiStateMachine

ifcg = mNwService.getInterfaceConfig(intf);                         if (ifcg != null) {                             /* IP/netmask: 192.168.43.1/255.255.255.0 */                             ifcg.setLinkAddress(new LinkAddress(                                     NetworkUtils.numericToInetAddress("192.168.43.1"), 24));                             ifcg.setInterfaceUp();                             mNwService.setInterfaceConfig(intf, ifcg);                         }

所以我是知道所謂打開熱點一方的 ip 地址,而 UDP 發送消息時還有一個特性,就是發出去的消息,處在整個網關的設備是都可以接收到的,所以我自己的 ip  地址就定為了 "192.168.43.255",所以這個 ip 地址和 "192.168.43.1" 在同一網關中,你發送的消息,它是可以收到的。

至于怎么判斷兩個 ip 地址是否處在同一網段中:

判斷兩個IP大小及是否在同一個網段中

來做一個階段總結:

首先我們創建了一個發送端 DatagramSocket,啟動了一個心跳程序,每間隔一段時間發送一個心跳包。

因為我知道熱點方的 ip 地址是默認的 "192.168.43.1",并且 UDP 的特性就是發送的消息同一網段的設備都可以收到。所以發送方的 ip  地址定為了與熱點一方處在同一網段的 "192.168.43.255"。

事件與數據

事件與數據這兩個模塊與業務就緊密相關了。

先來說數據,雙方發送的數據格式你們可以隨意定義,當然我覺得還是定義成常規的 Json  格式就好。其中可以包含一些關鍵的事件字段:比如廣播心跳包、收到心跳包給對方上線的應答包、超時的下線包、以及各種業務相關的數據等等。

當然發送數據時是轉換成二進制數組發送的。發送中文字符、圖片等都沒有問題,但是可能有一些細節需要注意,隨時 google 一下就好了。

再來說下事件:

與業務無關的事件有哪些?

比如:

  • DatagramSocket.send 方法之后就是發送數據成功的事件;

  • DatagramSocket.receive 方法之后是數據接收成功的事件;

  • 在心跳包發送一段時間,仍沒有接到回信時,是連接超時的事件;

  • 與業務相關的事件就和我們上文提到的數據類型有關了,設備上線,心跳包回應等等。

事件又如何發送出去,通知到各個頁面呢?用 Listener、或者其他事件總線的三方庫都沒問題,看你自己選擇了。

處理接收的消息

/**     * 處理接受到的消息     */    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(), 0, receivePacket.getLength());            Log.d(TAG, strReceive + " from " + receivePacket.getAddress().getHostAddress() + ":" + receivePacket.getPort());            //解析接收到的 json 信息            // 每次接收完UDP數據后,重置長度。否則可能會導致下次收到數據包被截斷。            if (receivePacket != null) {                receivePacket.setLength(BUFFER_LENGTH);            }        }    }

處理接收消息時,有幾個值得注意的點:

  • receive 方法是阻塞的,沒收到數據包時會一直阻塞,所以要放到子線程中;

  • 每次接收到消息之后,重新調用 receivePacket.setLength;

  • 收到消息刷新lastReceiveTime的值,暫停心跳包的發送;

  • 處理收到的數據具體在業務上就是剛才我們談的發送數據的問題,視業務而定。

“用戶”的概念

假如一個手機已經開啟了熱點,若多個手機與他相連接,則多個手機發送的消息它都可以收到。如果發送方的端口與接收方的端口相同的話,甚至自己發的消息,自己都可以收到。這就很尷尬了,也就是說我們既要剔除自己發給自己的消息,也得區分不同手機發來的消息,這個時候就理應有一個“用戶”的概念。

創建 User 對象,有哪些屬性可以看自己的業務,本文的例子就有 ip、imei、以及 softversion。

/**     * 創建本地用戶信息     */    private void createUser() {        if (localUser == null) {            localUser = new Users();        }        if (remoteUser == null) {            remoteUser = new Users();        }        localUser.setImei(DeviceUtil.getDeviceId(mContext));        localUser.setSoftVersion(DeviceUtil.getPackageVersionCode(mContext));        if (WifiUtil.getInstance(mContext).isWifiApEnabled()) {// 判斷當前是否是開啟熱點方            localUser.setIp("192.168.43.1");        } else {// 當前是開啟 wifi 方            localUser.setIp(WifiUtil.getInstance(mContext).getLocalIPAddress());            remoteUser.setIp(WifiUtil.getInstance(mContext).getServerIPAddress());        }    }    /**     * <p><b>IMEI.</b></p> Returns the unique device ID, for example, the IMEI for GSM and the MEID     * or ESN for CDMA phones. Return null if device ID is not available.     * <p>     * Requires Permission: READ_PHONE_STATE     *     * @param context     * @return     */    public synchronized static String getDeviceId(Context context) {        if (context == null) {            return "";        }        String imei = "";        try {            TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);            if (tm == null || TextUtils.isEmpty(tm.getDeviceId())) {                // 雙卡雙待需要通過phone1和phone2獲取imei,默認取phone1的imei。                tm = (TelephonyManager) context.getSystemService("phone1");            }            if (tm != null) {                imei = tm.getDeviceId();            }        } catch (SecurityException e) {            e.printStackTrace();        }        return imei;    }</p>

這里就不將所有的代碼展開來看了。如果有了手機的 imei  號,那很容易就可以來做身份的區分,你既可以區分不同的發送方,也可以剔除掉自己發給自己的消息。當然如果需要更多的信息,可以按照自己的業務區分,將這些信息作為發送的  messge,通過 Socket 發送。

“UDP在Java中的使用方法是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

阿荣旗| 安达市| 西青区| 修水县| 依兰县| 桃江县| 息烽县| 弥渡县| 大荔县| 汶川县| 商都县| 革吉县| 海盐县| 舟曲县| 沙洋县| 南和县| 彭泽县| 乡城县| 达日县| 本溪市| 景德镇市| 巴楚县| 定陶县| 文山县| 嵊泗县| 项城市| 威远县| 红河县| 吉隆县| 盐边县| 营山县| 瑞昌市| 乌拉特前旗| 阿瓦提县| 尼木县| 太白县| 色达县| 通许县| 肥西县| 图木舒克市| 名山县|