当前位置: 首页 > article >正文

【短距离通信】【WiFi】精讲Android WiFi P2P架构及代码示例

1. Android WiFi P2P架构

在P2P架构中,定义了两种主要角色:P2P Group Owner(简称GO)和P2P Client(简称GC)。GO的作用类似于Infrastructure BSS中的AP(接入点),而GC的作用类似于Infrastructure BSS中的STA(站点)。当两台设备通过P2P连接后,会随机(也可以手动指定)指派其中一台设备为组拥有者(GO),相当于一台服务器,另一台设备为组成员(GC)。其他设备可以通过与GO设备连接加入组,但不能直接和GC设备连接。
在这里插入图片描述
在Android系统中,WiFi P2P功能是在Android 4.0及更高版本系统中加入的。它可以通过WifiP2pManager类进行实现,这个类提供了许多方法来扫描可用设备、建立P2P连接并传输数据等功能。开发者可以通过这些方法来实现设备之间的文件传输等操作。

在设备发现阶段,Android WiFi P2P使用Probe Request和Probe Response帧来交换设备信息。在2.4GHz的1、6、11频段上发送Probe Request帧,这几个频段被称为Social Channels。一旦Listen Channel选择好后,在整个P2P Discovery阶段就不能更改,用于快速发现周围的Group。

尽管Android WiFi P2P功能强大,目前在Android系统中只是内置了设备的搜索和链接功能,并没有像蓝牙那样有许多应用。在实际开发中,可能需要通过软件手段解决一些逻辑和权限问题。

2. Android应用WiFi P2P实现数据传输

在Android中,WiFi P2P可以通过WifiP2pManager类进行实现。开发者可以通过获取WifiP2pManager实例,并进行广播接受者的创建和注册,调用其他WiFi P2P的API,实现设备间的搜索、连接和数据传输等功能。例如,指定某一台设备为服务器,创建群组并等待客户端的连接请求,而客户端则可以主动搜索附近的设备并加入群组,向服务器发起文件传输请求。
在这里插入图片描述

3. WiFi P2P示例代码(以Android为例)

WifiP2pManager mManager;
WifiP2pManager.Channel mChannel;
BroadcastReceiver mReceiver;
IntentFilter mIntentFilter;

mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
mChannel = mManager.initialize(this, getMainLooper(), null);
mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);

mIntentFilter = new IntentFilter();
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);

registerReceiver(mReceiver, mIntentFilter);

// Discover peers
mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
    @Override
    public void onSuccess() {
        // Success
    }

    @Override
    public void onFailure(int reasonCode) {
        // Failure
    }
});

3.1 添加权限

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

3.2 获取WifiP2pManager和WifiP2pManager.Channel对象

mWifiP2pManager = getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager
mWifiP2pManager?.initialize(this, Looper.getMainLooper()) {
    Log.d(TAG, "Channel断开连接")
}

3.3 服务端创建群组

//服务端创建群组
mWifiP2pManager?.createGroup(mChannel, object : WifiP2pManager.ActionListener {
    override fun onSuccess() {
        Log.d(TAG, "创建群组成功")
    }

    override fun onFailure(reason: Int) {
        Log.w(TAG, "创建群组失败$reason")
    }
})

3.4 客户端搜索对等设备

//客户端搜索对等设备
mWifiP2pManager?.discoverPeers(mChannel, object : WifiP2pManager.ActionListener {
    override fun onSuccess() {
        Log.d(TAG, "搜索成功")
    }

    override fun onFailure(reason: Int) {
        Log.d(TAG, "搜索失败:$reason")
    }

})
//使用异步方法(推荐通过广播监听) 获取设备列表
mWifiP2pManager?.requestPeers(mChannel) {
    mDeviceList.addAll(it.deviceList)
    if (mDeviceList.isEmpty()) {
        //没有设备
        runOnUiThread { Toast.makeText(this, "没有发现设备", Toast.LENGTH_SHORT).show() }
    } else {
        //刷新列表
        runOnUiThread { mDeviceAdapter.notifyDataSetChanged() }
    }
}

3.5 连接设备

val config = WifiP2pConfig().apply {
    this.deviceAddress = wifiP2pDevice.deviceAddress
    this.wps.setup = WpsInfo.PBC
}
mWifiP2pManager?.connect(mChannel, config, object : WifiP2pManager.ActionListener {
    override fun onSuccess() {
        Log.d(TAG, "连接成功")
    }

    override fun onFailure(reason: Int) {
        Log.w(TAG, "连接失败$reason")
    }

})

3.6 服务端创建Socket进行数据读写

// 将数据发送给客户端
//需要创建子线程 否则在主线程网络操作直接闪退
val serverSocket = ServerSocket(8888)
val socket = serverSocket.accept()
val inputStream = socket.getInputStream()
val outputStream = socket.getOutputStream()
//发送数据
outputStream?.write(data)
//此处为了方便 实际需要开启线程读取 并且要有合适的延迟
while (!mQuitReadData) {
    val reader = inputStream.bufferedReader(StandardCharsets.UTF_8)
    val text = reader.readLine()
    Log.d(TAG, "读取到的数据$text")
}

