1. 程式人生 > >Android傳送和接收UDP廣播

Android傳送和接收UDP廣播

要實現在Android平臺上發UDP廣播,可能需要先了解一下什麼是廣播地址

廣播地址

廣播地址(Broadcast Address)是專門用於同時向網路中所有工作站進行傳送的一個地址。在使用TCP/IP協議的網路中,主機標識端host ID 為全1的IP地址為廣播地址,廣播的分組傳送給host ID段所涉及的所有計算機。例如,對於10.1.1.0(255.255.255.0)網段,其廣播地址為10.1.1.255(255即為2進位制的11111111),當發出一個目的地址為10.1.1.255的分組(封包)時,它將被分發給該網段上的所有計算機。

這裡寫圖片描述
[圖片來自百度百科]

廣播地址有兩類:

  • 受限廣播
    它不被路由傳送,但會被送到相同物理網路段上的所有主機,IP地址的網路欄位和主機欄位全為1就是地址255.255.255.255。
  • 直接廣播
    網路廣播會被路由傳送,並會發送到專門網路上的每臺主機,IP地址的網路欄位定義這個網路,主機欄位通常為全為1,如 192.168.10.255

關於廣播地址的其他知識,大家可以自行搜尋學習。

當我們知道有廣播地址這個東西之後,就能很方便地在Android平臺上實現傳送廣播和接收廣播了。

一臺Android作為Server端傳送廣播,那麼此時的廣播地址怎麼確定,因為,作為Server端的手機可能是連線到一個路由器上,也有可能是自己作為AP裝置發熱點,讓Client端去連線。對於以上的兩種情況,廣播地址是有所不同的:

  • 第一種情況(server端連線到路由器):見下面程式碼片段。
  • 第二種情況(server端作為AP裝置傳送熱點):
    在這種情況下,IP也是可以確定,有人在分析Android原始碼的時候,發現如果Android裝置開啟了wifi熱點,那麼,該裝置的本地IP是固定的,是192.168.43.1,那麼我們就可以知道此時對應的廣播地址就是192.168.43.255。

