走进Android P2P技术 一探设施间点对点通讯成功细节 WiFi
WiFi P2P技术
WiFi P2P(Peer-to-Peer),也被称为WiFi Direct,是WiFi联盟颁布的一个协定。准许无线网络中的设施在无需无线路由器的状况下相互衔接,经过WiFi间接成功两台设施之间的无线点对点通讯。原理与基于AP(接入点)的通讯形式相似,支持P2P的设施可以在同一个小组内互传数据,成功同屏性能。
WiFi P2P被宽泛运行于移动设施之间的文件共享、游戏联机、音乐播放等运行场景中。相较于蓝牙,WiFi P2P具备更快的搜查速度和传输速度,以及更远的传输距离。而且只有要关上WiFi即可,不须要参与任何网络或AP,即可成功平等点衔接通讯。关于须要在用户之间共享数据的运行,如多人游戏或照片共享十分有用。
WiFi P2P也存在一些安保性疑问,如用户隐衷暴露、恶意软件和病毒流传,以及侵权和违法内容的流传。为了包全用户的安保和隐衷,一些P2P网络提供了匿名化处感性能,经常使用安保搜查引擎,以及设置过滤器来阻止违法和侵权内容的共享。
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系统中只是内置了设施的搜查和链接性能,并没有像蓝牙那样有许多运行。在实践开发中,或者须要经过软件手腕处置一些逻辑和权限疑问。
Android运行WiFi P2P成功数据传输
在Android中,WiFi P2P可以经过WifiP2pManager类启动成功。开发者可以经差错掉WifiP2pManager实例,并启动广播接受者的创立和注册,调用其余WiFi P2P的API,成功设施间的搜查、衔接和数据传输等性能。例如,指定某一台设施为主机,创立群组并期待客户端的衔接恳求,而客户端则可以被动搜查左近的设施并参与群组,向主机动员文件传输恳求。
减少权限
失掉WifiP2pManager和WifiP2pManager.Channel对象
mWifiP2pManager = getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManagermWifiP2pManager?.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() }}}
衔接设施
val config = WifiP2pConfig().apply {this.deviceAddress = wifiP2pDevice.deviceAddressthis.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")}})
服务端创立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")}
客户端创立Socket启动数据读写
//须要创立子线程 否则在主线程网络操作间接闪退val address: InetAddress = info.groupOwnerAddressval 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.simpleNameprivate lateinit var mBinding: ActivityMainBindingprivate var mWifiP2pManager: WifiP2pManager? = nullprivate var mChannel: WifiP2pManager.Channel? = nullprivate var mDeviceList = arrayListOf<WifiP2pDevice>()private lateinit var mDeviceAdapter: DeviceAdapterprivate 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 = mDeviceAdaptermDeviceAdapter.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 WifiP2pManagermChannel = 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.deviceAddressthis.wps.setup = WpsInfo.PBC}mWifiP2pManager?.connect(mChannel, config, object : WifiP2pManager.ActionListener {override fun onSuccess() {Log.d(TAG, "衔接成功")mQuitReadData = falsetransferData("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.groupOwnerAddressval 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的关系方法来断开衔接,并监禁关系资源。