Android发送和接收UDP广播
来源:互联网 发布:华为无线优化工程师 编辑:程序博客网 时间:2024/05/16 16:00
要实现在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
源码下载地址
Demo(CSDN下载)
- Android发送和接收UDP广播
- android 广播发送和接收
- android发送和接收广播
- Android UDP广播 客户端循环发送与接收
- C语言socket UDP广播的发送和接收示例
- Android动态广播的发送和接收
- Android——发送和接收广播
- android广播监听接收和发送短信
- Android 发送和接收定制广播
- Android 发送和接收定制广播
- 发送广播和接收广播
- Android发送UDP广播
- 接收和发送广播
- 接收和发送广播
- android 实现UDP发送和接收
- UDP 发送和接收
- 发送UDP广播并接收数据
- iphone发送udp广播并接收数据
- Puppet —自动化部署工具详解
- HDU2066 一个人的旅行
- jsp与servlrt之间传值
- static的作用域
- J2EE进阶之Cookie和Session 十一
- Android发送和接收UDP广播
- 视音频数据的数据源分析---音频
- Service的启动过程分析
- IntelliJ中的main函数和System.out.println()快捷键
- 小哥哥带你撸设计模式之---策略模式
- 【14】PMP规划质量管理,控制质量工具-控制图
- 中心缓存:CentralCache
- UC号、百家号,为什么这些科技公司都押注“内容”?
- Unity Shader.Find找不到