日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

WIFI P2P原理深入解析

發(fā)布時(shí)間:2024/3/12 编程问答 75 豆豆
生活随笔 收集整理的這篇文章主要介紹了 WIFI P2P原理深入解析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

前言

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 的代碼如下:

public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {private WifiP2pManager manager; private Channel channel; private MyWiFiActivity activity;public WiFiDirectBroadcastReceiver (WifiP2pManager manager, Channel channel,MyWifiActivity activity) { super(); this.manager=manager; this.channel=channel; this.activity=activity; } @Override public void onReceive(Context context, Intent intent) { String action=intent.getAction();if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTI0N.equals (action)) { //檢測 WIFI 功能是否被打開 } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals (action)) { //獲取當(dāng)前可用連接點(diǎn)的列表 } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals (action)) { //建立或者斷開連接 } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals (action)) { //當(dāng)前設(shè)備的 WIFI 狀態(tài)發(fā)生變化 } } }

2. 初始化操作

1)修改?AndroidManifest.xml 文件

指定支持 WIFI Direct 的 Android SDK 的最小版本并增加使用 WIFI Direct 的相應(yīng)權(quán)限,代碼如下:

<uses-sdk android:minSdkVersion="14" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

2)確認(rèn)當(dāng)前設(shè)備是否支持并且打開了 WIFI Direct 功能

相關(guān)代碼應(yīng)該被放在 BroadcastReceiver 的 onReceive() 方法中。實(shí)例代碼如下:

public void onReceive (Context context, Intent intent) { ... String action=intent.getAction();if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTI0N.equals (action)) { int state=intent.getIntExtra (WifiP2pManager.EXTRA_WIFI_STATE, -1); if (state==WifiP2pManager.WIFI_P2P_STATE_ENABLED) { // Wifi Direct is enabled } else { // Wi-Fi Direct is not enabled } } ... }

3)在 Activity 的 onCreate() 方法中創(chuàng)建對象

創(chuàng)建 WifiP2pManager 和 Channel 對象,并創(chuàng)建 BroadcastReceiver 對象,代碼如下:

WifiP2pManager mManager; Channel mChannel; BroadcastReceiver mReceiver; ...@Override protected void onCreate (Bundle savedInstanceState) { ... mManager= (WifiP2pManager) getSystemService (Context.WIFI_P2P_SERVICE); mChannel=mManager.initialize (this, getMainLooper(), null); mReceiver=new WiFiDirectBroadcastReceiver (manager, channel, this); ... }

4)創(chuàng)建 BroadcastReceiver 要使用的 IntentFilter 對象

代碼如下:

IntentFilter mIntentFilter; ... @Override protected void onCreate (Bundle savedInstanceState) { ... mIntentFilter=new IntentFilter(); mIntentFilter.addAction (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTI0N); mIntentFilter.addAction (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTI0N); mIntentFilter.addAction (WifiP2pManager.WIFI_P2P_C0NNECTI0N_CHANGED_ACTI0N); mIntentFilter.addAction (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTI0N); ... }

5)在 Activity 的 onResume() 方法中注冊 BroadcastReceiver 對象,在 onPause() 方法中注銷對象

代碼如下:

@Override protected void onResume(){ super.onResume(); registerReceiver(mReceiver, mIntentFilter); }/*unregister the broadcast receiver */ @Override protected void onPause(){ super.onPause(); unregisterReceiver(mReceiver); }

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ù)器代碼如下:

public static class FileServerAsyncTask extends AsyncTask { private Context context; private TextView statusText;public FileServerAsyncTask(Context context, View statusText) { this.context = context; this.statusText = (TextView) statusText; }@Override protected String doInBackground(Void... params) { try { //創(chuàng)建 ServerSocket 對象,監(jiān)聽 8888 端口,等待客戶連接 ServerSocket serverSocket = new ServerSocket(8888); Socket client = serverSocket.accept(); //建立連接成功,開始傳送數(shù)據(jù) final File f = new File(Environment.getExternalStorageDirectory() + "/" + context.getPackageName() + "/wifip2pshared-" + System.currentTimeMillis() + ".jpg"); File dirs = new File(f.getParent()); if (!dirs.exists()) dirs.mkdirs(); f.createNewFile(); InputStream inputstream = client.getlnputStream(); copyFile(inputstream, new FileOutputStream(f)); ServerSocket.close(); return f.getAbsolutePath(); } catch (IOException e) { Log.e(WiFiDirectActivity.TAG, e.getMessage()); return null; } }//啟動(dòng)用于顯示圖像的Activity @Override protected void onPostExecute(String result) { if (result != null) { statusText.setText("File copied - " + result); Intent intent = new Intent(); intent.setAction(android.content.Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse("file://" + result),"image/*"); context.startActivity(intent); } } }

客戶端的相關(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。