通過以下程式碼可以獲取到本地IP(java)

    public static String getLocalIPAddress() {
        Enumeration<NetworkInterface> enumeration = null;
        try
{ enumeration = NetworkInterface.getNetworkInterfaces(); } catch (SocketException e) { Logger.w(e); } if (enumeration != null) { // 遍歷所用的網路介面 while (enumeration.hasMoreElements()) { NetworkInterface nif = enumeration.nextElement();// 得到每一個網路介面繫結的地址 Enumeration<InetAddress> inetAddresses = nif.getInetAddresses(); // 遍歷每一個介面繫結的所有ip if (inetAddresses != null) while (inetAddresses.hasMoreElements()) { InetAddress ip = inetAddresses.nextElement(); if (!ip.isLoopbackAddress() && isIPv4Address(ip.getHostAddress())) { return ip.getHostAddress(); } } } } return ""; } /** * Ipv4 address check. */ private static final Pattern IPV4_PATTERN = Pattern.compile("^(" + "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" + "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"); /** * Check if valid IPV4 address. * * @param input the address string to check for validity. * @return True if the input parameter is a valid IPv4 address. */ public static boolean isIPv4Address(String input) { return IPV4_PATTERN.matcher(input).matches(); }

通過以下程式碼可以直接獲取廣播地址,如果是開啟wifi熱點直接返回“192.168.43.255”(kotlin):

    companion object {
        fun getBroadcastAddress(context: Context): InetAddress {
            if (isWifiApEnabled(context)) { //判斷wifi熱點是否開啟
                return InetAddress.getByName("192.168.43.255")  //直接返回
            }
            val wifi: WifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
            val dhcp: DhcpInfo = wifi.dhcpInfo ?: return InetAddress.getByName("255.255.255.255")
            val broadcast = (dhcp.ipAddress and dhcp.netmask) or dhcp.netmask.inv()
            val quads = ByteArray(4)
            for (k in 0..3) {
                quads[k] = ((broadcast shr k * 8) and 0xFF).toByte()
            }
            return InetAddress.getByAddress(quads)
        }

        /**
         * check whether the wifiAp is Enable
         */
        private fun isWifiApEnabled(context: Context): Boolean {
            try {
                val manager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
                val method = manager.javaClass.getMethod("isWifiApEnabled")
                return method.invoke(manager) as Boolean
            } catch (e: NoSuchMethodException) {
                e.printStackTrace()
            } catch (e: IllegalAccessException) {
                e.printStackTrace()
            } catch (e: InvocationTargetException) {
                e.printStackTrace()
            }
            return false
        }
    }

如果,廣播地址能確定了,下面就可以進行實現傳送廣播(Server)和接收廣播(Client)了

我們先定義一個通用的UDP廣播類:包括(獲取廣播地址,開啟和關閉廣播、傳送廣播包和接收廣播包)

UDPBroadcaster.kt:(kotlin)

class UDPBroadcaster(var mContext: Context) {
    private val TAG:String = UDPBroadcaster::class.java.simpleName
    private var mDestPort = 0
    private var mSocket: DatagramSocket? = null
    private val ROOT_PATH:String = Environment.getExternalStorageDirectory().path
    /**
     * 開啟
     */
    fun open(localPort: Int, destPort: Int): Boolean {
        mDestPort = destPort
        try {
            mSocket = DatagramSocket(localPort)
            mSocket?.broadcast = true
            mSocket?.reuseAddress = true
            return true
        } catch (e: SocketException) {
            e.printStackTrace()
        }
        return false
    }

    /**
     * 關閉
     */
    fun close(): Boolean {
        if (mSocket != null && mSocket?.isClosed?.not() as Boolean) {
            mSocket?.close()
        }
        return true
    }

    /**
     * 傳送廣播包
     */
    fun sendPacket(buffer: ByteArray): Boolean {
        try {
            val addr = getBroadcastAddress(mContext)
            Log.d("$TAG addr",addr.toString())
            val packet = DatagramPacket(buffer, buffer.size)
            packet.address = addr
            packet.port = mDestPort
            mSocket?.send(packet)
            return true
        } catch (e1: UnknownHostException) {
            e1.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return false
    }

    /**
     * 接收廣播
     */
    fun recvPacket(buffer: ByteArray): Boolean {
        val packet = DatagramPacket(buffer, buffer.size)
        try {
            mSocket?.receive(packet)
            return true
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return false
    }

    companion object {
        /**
         * 獲取廣播地址
         */
        fun getBroadcastAddress(context: Context): InetAddress {
            if (isWifiApEnabled(context)) { //判斷wifi熱點是否開啟
                return InetAddress.getByName("192.168.43.255")  //直接返回
            }
            val wifi: WifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
            val dhcp: DhcpInfo = wifi.dhcpInfo ?: return InetAddress.getByName("255.255.255.255")
            val broadcast = (dhcp.ipAddress and dhcp.netmask) or dhcp.netmask.inv()
            val quads = ByteArray(4)
            for (k in 0..3) {
                quads[k] = ((broadcast shr k * 8) and 0xFF).toByte()
            }
            return InetAddress.getByAddress(quads)
        }

        /**
         * check whether the wifiAp is Enable
         */
        private fun isWifiApEnabled(context: Context): Boolean {
            try {
                val manager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
                val method = manager.javaClass.getMethod("isWifiApEnabled")
                return method.invoke(manager) as Boolean
            } catch (e: NoSuchMethodException) {
                e.printStackTrace()
            } catch (e: IllegalAccessException) {
                e.printStackTrace()
            } catch (e: InvocationTargetException) {
                e.printStackTrace()
            }
            return false
        }
    }
}

宣告許可權

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>

Server:

傳送廣播包(kotlin):


class MainActivity : AppCompatActivity(), View.OnClickListener {

    val TAG: String = MainActivity::class.java.simpleName
    val SEND_PORT: Int = 8008
    val DEST_PORT: Int = 8009
    var isClosed: Boolean = false
    var sendBuffer: String = "This is UDP Server"
    lateinit var mSendBtn: Button
    lateinit var mCloseBtn: Button
    lateinit var mScrollView: ScrollView
    lateinit var mLogTx: TextView
    lateinit var mUDPBroadCast: UDPBroadcaster
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mUDPBroadCast = UDPBroadcaster(this)
        initView()
        initEvent()
    }

    private fun initView() {
        mSendBtn = findViewById(R.id.btn_send) as Button
        mCloseBtn = findViewById(R.id.btn_close) as Button
        mScrollView = findViewById(R.id.scrollview) as ScrollView
        mLogTx = findViewById(R.id.log) as TextView
    }

    private fun initEvent() {
        mSendBtn.setOnClickListener(this)
        mCloseBtn.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.btn_send -> sendUDPBroadcast()
            R.id.btn_close -> closeUDPBroadcast()
        }
    }

    private fun closeUDPBroadcast() {
        isClosed = true
    }


    private fun sendUDPBroadcast() {
        isClosed = false
        mUDPBroadCast.open(SEND_PORT, DEST_PORT) //開啟廣播
        val buffer: ByteArray = sendBuffer.toByteArray()
        Thread(Runnable {
            while (!isClosed) {
                try {
                    Thread.sleep(500) //500ms 延時
                } catch (e: Exception) {
                    e.printStackTrace()
                }
                mUDPBroadCast.sendPacket(buffer) //傳送廣播包
                addLog("$TAG data: ${String(buffer)}")
            }
            mUDPBroadCast.close() //關閉廣播
        }).start()
    }

    private fun addLog(log: String) {
        var mLog: String = log
        if (mLog.endsWith("\n").not()) {
            mLog += "\n"
        }
        mScrollView.post(Runnable {
            mLogTx.append(mLog)
            mScrollView.fullScroll(ScrollView.FOCUS_DOWN)
        })
    }
}

Client:

接收廣播(kotlin):

class MainActivity : AppCompatActivity(), View.OnClickListener {

    val TAG:String = MainActivity::class.java.simpleName
    val LOCAL_PORT:Int = 8009
    val DEST_PORT:Int = 8008
    lateinit var mRecvBtn: Button
    lateinit var mCloseBtn: Button
    lateinit var mScrollView: ScrollView
    lateinit var mLogTx: TextView
    var isClosed:Boolean = false
    lateinit var mUDPBroadCaster:UDPBroadcaster
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mUDPBroadCaster = UDPBroadcaster(this)
        initView()
        initEvent()
    }

    private fun initEvent() {
        mRecvBtn.setOnClickListener(this)
        mCloseBtn.setOnClickListener(this)
    }

    private fun initView() {
        mRecvBtn = findViewById(R.id.btn_receive) as Button
        mCloseBtn = findViewById(R.id.btn_close) as Button
        mScrollView = findViewById(R.id.scrollView) as ScrollView
        mLogTx = findViewById(R.id.log) as TextView

    }
    override fun onClick(v: View?) {
        when(v?.id){
            R.id.btn_receive->recvUDPBroadcast()
            R.id.btn_close->cancelRecv()
        }
    }

    private fun cancelRecv() {
        isClosed = true
    }

    private fun recvUDPBroadcast() {
        isClosed = false
        mUDPBroadCaster.open(LOCAL_PORT,DEST_PORT)
        var buffer:ByteArray = kotlin.ByteArray(1024)
        val packet = DatagramPacket(buffer, buffer.size)
        Thread(Runnable {
            while (!isClosed){
                try{
                    Thread.sleep(500) //500ms延時
                }catch (e:Exception){e.printStackTrace()}
                mUDPBroadCaster.recvPacket(packet) //接收廣播
                val data:String = String(packet.data)
                addLog("$TAG data: $data")
                addLog("$TAG addr: ${packet.address}")
                addLog("$TAG port: ${packet.port}")
            }
            mUDPBroadCaster.close() //退出接收廣播
        }).start()
    }
    private fun addLog(log: String) {
        var mLog: String = log
        if (mLog.endsWith("\n").not()) {
            mLog += "\n"
        }
        mScrollView.post(Runnable {
            mLogTx.append(mLog)
            mScrollView.fullScroll(ScrollView.FOCUS_DOWN)
        })
    }
}

程式碼很少,也很容易看懂。傳送和接收廣播的截圖如下:

  • Server

    這裡寫圖片描述

  • Client

    這裡寫圖片描述

原始碼下載地址

相關推薦

Android傳送接收UDP廣播

要實現在Android平臺上發UDP廣播,可能需要先了解一下什麼是廣播地址 廣播地址 廣播地址(Broadcast Address)是專門用於同時向網路中所有工作站進行傳送的一個地址。在使用TCP/IP協議的網路中,主機標識端host ID 為全1的IP

用C寫一個UDP傳送接收程式

1、UDP網路程式設計主要流程 UDP協議的程式設計框架,客戶端和伺服器之間的差別在於伺服器必須使用bind()函式來繫結偵聽的本地UDP埠,而客戶端則可以不進行繫結,直接傳送到伺服器地址的某個埠地址。框圖如圖1.3所示 UDP協議的伺服器端流程 伺服器流程主要分為下述6個部分,即建立套

UDP完成資料的傳送接收的程式碼

要實現UDP通訊需要建立一個傳送端程式和一個接收端程式,很明顯,在通訊時只有接收端程式先執行,才能避免因傳送端傳送的資料無法接收,而造成資料丟失。因此,首先需要來完成接收端程式的編寫。 UDP完成資料的傳送 /* * 傳送端  * 1,建立DatagramSocket

廣播的註冊、傳送接收過程

--摘自《Android進階解密》 一。廣播的註冊過程(動態註冊) 1)靜態註冊在應用安裝時由PackageManagerService來完成註冊過程 2)IIntentReceiver是一個Binder介面,用於廣播的跨程序的通訊,它在LoadedApk.ReceiverDispatcher.In

android連線SSM傳送接收資料

//註冊賬戶 URL url=null; HttpURLConnection httpURLConnection=null; String result=null,re="";

Android socket 使用PrintWriterBufferedReader傳送接收出現亂碼問題解決

專案中用到了Android和C++的通訊,選擇了用socket 傳送字元的方式,一開始使用的程式碼是: socket=new Socket(); InetSocketAddress isa = new InetSocketAddress(IP_STR,

26 API-網路程式設計(網路概述,Socket通訊機制,UDP協議傳送接收資料,TCP協議傳送接收資料)

1:網路程式設計(理解) (1)網路程式設計:用Java語言實現計算機間資料的資訊傳遞和資源共享(2)網路程式設計模型 l網路模型一般是指 OSI(Open System Interconnection開放系統互連)參考模型 TCP/IP參考模型 (3)網路程式

UDP網路程式傳送接收資料

建立一個基於udp的網路程式流程很簡單,具體步驟如下: 1.建立客戶端套接字 2.傳送/接收資料 3.關閉套接字 UDP傳送和接收資料 在 Python 中 使用socket 模組的函式 socket 就可以完成: import socket #

Android中基於ServerSocket的實際使用與簡單介紹(內附一個PC端群控多臺手機的訊息傳送接收Demo)

一、要想將ServerSocket整明白首先至少先要知道或是瞭解幾點基礎內容部分(大神或是有一定能力的小神跳過): 1.ServerSocket是需要在服務端定義書寫的而在客戶端不需要ServerSocket,客戶端只需要建立socket就可以了。 2.socket需要在子

通過UDP傳送接收資料包

傳送端: package net.udp; import java.io.IOException; import java.net.DatagramPacket; import java.net.Da

Android8.0.0-r4——廣播的註冊、傳送接收過程

原網址:https://blog.csdn.net/itachi85/article/details/716292011.廣播的註冊過程BroadcastReceiver的註冊分為兩種,分別是靜態註冊和動態註冊,靜態註冊在應用安裝時由PackageManagerService

node總結之GET/POST請求的傳送接收了解下

在我們的現實場景中,我們的node伺服器都需要跟使用者的瀏覽器打交道,也就是說建立一個互動的關係。那麼,這個關係之間的通訊基本上比較熟悉的就是get/post這種方式了。咱們這刺激來簡單看下在node中,是如何接收和處理這些關係的。 由於GET請求直接被嵌入在路徑中,URL是完整的請求路徑,

zigbee 之ZStack-2.5.1a原始碼分析(三)無線資料傳送接收

前面說過SampleApp_Init和SampleApp_ProcessEvent是我們重點關注的函式,接下來分析無線傳送和接收相關的程式碼: 在SampleApp_ProcessEvent函式中: if ( events & SYS_EVENT_MSG ) {  &nbs

【Java TCP/IP Socket程式設計】----傳送接收資料----構建解析協議訊息

--------筆記來自於書籍《Java TCP/IP Socket程式設計》。 簡介 使用套接字時,通常要麼是需要同時建立通訊通道兩端的程式,要麼實現一個給定的協議進行通訊。如果知道通訊雙方都使用java實現,且擁有對協議的完全控制權,那麼就可以使用Java的內建工具如Serialiabl

【Java TCP/IP Socket程式設計】----傳送接收資料----訊息成幀與解析

目錄   簡介 成幀與解析 成幀技術案例 簡介 在程式中使用套接字向其他程式提供資訊或者使用其他程式提供的資訊,這就需要任何需要交換資訊的程式間在資訊編碼方式上達成共識(包含了資訊交換的形式和意義),稱為協議,用來實現特定的應用程式的協議叫應用程式協議。大部分應

ARM40-A5應用程式——CAN匯流排的傳送接收

ARM40-­A5應用程式——CAN匯流排的傳送和接收 版權宣告:本文為博主原創文章,允許轉載。      ARM40-A5系列板卡共有2路隔離CAN匯流排,CAN匯流排的引腳定義見《ARM40-­A5指南——CAN匯流排介面與測試》。 一、shell中CAN匯流排的接收與傳送

JavaMail傳送接收郵件(轉載)

出處: http://blog.csdn.net/zapldy/article/details/3971579 一、JavaMail概述:        JavaMail是由Sun定義的一套收發電子郵

ROS 用 roboware實現節點資訊傳送接收

在ros下實現節點程式設計,實現一個節點發送訊息,另一個節點接收。實現方式有多種,可以直接在命令視窗建立工作空間包以及節點,用catkin_make進行編譯,新增.bash路徑,然後執行rosrun  package  node_name 。這種方式對於一個ROS初學者來說容易出錯,而且很多網上的教程中出現了

MQTT Java客戶端Eclipse paho實現資料的傳送接收

MQTT(MQ Telemetry Transport)是IBM開發的一種網路應用層的協議 使用場景: 1、不可靠、網路頻寬小的網路 2、執行的裝置CPU、記憶體非常有限 特點: 1、基於釋出/訂閱模型的協議 2、他是二進位制協議,二進位制的特點就是緊湊、佔用

js傳送接收二進位制位元組流資料

傳送二進位制資料 var oReq = new XMLHttpRequest(); oReq.open("POST", url, true); oReq.onload = function (oEvent) { // Uploaded. }; var blo