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连接。这种方式会根据服务提供的字段和协议来判断是否符合连接要求。

服务创建

  1. 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参数指定了应用程序使用的协议和传输层。

  2. 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/146437.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【React】React组件生命周期以及触发顺序(部分与vue做比较)

最近在学习React&#xff0c;发现其中的生命周期跟Vue有一些共同点&#xff0c;但也有比较明显的区别&#xff0c;并且执行顺序也值得讨论一下&#xff0c;于是总结了一些资料在这里&#xff0c;作为学习记录。 v17.0.1后生命周期图片 初始化阶段 由ReactDOM.render()触发 —…

【Java 进阶篇】JDBC插入数据详解

在Java应用程序中&#xff0c;与数据库交互是一项常见的任务。其中&#xff0c;插入数据操作是一种基本的数据库操作之一。本文将详细介绍如何使用Java JDBC&#xff08;Java Database Connectivity&#xff09;来执行插入数据操作。无论您是初学者还是有一定经验的开发人员&am…

CSS详细基础(六)边框样式

本期是CSS基础的最后一篇~ 目录 一.border属性 二.边框属性复合写法 三.CSS修改表格标签 四.内边距属性 五.外边距属性 六.其他杂例 1.盒子元素水平居中 2.清除网页内外元素边距 3.外边距的合并与塌陷 4.padding不会撑大盒子的情况 七.综合案例——新浪导航栏仿真 …

Android studio升级Giraffe | 2022.3.1 Patch 1踩坑

这里写自定义目录标题 not "opens java.io" to unnamed module错误报错信息解决 superclass access check failed: class butterknife.compiler.ButterKnifeProcessor$RScanner报错报错信息解决 Android studio升级Giraffe | 2022.3.1 Patch 1后&#xff0c;出现项目…

imgui开发笔记<1>、ubuntu环境下快速应用

去这个链接下载imgui源码&#xff08;在此之前需要安装opengl glfw3等等&#xff09;&#xff1a; sudo apt-get install libglfw3-dev https://github.com/ocornut/imgui 我这里源码下载到/home/temp/imgui目录下&#xff0c;咱们不需要编译源码成库&#xff0c;而是直接将下…

【Axure高保真原型】3D圆柱图_中继器版

今天和大家分享3D圆柱图_中继器版的原型模板&#xff0c;图表在中继器表格里填写具体的数据&#xff0c;调整坐标系后&#xff0c;就可以根据表格数据自动生成对应高度的圆柱图&#xff0c;鼠标移入时&#xff0c;可以查看对应圆柱体的数据……具体效果可以打开下方原型地址体验…

网络安全渗透测试工具之skipfish

网络安全渗透测试工具skipfish介绍 在数字化的时代,Web 应用程序安全成为了首要任务。想象一下,您是一位勇敢的安全冒险家,迎接着那些隐藏在 Web 应用程序中的未知风险。而在这个冒险之旅中,您需要一款强大的工具来帮助您发现漏洞,揭示弱点。而这个工具就是 Skipfish。 …

LeetCode 周赛上分之旅 #48 一道简单的树上动态规划问题

⭐️ 本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 和 BaguTree Pro 知识星球提问。 学习数据结构与算法的关键在于掌握问题背后的算法思维框架&#xff0c;你的思考越抽象&#xff0c;它能覆盖的问题域就越广&#xff0c;理解难度…

点击、拖拉拽,BI系统让业务掌握数据分析主动权

在今天的商业环境中&#xff0c;数据分析已经成为企业获取竞争优势的关键因素之一。然而&#xff0c;许多企业在面对复杂的数据分析工具时&#xff0c;却常常感到困扰。这些工具往往需要专业的技术人员操作&#xff0c;而且界面复杂&#xff0c;难以理解和使用。对业务人员来说…

阿里云 Oss 权限控制

前言 最近公司的私有 Oss 服务满了&#xff0c;且 Oss 地址需要设置权限&#xff0c;只有当前系统的登录用户才能访问 Oss 下载地址。一开始想着用 Nginx 做个转发来着&#xff0c;Nginx 每当检测当前请求包含特定的 Oss 地址就转发到我们的统一鉴权接口上去&#xff0c;但是紧…

picoctf_2018_shellcode

picoctf_2018_shellcode Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments32位&#xff0c;啥都没开 这个看着挺大的&#xff0c;直接来个ROPchain&#xff0c;…

Redis缓存穿透、击穿和雪崩

面试高频 服务的高可用问题&#xff01; 在这里我们不会详细的区分析解决方案的底层&#xff01; Redis缓存概念 Redis缓存的使用&#xff0c;极大的提升了应用程序的性能和效率&#xff0c;特别是数据查询方面。但同时&#xff0c;它也带来了一些问题。其中&#xff0c;最要…

Android stdio的Gradle菜单栏无内容问题的解决方法

右边Gradle菜单栏里没有Tasks选项内容的问题 正常情况↓ 如果这个问题如果无法解决的话&#xff0c;Gradle打包就只能通过控制台输入命令来解决&#xff0c;但这无疑是把简单问题复杂化了&#xff0c;我们来看看怎么解决这个问题吧。 这里有几个方法提供&#xff0c;可以自行选…

排序篇(四)----归并排序

排序篇(四)----归并排序 1.归并(递归) 基本思想&#xff1a; 归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法&#xff08;Divide andConquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到…

leetCode 376.摆动序列 动态规划 + 图解 + 状态转移

376. 摆动序列 - 力扣&#xff08;LeetCode&#xff09; 如果连续数字之间的差严格地在正数和负数之间交替&#xff0c;则数字序列称为 摆动序列 。第一个差&#xff08;如果存在的话&#xff09;可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。 例如…

微信小程序引入字体在部分机型失效不兼容解决办法

写小程序页面&#xff0c;美工作图用了特殊字体 引入代码&#xff1a; font-face {font-family: huxiaobo;src: url("https://xxxxxxxx.top/assets/fonts/huxiaobonanshenti.woff") } .font-loaded {font-family: "huxiaobo"; } 上线后发现部分安卓机型不…

209. 长度最小的子数组(滑动窗口)

一、题目 209. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 二、代码 class Solution { public:int minSubArrayLen(int target, vector<int>& nums) {int left 0, right 0;int sum nums[right];int MinLength INT_MAX;while (left <nums.siz…

计算机竞赛 深度学习火车票识别系统

文章目录 0 前言1 课题意义课题难点&#xff1a; 2 实现方法2.1 图像预处理2.2 字符分割2.3 字符识别部分实现代码 3 实现效果4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 图像识别 火车票识别系统 该项目较为新颖&#xff0c;适…

54、数组--模拟

LCR 146. 螺旋遍历二维数组 给定一个二维数组 array&#xff0c;请返回「螺旋遍历」该数组的结果。 螺旋遍历&#xff1a;从左上角开始&#xff0c;按照 向右、向下、向左、向上 的顺序 依次 提取元素&#xff0c;然后再进入内部一层重复相同的步骤&#xff0c;直到提取完所有…