Android WiFi直連 雙向通信

原文地址https://blog.csdn.net/VNanyesheshou/article/details/82316436
本文主要說一下,Android通過WiFi直連的方式實現圖片雙向傳輸(圖片可以傳輸,也可以把它修改下傳輸聊天信息了)。

1 WiFi直連概述

WiFi直連也就是WiFi設備點對點連接(WiFi P2P),它允許具有適當硬件的Android 4.0(API級別14)或更高版本的設備通過Wi-Fi直接相互連接,而無需中間接入點。使用這些API,您可以發現並連接到其他設備(前提是每個設備支持Wi-Fi P2P),然後通過比藍牙連接更長的距離快速連接進行通信。這對於在用戶之間共享數據的應用程序很有用,例如多人遊戲或照片共享應用程序。

Wi-Fi P2P API包含以下主要部分:
允許您發現,請求和連接到對等的方法在WifiP2pManager類中定義。
允許您通知WifiP2pManager方法調用成功或失敗的監聽器。調用WifiP2pManager方法時,每個方法都可以接收作爲參數傳入的特定偵聽器。
通知您Wi-Fi P2P框架檢測到的特定事件的意圖,例如斷開的連接或新發現的對等體。
您經常將API的這三個主要組件一起使用。例如,您可以提供WifiP2pManager.ActionListener呼叫discoverPeers(),以便您可以使用ActionListener.onSuccess()和ActionListener.onFailure() 方法通知您。


2 Demo

demo
注意事項:

2.1 搜索不到可用設備

  1. 確保對端設備處於搜索狀態;
  2. 對端設備異常,可嘗試重啓WLAN開關,重新搜索。
  3. 自身設備異常,可嘗試重啓WLAN開關,重新搜索。

2.2 連接失敗

  1. 確保對端設備沒有與其他設備建立WLAN直連連接。
  2. 確保對端設備接收連接請求。
  3. 對端設備異常,請嘗試重新連接。

3 創建Wi-Fi P2P應用程序

創建Wi-Fi P2P應用程序涉及爲您的應用程序創建和註冊廣播接收器,發現對等體,連接到對等體以及將數據傳輸到對等體。以下部分描述瞭如何執行此操作。

3.1 初始設置

1 添加權限

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

<uses-feature android:name="android.hardware.wifi.direct" android:required="true"/>

build.gradle並設置最小版本爲14或以上。 minSdkVersion 14

2 初始化

獲取Wi-Fi P2P框架的實例並註冊您的應用程序。

//獲取Wifi P2P服務對象
mWifiP2pManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
// 註冊應用程序 這一步必須的。
mChannel = mWifiP2pManager.initialize(this, getMainLooper(), null);

3 註冊監聽

IntentFilter intentFilter = new IntentFilter();
//監聽 Wi-Fi P2P是否開啓
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
//監聽 Wi-Fi P2P掃描狀態
intentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
//監聽 可用的P2P列表發生了改變。
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
//監聽 Wi-Fi P2P的連接狀態發生了改變
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
//監聽 設備的詳細配置發生了變化
intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);

mReceiver = new WiFiDirectBroadcastReceiver(mWifiP2pManager, mChannel, this);
//對上述的action 進行註冊監聽
registerReceiver(mReceiver, intentFilter);

3.2 發現設備

調用discoverPeers()以檢測範圍內的可用對等方,此函數的調用是異步的。如果您創建了一個WifiP2pManager.ActionListener,則會將成功或失敗傳遞給您的應用程序onSuccess(),onFailure()。該 onSuccess()方法僅通知您發現過程成功,並且未提供有關其發現的實際對等方的任何信息

mWifiP2pManager.discoverPeers(mChannel, new ActionListener() {
    //啓動成功,實際上還沒有發現任何服務,因此這種方法通常可以留空。
    @Override
    public void onSuccess() {
        Toast.makeText(MainActivity.this, "Discovery Initiated",
                Toast.LENGTH_SHORT).show();
    }

    //啓動失敗
    @Override
    public void onFailure(int reasonCode) {
        Toast.makeText(MainActivity.this, "Discovery Failed : " + reasonCode,
                Toast.LENGTH_SHORT).show();
    }
});

如果發現過程成功並檢測到對等體,則系統會發送廣播WIFI_P2P_PEERS_CHANGED_ACTION,您可以在廣播接收器中監聽該意圖以獲得對等體列表。當您的應用程序收到WIFI_P2P_PEERS_CHANGED_ACTION意圖時,您可以請求已發現的對等方的列表requestPeers()。以下代碼顯示瞭如何設置它:

