Android Framework 常见解决方案(20)UDP广播无效问题

1 现象描述和原理解读

该问题同时存在于android App和Framework系统中。最终效果是在Android系统中直接使用UDP广播无效,有意思的是有的android系统可以,有的Android 系统不行。然而该部分代码自己在Linux上测试时是有效的,代码不变,只是简单的编译移植过来就变得莫名其妙的不行了,头还真是大的不行。

UDP广播接收端的关键实现程序如下所示:

cpp 复制代码
#include <iostream>
#include <string>
#include <cstring>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>

const int UDP_PORT = 19662;

int main() {
    int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (socket_fd == -1) {
        std::cerr << "Failed to create socket" << std::endl;
        return 1;
    }

    struct sockaddr_in local_address{};
    local_address.sin_family = AF_INET;
    local_address.sin_addr.s_addr = INADDR_ANY;
    local_address.sin_port = htons(UDP_PORT);

    if (bind(socket_fd, (struct sockaddr*)&local_address, sizeof(local_address)) == -1) {
        std::cerr << "Failed to bind socket" << std::endl;
        close(socket_fd);
        return 1;
    }

    std::cout << "Listening for UDP broadcast on port " << UDP_PORT << std::endl;

    char buffer[1024];
    struct sockaddr_in sender_address{};
    socklen_t sender_address_length = sizeof(sender_address);

    while (true) {
        ssize_t bytes_received = recvfrom(socket_fd, buffer, sizeof(buffer), 0,
                                          (struct sockaddr*)&sender_address, &sender_address_length);
        if (bytes_received == -1) {
            std::cerr << "Error receiving data" << std::endl;
            close(socket_fd);
            return 1;
        }

        std::string received_message(buffer, bytes_received);
        std::cout << "Received message from " << inet_ntoa(sender_address.sin_addr)
                  << ": " << received_message << std::endl;
    }

    close(socket_fd);
    return 0;
}

UDP广播发送端测试程序如下:

cpp 复制代码
#include <iostream>
#include <string>
#include <cstring>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>

const int UDP_PORT = 19662;
const std::string UDP_BROADCAST_ADDRESS = "192.168.1.255";

int main() {
    int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (socket_fd == -1) {
        std::cerr << "Failed to create socket" << std::endl;
        return 1;
    }

    int broadcast_enable = 1;
    if (setsockopt(socket_fd, SOL_SOCKET, SO_BROADCAST, &broadcast_enable, sizeof(broadcast_enable)) == -1) {
        std::cerr << "Failed to enable broadcast" << std::endl;
        close(socket_fd);
        return 1;
    }

    struct sockaddr_in target_address{};
    target_address.sin_family = AF_INET;
    target_address.sin_port = htons(UDP_PORT);
    if (inet_pton(AF_INET, UDP_BROADCAST_ADDRESS.c_str(), &target_address.sin_addr) <= 0) {
        std::cerr << "Invalid address" << std::endl;
        close(socket_fd);
        return 1;
    }

    std::string message = "Hello UDP Broadcast!";
    ssize_t bytes_sent = sendto(socket_fd, message.c_str(), message.size(), 0,
                                (struct sockaddr*)&target_address, sizeof(target_address));
    if (bytes_sent == -1) {
        std::cerr << "Failed to send data" << std::endl;
        close(socket_fd);
        return 1;
    }

    std::cout << "Sent broadcast message: " << message << std::endl;

    close(socket_fd);
    return 0;
}

在移植到android的过程中实际上是使用android走JNI调用C++的方式来使用,这里就不详述了。

最后分析,发现,果然是android的问题。因为在 移动端 Android 系统中,使用 UDP 广播可能会引发一些耗电的问题,因为 UDP 广播需要 Wi-Fi 连接保持在活动状态,以便能够发送和接收数据包。为了避免在应用程序使用 UDP 广播时造成不必要的电池消耗,开发者可以考虑使用 Wi-Fi 锁来控制 Wi-Fi 连接的状态。

Wi-Fi 锁是 Android 提供的一种机制,允许应用程序在需要时保持 Wi-Fi 连接处于活动状态,而不会由于系统的网络管理策略而被关闭或断开连接。使用 Wi-Fi 锁,应用程序可以确保在需要进行网络通信时,Wi-Fi 连接一直保持活跃,从而避免了频繁的连接和断开过程,这有助于降低耗电量。

2 修改方案(Android All)

Wi-Fi锁的获取和释放源码如下所示:

java 复制代码
import android.content.Context;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.WifiLock;

public class WifiLockManager {
    private WifiLock wifiLock;
    private WifiManager wifiManager;

    public WifiLockManager(Context context) {
        wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "MyWifiLock");
    }

    //获取锁
    public void acquireWifiLock() {
        wifiLock.acquire();
    }

    //释放锁
    public void releaseWifiLock() {
        if (wifiLock.isHeld()) {
            wifiLock.release();
        }
    }
}

这样在UDP广播时就不会出现连不上的情况了。实际上Wi-Fi 锁机制从 Android 1.0 版本就存在,但是随着不同版本的 Android 系统的发布和演变,该机制可能会有一些变化和改进。

具体的行为和影响因 Android 版本和设备厂商而异。在早期的 Android 版本中,Wi-Fi 锁主要用于控制 Wi-Fi 连接的休眠策略,以防止在连接处于活动状态时进入省电模式。然而,随着 Android 版本的更新,系统对网络管理策略进行了多次改进,旨在更好地平衡性能和电池寿命,因此 Wi-Fi 锁的影响和需求可能会因 Android 版本的变化而变化。

另外,不同的硬件厂商可能会在其定制的 Android 版本中对网络管理和电池优化策略进行调整。这意味着在某些设备上,Wi-Fi 锁的行为可能会受到硬件和厂商定制的影响,因此才会出现有的设备能直接广播而有的需要wifi锁这样的情况。

相关推荐
数据法师7 小时前
开源情报收集工具GhostTrack深度测评:IP、手机号、用户名的合规信息查询方案
网络·网络协议·tcp/ip
liang_jy7 小时前
Android SparseArray
android·源码
liang_jy8 小时前
Activity 启动流程扩展篇(一)—— startActivityInner 任务决策全解析
android·源码
想成为优秀工程师的爸爸9 小时前
第三十篇技术笔记:郭大侠学UDS - 人有生老三千疾,望闻问切良方医
网络·笔记·网络协议·tcp/ip·信息与通信
NPE~9 小时前
[App逆向]脱壳实战
android·教程·逆向·android逆向·逆向分析
木易 士心9 小时前
别再只会用 drawCircle 了!一文搞懂 Android Canvas 底层机制
android
AtOR CUES10 小时前
MySQL——表操作及查询
android·mysql·adb
灰子学技术11 小时前
Envoy HTTP Connection Manager (HCM) 技术文档
网络·网络协议·http
怣疯knight12 小时前
安卓App无法增加自定义图片作为图标功能
android
jinanwuhuaguo13 小时前
OpenClaw联邦之心——从孤岛记忆到硅基集体潜意识的拓扑学革命(第二十三篇)
android·人工智能·kotlin·拓扑学·openclaw