Wi-Fi直连分享:Android设备间的高速连接
Wi-Fi直连分享:Android设备间的高速连接
引言
随着无线局域网(Wi-Fi)的普及和发展,使用Wi-Fi直连技术(P2P)在没有中间接入点的情况下实现设备间直接互联成为可能。通过Wi-Fi直连,具备相应硬件的Android 4.0及更高版本设备可以实现高速连接通信,比传统蓝牙连接具有更远的传输距离。这项技术对于多人游戏、照片共享等需要设备间数据共享的应用非常有用。
Wi-Fi直连的核心API是WifiP2pManager
类。
普通连接,代码实现
初始化
private WifiP2pManager.Channel mChannel; // app与framework联系的纽带
private WifiP2pManager mManager;
...
mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
mChannel = mManager.initialize(this, getMainLooper(), null);
创建群组
只有服务端设备需要执行此步骤,客户端设备可以跳过。即使不创建群组,设备仍然可以进行后续的设备发现和连接操作。一旦连接成功,这两台设备将自动形成一个群组,并在其中选择一台设备作为群主。创建群组后,会在Wi-Fi列表中出现一个名为DIRECT_**_DeviceName
的网络,这个网络是为没有Wi-Fi直连功能的设备准备的,它们可以通过连接此网络来加入群组。但是使用此方式加入的设备不会收到WIFI_P2P_CONNECTION_CHANGED_ACTION
广播,不过在调用mManager.requestGroupInfo
时,可以获取到这些设备的信息。
mManager.createGroup(mChannel, new WifiP2pManager.ActionListener() {@Overridepublic void onSuccess() {appendLog("创建群组成功");// 在这里可以创建ServerSocket并等待客户端接入...}@Overridepublic void onFailure(int reason) {appendLog("创建群组失败");...}
});
很多地方都会使用到WifiP2pManager.ActionListener
回调,下面列出了一些可能的错误原因:
/*** Wi-Fi直连操作失败*/
public static final int ERROR = 0;/*** 设备不支持Wi-Fi直连功能*/
public static final int P2P_UNSUPPORTED = 1;/*** 操作失败,框架忙于处理其他请求*/
public static final int BUSY = 2;/*** 操作失败,未添加任何服务请求*/
public static final int NO_SERVICE_REQUESTS = 3;
发现设备
mManager.discoverPeers(mChannel, mActionListener);
客户端设备直接执行此操作,不需要创建群组。两台设备必须同时执行设备发现操作,才能相互发现对方的存在。
停止发现设备
mManager.stopPeerDiscovery(mChannel, mActionListener);
连接设备
连接设备的操作可以由服务端或客户端发起。device
是通过设备发现获得的对方设备信息,后续会介绍广播的相关内容。
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
mManager.connect(mChannel, config, mActionListener);
取消连接
连接后也可以取消连接。注意:取消操作将取消所有正在发起连接邀请的设备,不能针对单个设备进行操作。
mManager.cancelConnect(mChannel, mActionListener);
解散群组
当需要取消某个单一连接时,只能从客户端取消对服务端的连接。如果需要取消所有连接,只能通过解散群组来实现。
mManager.removeGroup(mChannel, mActionListener);
获取群组相关信息
可以通过以下代码获取群组中的所有设备信息:
mManager.requestGroupInfo(mChannel, new WifiP2pManager.GroupInfoListener() {@Overridepublic void onGroupInfoAvailable(WifiP2pGroup group) {appendLog("已连接的设备:");Collection<WifiP2pDevice> devices = group.getClientList();int i=1;for(WifiP2pDevice d: devices) {appendLog((i++) +": ip:" +d.deviceAddress+", name:" +d.deviceName +", isGroupOwner:" +d.isGroupOwner() );}}
});
广播实现
使用广播来通知应用层自身设备、发现设备和连接设备的状态变化。
Intent | 说明 |
---|---|
WIFI_P2P_CONNECTION_CHANGED_ACTION | 当设备的 WLAN 连接状态更改时广播。 |
WIFI_P2P_PEERS_CHANGED_ACTION | 当您调用 discoverPeers() 时广播。如果您在应用中处理此 Intent,则通常需要调用 requestPeers() 以获取对等设备的更新列表。 |
WIFI_P2P_STATE_CHANGED_ACTION | 当 WLAN P2P 在设备上启用或停用时广播。 |
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION | 当设备的详细信息(例如设备名称)更改时广播。 |
WIFI_P2P_DISCOVERY_CHANGED_ACTION | 当设备开始或停止发现设备时广播 |
下面是一个实现广播接收器的示例:
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {public WiFiDirectBroadcastReceiver() {super();}@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {// 检查 Wi-Fi 是否启用并通知相应的活动int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, WifiP2pManager.WIFI_P2P_STATE_DISABLED);// wifi p2p 能否使用取决于你的 Wi-Fi 是否已打开。if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {// wifi p2p 可用} else if (state == WifiP2pManager.WIFI_P2P_STATE_DISABLED) {// wifi p2p 不可用}} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {// 调用 WifiP2pManager.requestPeers() 获取当前对等设备列表// 获取所有扫描到的设备。WifiP2pDeviceList mPeers = intent.getParcelableExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST);} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {// 处理新连接或断开连接的情况// wifi p2p 连接状态发生变化,在创建组成功时也会触发该广播。// 获取 Wi-Fi P2P 网络状态NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);// 关于该群组的连接信息。有三个属性:// 1. groupFormed:群组是否已形成,作为群主,创建群组后获得的该属性值为 true// 2. isGroupOwner:本设备是否为群主// 3. groupOwnerAddress:群主的 IP 地址WifiP2pInfo p2pInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);// 群组的相关信息,如网络名称、密码和所有已连接设备等。WifiP2pGroup p2pGroup = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);if (networkInfo != null && networkInfo.isConnected()) {// 表明已连接上,创建组成功也会进入该判断if (p2pInfo.groupFormed && p2pInfo.isGroupOwner) {// 群组已形成,且本设备为群主} else if (p2pInfo.groupFormed) {// 群组已形成,但本设备非群主// 获取群主 IPString groupOwnerAddress = p2pInfo.groupOwnerAddress.getHostAddress();// 建立 Socket 连接}}} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {// 处理本设备的 Wi-Fi 状态变化// 本设备信息发生变化WifiP2pDevice wifiP2pDevice = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);} else if (WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION.equals(action)) {// 处理发现设备操作状态变化// WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED:停止状态// WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED:开始状态int discoveryState = intent.getIntExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE,WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);}}
}
获取连接信息
上述代码片段展示了如何使用WifiP2pManager来获取连接信息。通过调用requestConnectionInfo()
方法,并传入一个ConnectionInfoListener
,我们可以在连接信息可用时得到回调。
- 如果连接信息为null或群主地址为空,直接返回。
- 如果群已建立,且设备是群主,可以通过
requestGroupInfo()
方法获取群组信息,其中包括WiFi热点密码。
对于客户端设备,可以根据需要执行相应的操作。
针对性WiFi P2P连接
除了普通的搜索和连接方式外,还可以使用WifiP2pManager.discoverServices()
方法进行针对性的WiFi P2P连接。这种方式会根据服务提供的字段和协议来判断是否符合连接要求。
服务创建
-
Bonjour
使用Bonjour服务时,可以通过创建txt记录来指定相关信息。
示例代码如下:
Map<String, String> txtRecord = new HashMap<String, String>(); txtRecord.put("txtvers", "1"); txtRecord.put("pdl", "application/postscript"); mServiceInfo = WifiP2pDnsSdServiceInfo.newInstance(instanceName,serviceType, txtRecord);
其中,
serviceType
参数指定了应用程序使用的协议和传输层。 -
UPnP
使用UPnP服务时,可以指定服务类型和相关信息。
示例代码如下:
List<String> services = new ArrayList<String>(); services.add("urn:schemas-upnp-org:service:AVTransport:1"); services.add("urn:schemas-upnp-org:service:ConnectionManager:1"); mServiceInfo = WifiP2pUpnpServiceInfo.newInstance("6859dede-8574-59ab-9332-123456789011","urn:schemas-upnp-org:device:MediaRenderer:1",services);
添加服务
创建完服务信息后,可以使用addLocalService()
方法将服务添加到本地。
示例代码如下:
mManager.addLocalService(mChannel, mServiceInfo, mActionListener);
创建群组
与之前的示例相同,可以使用相同的方法来创建WiFi P2P群组。
创建服务监听
针对Bonjour和UPnP服务,可以分别创建相应的监听器。当搜索到设备服务时,这些监听器会得到回调。
示例代码如下:
// Bonjour服务监听器
WifiP2pManager.DnsSdServiceResponseListener ptrListener = new WifiP2pManager.DnsSdServiceResponseListener() {@Overridepublic void onDnsSdServiceAvailable(String instanceName, String registrationType, WifiP2pDevice srcDevice) {}}WifiP2pManager.DnsSdTxtRecordListener txtListener = new WifiP2pManager.DnsSdTxtRecordListener() {@Overridepublic void onDnsSdTxtRecordAvailable(String fullDomainName, Map<String, String> txtRecordMap, WifiP2pDevice srcDevice) {}
}
mManager.setDnsSdResponseListeners(mChannel, ptrListener, txtListener);// UPnP服务监听器
private WifiP2pManager.UpnpServiceResponseListener upnpListener = new WifiP2pManager.UpnpServiceResponseListener() {@Overridepublic void onUpnpServiceAvailable(List<String> uniqueServiceNames, WifiP2pDevice srcDevice) {}
}
mManager.setUpnpServiceResponseListener(mChannel, upnpListener);
这些监听器会在搜索到设备服务时得到回调。根据搜索时传入的服务request,决定回调哪个监听器。
发现服务
通过创建相应的服务请求,可以发现Bonjour和UPnP服务。
示例代码如下:
// Bonjour类型的TXT服务,对应上面 txtListener
WifiP2pDnsSdServiceRequest txtRequest = WifiP2pDnsSdServiceRequest.newInstance(INSTANCE_NAME, SERVICE_TYPE);
// Bonjour类型的PTR服务,对应上面 ptrListener
WifiP2pDnsSdServiceRequest ptrRequest = WifiP2pDnsSdServiceRequest.newInstance(SERVICE_TYPE);
// UPnP服务,对应上面的upnpListener
WifiP2pUpnpServiceRequest upnpRequest = WifiP2pUpnpServiceRequest.newInstance(searchTarget);mManager.addServiceRequest(mChannel, txtRequest, mActionListener);
mManager.addServiceRequest(mChannel, ptrRequest, mActionListener);
mManager.addServiceRequest(mChannel, upnpRequest, mActionListener);
其余步骤与之前的示例相同。
以上是针对性WiFi P2P连接的示例代码和步骤。你可以根据自己的需求来选择合适的服务类型和设置相应的监听器来处理搜索到的设备服务。
参考
参考文档:https://developer.android.google.cn/guide/topics/connectivity/wifip2p?hl=zh-cn
相关文章:

Wi-Fi直连分享:Android设备间的高速连接
Wi-Fi直连分享:Android设备间的高速连接 引言 随着无线局域网(Wi-Fi)的普及和发展,使用Wi-Fi直连技术(P2P)在没有中间接入点的情况下实现设备间直接互联成为可能。通过Wi-Fi直连,具备相应硬件…...

LeetCode 面试题 05.06. 整数转换
文章目录 一、题目二、Java 题解 一、题目 整数转换。编写一个函数,确定需要改变几个位才能将整数A转成整数B。 示例1: 输入: A 29 (或者0b11101), B 15(或者0b01111) 输出: 2 示例2: 输入&a…...

增强for循环和一般for循环的对比使用
文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。个人B站主页热爱技术的小郑 ,视频内容主要是对应文章的视频讲解形式。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘…...

云原生之使用Docker部署RSS阅读器Huntly
云原生之使用Docker部署RSS阅读器Huntly 一、Huntly介绍1.1 Huntly简介1.2 Huntly功能2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Huntly镜像五、部署Huntly5.1 创建挂载目录5.2 创建Hun…...

Powershell 实现禁用密码复杂性,空密码
前提条件 开启wmi,配置网卡,参考 实现一键关闭密码策略和远程空密码登录 最近客户需要的一个无法理解的需求,需要远程登录不输入密码,安全性没有了还要实现,没办法客户是上帝,客户怎么开心怎么来都行,安全性问题告知不重视,实际环境不建议一下操作,只要联网你被黑的哦…...

【c语言】详解动态内存管理
目录 关于动态内存分配malloc和calloc函数介绍动态内存回收----freerealloc函数介绍常见的动态内存错误 关于动态内存分配 回想一下我们之前学过的内存开辟方式: int val 20;//在栈空间上开辟四个字节 char arr[10] {0};//在栈空间上开辟10个字节的连续空间在学…...

深度学习概念——端对端
目录 1、端对端是什么2、端对端有什么用3、例子4、引用 在读论文的过程中反复遇到端对端的概念,就需要理解深刻一些。在此将收集到的一些资料拿出来辅以自己的拙见,请大家多多批评指正! 1、端对端是什么 在计算机学科中有一种算法叫分治法&a…...

苹果触控笔有必要买吗?平价ipad电容笔推荐
其实,市面上的数码类产品很多,有的侧重于美观,有的侧重于功能,有的侧重于性能。与iPad平板电脑搭配使用的电容笔同样也如此。因此,在选购电容笔时,一定要了解有关电容笔的知识。在购买之前,一定…...

React的高阶函数
1.认识高阶函数 高阶组件 本身不是一个组件,而是一个函数函数的参数是一个组件,返回值也是一个组件 高阶组件的定义 import ThemeContext from "../context/theme_context"function withTheme(OriginComponent) {return (props) > {retur…...

Java8实战-总结34
Java8实战-总结34 重构、测试和调试使用 Lambda 重构面向对象的设计模式观察者模式责任链模式 重构、测试和调试 使用 Lambda 重构面向对象的设计模式 观察者模式 观察者模式是一种比较常见的方案,某些事件发生时(比如状态转变)࿰…...

uniapp项目实践总结(二十四)安卓平台 APP 打包教程
导语:当你的应用程序开发完成后,在上架安卓应用商店之前,需要进行打包操作,下面简单介绍一下打包方法。 目录 准备工作配置项目生成证书打包配置准备工作 在打包之前,请保证你的 uniapp 应用程序编译到安卓手机模拟器的 App 是可以正常运行的,APP 打包分为安卓和 ios 两…...

GeoServer地图服务器权限控制
目录 1下载相关软件 2部署软件 3配置鉴权环节 4Java工程 5测试鉴权 6测试鉴权结果分析 本文章应该会后面试验一个鉴权功能就会发布一系列测试过程(GeoServer有很多鉴权方式) 1Download - GeoServer 1下载相关软件 进入geoserver官网的下载页面 …...

Python+requests+unittest+excel实现接口自动化测试框架
一、框架结构: 工程目录 二、Case文件设计 三、基础包 base 3.1 封装get/post请求(runmethon.py) 1 import requests2 import json3 class RunMethod:4 def post_main(self,url,data,headerNone):5 res None6 if heade…...

25807-2020 间脲基苯胺盐酸盐 课堂随笔
声明 本文是学习GB-T 25807-2020 间脲基苯胺盐酸盐. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了间脲基苯胺盐酸盐的要求、采样、试验方法、检验规则以及标志、标签、包装、运输和 贮存。 本标准适用于间脲基苯胺盐酸盐产品…...

苹果手机通讯录联系人如何一键删除? 1个方法轻松解决!
手机通讯录里存了上百个之前公司客户的电话,结果苹果手机不支持一键清空联系人。有什么其他办法可以将这些联系人一次性全部删除吗? 随着时间的增长,手机通讯录中难免会积累许多不再联系的用户或者是已经失效的联系人。对于这些许久不联系的人…...

【Linux成长史】Linux编辑器-gcc/g++使用
🎬 博客主页:博主链接 🎥 本文由 M malloc 原创,首发于 CSDN🙉 🎄 学习专栏推荐:LeetCode刷题集 数据库专栏 初阶数据结构 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如…...

【CNN-FPGA开源项目解析】卷积层03--单格乘加运算单元PE 单窗口卷积块CU 模块
03–单格乘加运算单元PE & 单窗口卷积块CU 文章目录 03--单格乘加运算单元PE & 单窗口卷积块CU前言单格乘加运算单元PE代码模块结构时序逻辑分析对其上层模块CU的要求 单窗口卷积块CU代码逻辑分析 前言 第一和第二篇日志已经详细阐述了"半精度浮点数"的加…...

一文教你学会ArcGIS Pro地图设计与制图系列全流程(2)
ArcGIS Pro做的成果图及系列文章目录: 系列文章全集: 《一文教你学会ArcGIS Pro地图设计与制图系列全流程(1)》《一文教你学会ArcGIS Pro地图设计与制图系列全流程(2)》《一文教你学会ArcGIS Pro地图设计与…...

ICML 2017: 基于卷积的Seq2Seq解决方案
一.文章概述 通常而言,Seq2Seq解决方案一般都采用循环神经网络,但在本文,作者提出了基于卷积神经网络的解决方案ConvS2S。基于卷积神经网络的方案有两大优势:计算并行化更高,优化更容易(非线性的数量是固定…...

探索GmSSL+Nginx实践及原理
前言 随着大国崛起步伐的迈进,敏感单位的数据安全问题越发受到重视,数据的加密安全传输尤为重要,对于安全问题,国家自研加密算法提供了有力的保障。 作为信创行业的国有企业,十分有必要在网络通信中使用国密算法加密…...

Mybatis框架学习
什么是mybatis? mybatis是一款用于持久层的、轻量级的半自动化ORM框架,封装了所有jdbc操作以及设置查询参数和获取结果集的操作,支持自定义sql、存储过程和高级映射 mybatis用来干什么? 用于处理java和数据库的交互 使用mybat…...

基于微信小程序的电影院订票系统设计与实现(源码+lw+部署文档+讲解等)
文章目录 前言运行环境说明用户微信小程序端的主要功能有:管理员的主要功能有:具体实现截图详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding)有保障的售后福利 代码参考论文参考源码获取 前言 💗博主介绍&…...

LeetCode-热题100-笔记-day32
二分查找 今日刷到二分查找,以前做过的题忘的一干二净;庆幸自己用新的方法做了出来两道“中等”题;(我都能做出来我认为应该标“简单”)由于之前题的难度基本在抄答案,所以停更几天。今天没抄答案就更新一…...

STP生成树协议基本配置示例---STP逻辑树产生和修改
STP是用来避免数据链路层出现逻辑环路的协议,运行STP协议的设备通过交互信息发现环路,并通过阻塞特定端口,最终将网络结构修剪成无环路的树形结构。在网络出现故障的时候,STP能快速发现链路故障,并尽快找出另外一条路径…...

Java版企业电子招标采购系统源码—企业战略布局下的采购寻源
项目说明 随着公司的快速发展,企业人员和经营规模不断壮大,公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境,最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范,以及审…...

华纳云:SQLserver配置远程连接的方法是什么
在 SQL Server 中配置远程连接涉及一些步骤,包括启用远程连接、配置网络协议、设置防火墙规则和授权远程访问。以下是一些配置远程连接的主要步骤: 启用远程连接: 打开 SQL Server Management Studio(SSMS)并连接到你的…...

CryptoCTF easy
文章目录 2023suctionBlue Office 2022Baphomet 2023 suction 题目描述: from Crypto.Util.number import * from flag import flagdef keygen(nbit, r):while True:p, q [getPrime(nbit) for _ in __]e, n getPrime(16), p * qphi (p - 1) * (q - 1)if GCD(e…...

计算机视觉技术的革新:医疗领域的应用
近年来,计算机视觉技术在医疗领域引起了广泛的关注和应用。通过将计算机视觉与医疗技术相结合,我们可以实现更准确、高效的医疗诊断和治疗,为患者提供更好的医疗服务。 首先,计算机视觉在医疗图像分析中的应用是其中的一个重要方…...

python找出文件夹里文件名以特定字符结尾的文件并移动到另一个目录
python找出文件夹里文件名以特定字符结尾的文件并移动到另一个目录 代码 import os import shutil# 源文件夹路径 source_folder "./data/deep/test"# 目标文件夹路径 target_folder "./data/deep/A"# 确保目标文件夹存在,如果不存在则创建…...

面试官问:大量的 TIME_WAIT 状态 TCP 连接,对业务有什么影响?怎么处理?
几个方面: 问题描述:什么现象?什么影响? 问题分析 解决方案 底层原理 1.问题描述 模拟高并发的场景,会出现批量的 TIME_WAIT 的 TCP 连接: 短时间后,所有的 TIME_WAIT 全都消失࿰…...