WIFI P2P原理深入解析
目錄
前言
1.原理及架構(gòu)
2.實(shí)例及應(yīng)用
3.常見的問題
前言
? ? ? ? 對Wi-Fi Direct即wifi直連,在物聯(lián),中短距離的傳輸穩(wěn)定性明顯比藍(lán)牙具有優(yōu)勢,本文主要介紹兩個(gè)方面:一是原理及架構(gòu),二是實(shí)例及應(yīng)用;
1.原理及架構(gòu)
P2P架構(gòu)中定義了三個(gè)組件,一個(gè)設(shè)備,兩種角色。這三個(gè)組件分別是:
- P2P Device:它是P2P架構(gòu)中角色的實(shí)體,讀者可把它當(dāng)做一個(gè)Wi-Fi設(shè)備。
- P2P Group Owner(GO):P2P網(wǎng)絡(luò)建立時(shí)會(huì)產(chǎn)生一個(gè)Group。
- P2P Group Client(GC):
- 在組建P2P Group(即P2P Network)之前,智能終端都是一個(gè)一個(gè)的P2P Device。
- 當(dāng)這些P2P Device設(shè)備之間完成P2P協(xié)商后,那么其中將有一個(gè)并且只能有一個(gè)Device來扮演GO的角色,而其他Device來扮演GC的角色。
最終構(gòu)成的這個(gè)P2P Group組織結(jié)構(gòu)如圖所示:
?P2P Group示意圖
如圖展示了一個(gè)典型P2P Group的構(gòu)成,其中:
一個(gè)P2P Group中只能有一個(gè)GO。一個(gè)GO可以支持1個(gè)或多個(gè)(即圖中的1:n)GC連接。
- 由于GO的功能類似于AP,所以周圍那些不支持P2P功能的WIFI STA也能發(fā)現(xiàn)并關(guān)聯(lián)到GO。這些WIFI STA被稱之為Legacy Clients。
注意:“不支持P2P功能”更準(zhǔn)確的定義是指不能處理P2P協(xié)議。在P2P網(wǎng)絡(luò)中,GO等同于AP,所以Legacy Clients也能搜索到GO并關(guān)聯(lián)上它。不過,由于Legacy Clients不能處理P2P協(xié)議,所以P2P一些特有功能在這些Legacy Clients中無法實(shí)現(xiàn)。
Wifi_Direct的大致配對流程如下:
? ? ? ? a. WifiP2pManager.discoverPeers()開始掃描設(shè)備
? ? ? ? b. 獲取掃描到的設(shè)備,選擇其中一個(gè)設(shè)備進(jìn)行連接配對WifiP2pManager.connect
? ? ? ? c. 配對成功后,根據(jù)WifiP2pInfo.isGroupOwner和WifiP2pInfo.groupOwnerAddress進(jìn)行連接。
? ? ? ? 雙方時(shí)序圖如下:
2.實(shí)例及應(yīng)用
創(chuàng)建一個(gè) WIFI Direct 應(yīng)用程序,包括發(fā)現(xiàn)連接點(diǎn)、請求連接、建立連接、發(fā)送數(shù)據(jù),以及建立對該應(yīng)用程序廣播的 Intent 進(jìn)行接收的 BroadcastReceiver,需要經(jīng)過以下步驟。
1. 創(chuàng)建 BroadcastReceiver
需要注意的是,要在 BroadcastReceiver 的構(gòu)造方法中傳入 WifiP2pManager、WifiP2pManager.Channel 以及注冊該 BroadcastReceiver 的 Activity 的對象,以便在 BroadcastReceiver 中訪問 WIFI 硬件設(shè)備并對 Activity 進(jìn)行更新。
創(chuàng)建 BroadcastReceiver 的代碼如下:
2. 初始化操作
1)修改?AndroidManifest.xml 文件
指定支持 WIFI Direct 的 Android SDK 的最小版本并增加使用 WIFI Direct 的相應(yīng)權(quán)限,代碼如下:
2)確認(rèn)當(dāng)前設(shè)備是否支持并且打開了 WIFI Direct 功能
相關(guān)代碼應(yīng)該被放在 BroadcastReceiver 的 onReceive() 方法中。實(shí)例代碼如下:
3)在 Activity 的 onCreate() 方法中創(chuàng)建對象
創(chuàng)建 WifiP2pManager 和 Channel 對象,并創(chuàng)建 BroadcastReceiver 對象,代碼如下:
4)創(chuàng)建 BroadcastReceiver 要使用的 IntentFilter 對象
代碼如下:
5)在 Activity 的 onResume() 方法中注冊 BroadcastReceiver 對象,在 onPause() 方法中注銷對象
代碼如下:
3. 使用 WifiP2pManager.discoverPeers() 方法獲取可以連接點(diǎn)的列表
示例代碼如下:
manager.discoverPeers (channel, new WifiP2pManager.ActionListener() { @Override public void onSuccess(){ ... }@Override public void onFailure (int reasonCode) { ... } });若成功搜尋到可以連接的點(diǎn),則 WIFI Direct 系統(tǒng)框架會(huì)廣播一個(gè)帶有 WIFI_P2P_ PEERS_CHANGED_ACTION 信息的 Intent,該 Intent 會(huì)被之前定義的 BoradcastReceiver 接收,并獲得可以連接點(diǎn)的列表。示例代碼如下:
PeerListListener myPeerListListener; ... if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals (action)) { if (manager !=null) { manager.requestPeers (channel, myPeerListListener); } }4. WifiP2pManager.connect() 方法可以與列表中的某個(gè)連接點(diǎn)設(shè)備建立連接,該方法通過 WifiP2pConfig 對象獲得連接設(shè)備的相關(guān)信息
示例代碼如下:
WifiP2pDevice device; WifiP2pConfig config=new WifiP2pConfig(); config.deviceAddress=device.deviceAddress; manager.connect (channel, config, new ActionListener () {@Override public void onSuccess(){ //success logic }@Override public void onFailure (int reason) { //failure logic } });5. 連接建立后,就可以用兩個(gè)設(shè)備直接通過 Socket 進(jìn)行數(shù)據(jù)傳輸
其傳輸過程與之前講解的 Socket 通信完全相同,基本步驟如下:
1)在其中一個(gè)設(shè)備上建立 ServerSocket 對象,監(jiān)聽特定端口,并堵塞應(yīng)用程序,直到有連接請求。
2)在另一個(gè)設(shè)備上建立 Socket 對象,通過 IP 地址和端口向 ServerSocket 發(fā)出連接請求。
3)ServerSocket 監(jiān)聽到連接請求后,調(diào)用 accept() 方法建立連接。
4)連接建立后,Socket 對象可以通過字節(jié)流在兩個(gè)設(shè)備間直接進(jìn)行數(shù)據(jù)傳遞。
下面的示例代碼演示了通過 ServerSocket 和 Socket 在客戶端和服務(wù)器間直接傳遞 JPG 圖像的過程。
服務(wù)器代碼如下:
客戶端的相關(guān)代碼如下:
Context context=this.getApplicationContext(); String host; int port; int len; Socket socket=new Socket(); byte buf[]=new byte[1024]; ... try{ //創(chuàng)建Socket對象,并請求連接 socket.bind (null); socket.connect((new InetSocketAddress(host,port)),500);//連接建立成功,開始傳輸數(shù)據(jù) OutputStream outputStream=socket.getOutputStream(); ContentResolver cr=context.getContentResolver(); Inputstream inputStream=null; inputstream=cr.openlnputStream(Uri.parse("path/to/picture.jpg"))while((len=inputStream.read(buf))!=-l){outputStream.write(buf,0,len); outputStream.close(); inputstream.close(); }catch(FileNotFoundException e){ //catch logic } catch (IOException e) { //catch logic } //關(guān)閉連接 finally{ if(socket!=null){ if(socket.isConnected()){ try{ socket.close(); }catch(IOException e){ //catch logic } } } }3.常見的問題
問題1:WifiP2pManger.connect()時(shí),如何確定誰是GO,誰是GC
答:調(diào)用WifiP2pManger.connect()進(jìn)行連接時(shí),GO還算GC的身份是隨機(jī)的。開發(fā)者無法決定GroupOwner是哪臺(tái)設(shè)備,但是可以通過WifiP2pConfig.groupOwnerIntent參數(shù)進(jìn)行建議。
問題2:如果一定要確定誰是GO,誰是GC,怎么辦
答:第一步:GO端先調(diào)用WifiP2pManger.createGroup
第二步:GO端或者GC端調(diào)用WifiP2pManger.connect
即:先建立Group,再連接
問題3:如何斷開連接
答:WifiP2pManger.removeGroup
注意:WifiP2pManger.removeGroup是移除Group,斷開連接。WifiP2pManger.cancelConnect()斷開一個(gè)connecting的連接,即斷開當(dāng)前狀態(tài)是Invited的連接。
問題4:?我們已知配對成功的前提條件是:進(jìn)行配對的兩臺(tái)設(shè)備都必須能夠掃描到對方。那么如何保證本機(jī)一直處于搜索狀態(tài)呢?
答:經(jīng)過測試得知,一般情況下,本機(jī)Scan一次,能夠保持在線狀態(tài)3分鐘,即能夠搜索到其他設(shè)備/被其他設(shè)備搜索到的時(shí)間一般是3分鐘。但是這個(gè)3分鐘不是非常準(zhǔn)確的,這跟手機(jī)性能或者WIFI芯片都有很大關(guān)系。因此我們能做的方案就是如果搜索結(jié)束,就重啟一次搜索。
對于一般的Peer Discovery而言,如果搜索結(jié)束,會(huì)收到廣播WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION,這樣再收到廣播后重新搜索就可以。
對于Service Discovery而言(這其實(shí)是使用最廣泛的),搜索結(jié)束后,系統(tǒng)不會(huì)發(fā)出廣播通知,這樣就給開發(fā)者帶來一個(gè)難題:你無法知曉當(dāng)前是否處于搜索(可見)狀態(tài)。
目前比較可行的做法是:每隔3分鐘(或者更短)重啟一次搜索,這樣基本保證本機(jī)一直處于搜索狀態(tài)。但是這僅能覆蓋大多數(shù)的情況,建議再此基礎(chǔ)上再加入手動(dòng)搜索(搜不到可以讓用戶手動(dòng)搜索)保證當(dāng)前的可見狀態(tài)。
目前wifip2p依然不是很穩(wěn)定,從測試的結(jié)果來說,Wifi_Direct的表現(xiàn)受具體設(shè)備的影響很大,配對的速度也有較大差異,從10秒到2分鐘甚至更久。有可能出現(xiàn)
a.A機(jī)器處于搜索(可見)狀態(tài),但是B機(jī)器依然搜索不到;
b.還有可能出現(xiàn)A機(jī)器處于搜索(可見)狀態(tài),B機(jī)器也搜索到了,但是連接失敗(此情況的主要原因還是因?yàn)锳機(jī)器搜索結(jié)束后系統(tǒng)不發(fā)通知,而B機(jī)器當(dāng)前搜索到的A機(jī)器是之前A機(jī)器的狀態(tài))
問題5:如何把一個(gè)文件非常方便的發(fā)送給多個(gè)設(shè)備
答:
方法1:在一次一對一文件傳輸完畢后,直接斷開連接進(jìn)行搜索/連接/傳輸,這樣就可以實(shí)現(xiàn)相同文件(夾)的多目標(biāo)設(shè)備的發(fā)送。
方法2:使用問題2的答案,發(fā)送方做GO,接收方全部做GC,即可。
問題6:
我們知道
1.在進(jìn)行connect的時(shí)候,連接兩端是GO還是GC是隨機(jī)的。
2.GC可以知道GO的地址,而GO是不知道GC的地址的.
3.一般的Socket編程思路是,GO做Server端,GC做Client端。
那么問題來了,如何能夠?qū)崿F(xiàn)PeerA連接PeerB后,PeerA直接發(fā)送數(shù)據(jù)給PeerB呢?
答:方法一:使用問題2中的答案,先確定身份(誰是GO,誰是GC),再發(fā)送數(shù)據(jù)
方法二:步驟(假設(shè)PeerA是數(shù)據(jù)發(fā)送端,PeerB是數(shù)據(jù)接收端)
1.connect成功后,GO做server,GC做client
2.socket連接成功后,PeerA執(zhí)行發(fā)送線程,PeerB執(zhí)行接收線程
總結(jié)
以上是生活随笔為你收集整理的WIFI P2P原理深入解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022年必读的10本好书
- 下一篇: buck dcm占空比计算_如何计算BO