3.7 客户端创建Socket进行数据读写

//需要创建子线程 否则在主线程网络操作直接闪退
val address: InetAddress = info.groupOwnerAddress
val socket = Socket(address, 8888)
val inputStream = socket.getInputStream()
val outputStream = socket.getOutputStream()
//发送数据
outputStream?.write(data)
//此处为了方便 实际需要开启线程读取 并且要有合适的延迟
while (!mQuitReadData) {
    val reader = inputStream.bufferedReader(StandardCharsets.UTF_8)
    val text = reader.readLine()
    Log.d(TAG, "读取到的数据$text")
}
class MainActivity : AppCompatActivity() {

    private val TAG = MainActivity::class.java.simpleName

    private lateinit var mBinding: ActivityMainBinding

    private var mWifiP2pManager: WifiP2pManager? = null
    private var mChannel: WifiP2pManager.Channel? = null

    private var mDeviceList = arrayListOf<WifiP2pDevice>()
    private lateinit var mDeviceAdapter: DeviceAdapter

    private var mQuitReadData = true

    @SuppressLint("NotifyDataSetChanged")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        mBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(mBinding.root)
        ViewCompat.setOnApplyWindowInsetsListener(mBinding.main) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }

        val intentFilter = IntentFilter()
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION)
        registerReceiver(mReceiver, intentFilter)

        mDeviceAdapter = DeviceAdapter(mDeviceList)
        mBinding.rvDeviceList.adapter = mDeviceAdapter
        mDeviceAdapter.mOnItemSelectedListener = object : OnItemSelectedListener {
            override fun onItemSelected(
                parent: AdapterView<*>?,
                view: View?,
                position: Int,
                id: Long
            ) {
                val wifiP2pDevice = mDeviceList[position]
                connect(wifiP2pDevice)
            }

            override fun onNothingSelected(parent: AdapterView<*>?) {
            }
        }

        //通用步骤
        mWifiP2pManager = getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager

        mChannel = mWifiP2pManager?.initialize(this, Looper.getMainLooper()) {
            Log.d(TAG, "Channel断开连接")
        }

        //服务端部分
        //服务端创建群组
        mWifiP2pManager?.createGroup(mChannel, object : WifiP2pManager.ActionListener {
            override fun onSuccess() {
                Log.d(TAG, "创建群组成功")
            }

            override fun onFailure(reason: Int) {
                Log.w(TAG, "创建群组失败$reason")
            }
        })

        //客户端部分
        //客户端搜索对等设备
        mWifiP2pManager?.discoverPeers(mChannel, object : WifiP2pManager.ActionListener {
            override fun onSuccess() {
                Log.d(TAG, "搜索成功")
            }

            override fun onFailure(reason: Int) {
                Log.d(TAG, "搜索失败:$reason")
            }

        })

        //使用异步方法(推荐通过广播监听) 获取设备列表
        mWifiP2pManager?.requestPeers(mChannel) {
            mDeviceList.addAll(it.deviceList)
            if (mDeviceList.isEmpty()) {
                //没有设备
                runOnUiThread { Toast.makeText(this, "没有发现设备", Toast.LENGTH_SHORT).show() }
            } else {
                //刷新列表
                runOnUiThread { mDeviceAdapter.notifyDataSetChanged() }
            }
        }
    }

    /**
     * 连接设备
     */
    private fun connect(wifiP2pDevice: WifiP2pDevice) {
        val config = WifiP2pConfig().apply {
            this.deviceAddress = wifiP2pDevice.deviceAddress
            this.wps.setup = WpsInfo.PBC
        }
        mWifiP2pManager?.connect(mChannel, config, object : WifiP2pManager.ActionListener {
            override fun onSuccess() {
                Log.d(TAG, "连接成功")
                mQuitReadData = false
                transferData("Hello".toByteArray())
            }

            override fun onFailure(reason: Int) {
                Log.w(TAG, "连接失败$reason")
                mQuitReadData = true
            }

        })
    }

    private fun transferData(data: ByteArray) {
        //请求设备连接信息
        mWifiP2pManager?.requestConnectionInfo(mChannel) { info ->
            if (info.groupFormed && info.isGroupOwner) {
                // 将数据发送给客户端
                val serverSocket = ServerSocket(8888)
                val socket = serverSocket.accept()
                val inputStream = socket.getInputStream()
                val outputStream = socket.getOutputStream()
                //发送数据
                outputStream?.write(data)
                //此处为了方便 实际需要开启线程读取 并且要有合适的延迟
                while (!mQuitReadData) {
                    val reader = inputStream.bufferedReader(StandardCharsets.UTF_8)
                    val text = reader.readLine()
                    Log.d(TAG, "读取到的数据$text")
                }
            } else {
                //设备是客户端
                val address: InetAddress = info.groupOwnerAddress
                val socket = Socket(address, 8888)
                val inputStream = socket.getInputStream()
                val outputStream = socket.getOutputStream()
                //发送数据
                outputStream?.write(data)
                //此处为了方便 实际需要开启线程读取 并且要有合适的延迟
                while (!mQuitReadData) {
                    val reader = inputStream.bufferedReader(StandardCharsets.UTF_8)
                    val text = reader.readLine()
                    Log.d(TAG, "读取到的数据$text")
                }
            }
        }
    }

    private val mReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val action = intent?.action;
            if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
                // Check to see if Wi-Fi is enabled and notify appropriate activity
                // 检查 Wi-Fi P2P 是否已启用
                val state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1)
                val isEnabled = (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED)
            } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
                // Call WifiP2pManager.requestPeers() to get a list of current peers
                //异步方法
                // mWifiP2pManager?.requestPeers();
            } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
                // Respond to new connection or disconnections
                // 链接状态变化回调
                // 此广播 会和 WIFI_P2P_THIS_DEVICE_CHANGED_ACTION 同时回调
                // 注册广播、连接成功、连接失败 三种时机都会调用
                // 应用可使用 requestConnectionInfo()、requestNetworkInfo() 或 requestGroupInfo() 来检索当前连接信息。
            } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
                // Respond to this device's wifi state changing
                // 此设备的WiFi状态更改回调
                // 此广播 会和 WIFI_P2P_CONNECTION_CHANGED_ACTION 同时回调
                // 注册广播、连接成功、连接失败 三种时机都会调用
                // 应用可使用 requestDeviceInfo() 来检索当前连接信息。
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        //移除群组
        mWifiP2pManager?.removeGroup(mChannel, null)
        //取消链接
        mWifiP2pManager?.cancelConnect(mChannel, null)
    }
}