PeerListListener peerListListener;

if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {

    // request available peers from the wifi p2p manager. This is an
    // asynchronous call and the calling activity is notified with a
    // callback on PeerListListener.onPeersAvailable()
    if (mManager != null) {
        mManager.requestPeers(mChannel, peerListListener);
    }
}

該requestPeers()方法也是異步的,並且可以在對等列表可用時通知您的活動,該列表onPeersAvailable()在WifiP2pManager.PeerListListener接口中定義。該onPeersAvailable()方法爲您提供了一個WifiP2pDeviceList,您可以迭代以查找要連接的對等方。

3.3 連接

在獲取可用的對等項列表後,如果已找到要連接的設備,請調用該connect()方法以連接到該設備。此方法調用需要一個WifiP2pConfig 包含要連接的設備信息的對象。以下代碼顯示如何創建與所需設備的連接:

WifiP2pDevice device;
WifiP2pConfig config = new WifiP2pConfig();
//設置地址
config.deviceAddress = device.deviceAddress;
mManager.connect(mChannel, config, new ActionListener() {

    @Override
    public void onSuccess() {
    }

    @Override
    public void onFailure(int reason) {
        //failure logic
    }
});

WifiP2pManager.ActionListener中onSuccess()並不能表示成功連接,而是應該通過廣播監聽

ConnectionInfoListener infoListener
if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
    NetworkInfo networkInfo = (NetworkInfo) intent
        .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
    if (networkInfo.isConnected()) {
        // we are connected with the other device, request connection
        // info to find group owner IP
        manager.requestConnectionInfo(channel, infoListener);
    } else {
        // It's a disconnect
    }
}

連接成功後,調用requestConnectionInfo()獲取group的相關信息。該操作是異步的,在如下代碼漲接收信息:

@Override
public void onConnectionInfoAvailable(final WifiP2pInfo info) {
    if (info.groupFormed && info.isGroupOwner) {
        //server 創建ServerSocket
    } else if (info.groupFormed) {
        // The other device acts as the client. In this case, we enable the
        // get file button.
        //連接Server。
    }
}

3.4 Socket

建立連接後,您可以使用套接字在設備之間傳輸數據。傳輸數據的基本步驟如下:

創建一個ServerSocket。此套接字等待來自指定端口上的客戶端的連接並阻塞直到它發生,因此在後臺線程中執行此操作。
創建一個客戶端Socket。客戶端使用服務器套接字的IP地址和端口連接到服務器設備。
將數據從客戶端發送到服務器。當客戶端套接字成功連接到服務器套接字時,您可以使用字節流將數據從客戶端發送到服務,也可以從服務端發送到客戶端。
服務器套接字等待客戶端連接(使用該accept()方法)。此調用將阻塞,直到客戶端連接,因此調用它是另一個線程。當連接發生時,服務器設備可以從客戶端接收數據。對此數據執行任何操作,例如將其保存到文件或將其呈現給用戶。

group owner創建Server Socket,等待連接

mServerSocket = new ServerSocket(SOCKET_PORT);
mClientSocket = mServerSocket.accept();

非Owner創建Socket,連接Server端。

mClientSocket = new Socket();
mClientSocket.connect((new InetSocketAddress(mHostAddress, SOCKET_PORT)), 0);

3.5 互傳圖片

socekt連接成功後,保留outputStream 和inputStream,並進行接收和發送的相關操作。

//獲取輸入輸出流
mOutputStream = new DataOutputStream(mClientSocket.getOutputStream());
mInputStream = new DataInputStream(mClientSocket.getInputStream());
//不管是server還是client都接收圖片。
while (!mExit) {
    //循環接收圖片
    if(!receiveFile(mInputStream))
        break;
}

發送圖片

long len = file.length();
try {
    //發送圖片長度
    mOutputStream.writeLong(len);
    //獲取文件輸入流
    FileInputStream inputStream = new FileInputStream(file);
    byte buf[] = new byte[4096];
    int count;
    //發送圖片數據
    while ((count = inputStream.read(buf)) != -1) {
        mOutputStream.write(buf, 0, count);
    }
    inputStream.close();
} catch (IOException e) {
    e.printStackTrace();
}

歡迎大家關注、評論、點贊
你們的支持是我堅持的動力。
歡迎關注我的微信公衆號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章