Android WiFi P2P使用流程总结:

「权限声明」:
在AndroidManifest.xml中声明必要的权限,包括网络访问权限和文件读写权限。

「初始化」:
在Android应用中,首先需要获取WifiP2pManager实例,并通过调用其initialize方法进行初始化。这将注册应用并准备使用Wi-Fi P2P功能。

初始化完成后,会获得一个Channel对象,它是后续操作的关键。

3.「广播接收与处理」:

在整个过程中,应用需要注册并监听特定的广播,以处理Wi-Fi P2P状态变化、设备发现、连接变化等事件。

这些广播会通知应用有关Wi-Fi P2P操作的状态和结果,以便应用可以做出相应的响应。

4.「设备发现」:

使用WifiP2pManager的discoverPeers方法开始搜索附近的Wi-Fi P2P设备。

设备会在特定的频段(如2.4GHz的1、6、11频段)上发送Probe Request帧来寻找其他设备。

搜索到的设备会作为列表展示在应用界面上,用户可以从中选择想要连接的设备。

5.「建立连接」:

选定一个设备后,作为客户端或服务端(Group Owner,GO)发起连接请求。

通过WifiP2pConfig对象配置连接参数,如目标设备的地址和WPS(Wi-Fi Protected Setup)设置。

使用WifiP2pManager的connect方法尝试建立连接。

6.「连接确认与数据传输」:

一旦连接建立成功,设备之间就可以开始数据传输了。

可以通过Socket编程在设备之间建立连接,并传输文件或其他数据。

根据应用需求,可以创建服务端套接字监听客户端的连接请求,也可以作为客户端主动连接到服务端。

7.「数据传输完成与断开连接」:

数据传输完成后,应用需要适当地关闭套接字和断开Wi-Fi P2P连接。

使用WifiP2pManager的相关方法来断开连接,并释放相关资源。


http://www.kler.cn/a/306319.html

相关文章:

  • Diffusion Policy——斯坦福机器人UMI所用的扩散策略:从原理到其编码实现(含Diff-Control、ControlNet详解)
  • 卓胜微嵌入式面试题及参考答案(2万字长文)
  • GxtWaitCursor:Qt下基于RAII的鼠标等待光标类
  • 区块链技术在慈善捐赠中的应用
  • MyBatis CRUD快速入门
  • 数据结构-集合
  • SpringBoot教程(安装篇) | RabbitMQ的安装
  • MySQl篇(数据类型)(持续更新迭代)
  • 面试常见题之Spring Cloud
  • Redis常见应用场景
  • fsck 命令:修复文件系统错误
  • 读构建可扩展分布式系统:方法与实践05分布式缓存
  • 2-3.Android 存储之存储空间(私有空间、公共空间)
  • 【Android】Room—数据库的基本操作
  • 第108集《大佛顶首楞严经》
  • CAD_Electrical 2022使用记录
  • [Python学习日记-23] Python v2 和 v3 中的字符编码
  • python定时任务,定时爬取水质和天气
  • Navicat On-Prem Server 2.0 | MySQL与MariaDB基础管理功能正式上云
  • Windows电脑A远程连接电脑B
  • Java面试篇基础部分-Java反射机制是什么
  • 镜舟科技与中启乘数科技达成战略合作,共筑数据服务新生态
  • Lua闭包
  • 基于yolov5的混凝土缺陷检测系统python源码+onnx模型+评估指标曲线+精美GUI界面
  • 【Linux C | 终端设备】Linux下 tty、ttyS*、ttyAMA*、console 的区别,以及系统输出重定向(附带代码)
  • NPM私库搭建-verdaccio(Linux)