Android WLAN 直连(对等连接或 P2P)

概念

Wi-Fi Direct®支持Wi-Fi设备相互直接连接,并非Android设备独有的,是WIfi联盟提供的一种设备与设备之间直接联网通讯的技术,实现点对点的连接,我们常说的Wifi P2P连接。

最大的特点和优势就是无需接入网络就可连接设备
有了Wi-Fi Direct,手机、摄像头、打印机、个人电脑和游戏设备无需互联网连接,就能够建立自己的Wi-Fi网络。Wi-Fi Direct设备相互连接后,通过设备设置,就可快速简便地传送或显示内容、玩游戏以及分享应用。这些设备可以一对一地连接,或者几个设备形成的一组设备可以同时连接。由于无需接入点或互联网连接,因此设备在哪里,哪里就有Wi-Fi Direct网络。设备之间建立的Wi-Fi Direct连接对包括Miracast®在内的很多应用而言属于底层技术。诸如智能手机、摄像头、打印机、电视机、个人电脑、游戏机等成千上万的设备均已通过认证。
随时随地建立连接
即使附近没有Wi-Fi网络可用,Wi-Fi Direct设备之间也可以随时随地建立连接。Wi-Fi Direct设备向同一区域内的其他设备发送信号,让这些设备知道可以建立连接。用户可以查看可用设备并请求连接,也可以接收另一台设备发出的连接邀请。两个或更多个经过Wi-Fi Direct认证的设备直接连接时,会使用Wi-Fi Protected Setup™形成一个Wi-Fi Direct设备组。

Android设备如何使用WLAN P2P

Android API 概览
WifiP2pManager 类提供的方法使您可以在设备上与 WLAN 硬件交互,以执行发现和连接对等设备等操作。可执行的操作如下:

initialize()通过 WLAN 框架注册应用。必须先调用此方法,然后再调用任何其他 WLAN P2P 方法。
connect()启动与具有指定配置的设备的对等连接。
cancelConnect()取消任何正在进行的对等群组协商。
requestConnectInfo()请求设备连接信息。
createGroup()以群组所有者的身份,使用当前设备创建对等群组。
removeGroup()移除当前对等群组。
requestGroupInfo()请求对等群组信息。
discoverPeers()启动对等设备发现
requestPeers()请求已发现对等设备的当前列表。

WifiP2pManager 方法使您可以在侦听器中进行传递,以便 WLAN P2P 框架可以向您的 Activity 通知通话状态。下表介绍可用的侦听器接口和使用侦听器的相应 WifiP2pManager 方法调用:

侦听器接口相关操作
WifiP2pManager.ActionListenerconnect()、cancelConnect()、createGroup()、removeGroup() 和 discoverPeers()
WifiP2pManager.ChannelListenerinitialize()
WifiP2pManager.ConnectionInfoListenerrequestConnectInfo()
WifiP2pManager.GroupInfoListenerrequestGroupInfo()
WifiP2pManager.PeerListListenerrequestPeers()

WLAN P2P API 定义当发生特定 WLAN P2P 事件时会广播的 Intent,例如发现新的对等设备时,或设备的 WLAN 状态更改时。您可以通过创建处理这些 Intent 的广播接收器,在应用中注册接收这些 Intent:

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当设备的详细信息(例如设备名称)更改时广播。

创建 WLAN P2P 应用

创建 WLAN P2P 应用涉及为应用创建并注册广播接收器、发现对等设备,连接到对等设备,以及将数据传输到对等设备。以下部分将介绍如何完成此操作。

初始设置
在使用 WLAN P2P API 之前,您必须确保您的应用可以访问硬件,并且设备支持 WLAN P2P API 协议。如果设备支持 WLAN P2P,您可以获得 WifiP2pManager 的实例,创建并注册广播接收器,然后开始使用 WLAN P2P API。

请求在设备上使用 WLAN 硬件的权限,同时声明您的应用在 Android 清单中具有正确的最低 SDK 版本:

<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.ACCESS_FINE_LOCATION" />
<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" />

检查 WLAN P2P 是否开启并受支持。您可以在广播接收器收到 WIFI_P2P_STATE_CHANGED_ACTION Intent 时,在接收器中检查此项。向您的 Activity 通知 WLAN P2P 的状态,并作出相应回应:

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

获取和初始化P2P资源
获取 WifiP2pManager 的实例,并通过调用 initialize(),在 WLAN P2P 框架中注册您的应用。此方法会返回 WifiP2pManager.Channel,用于将您的应用连接到 WLAN P2P 框架。此外,您还应该通过 WifiP2pManager 和 WifiP2pManager.Channel 对象以及对 Activity 的引用,创建广播接收器实例。这样广播接收器便可通知 Activity 感兴趣的事件并进行相应更新。此外,您还可以操纵设备的 WLAN 状态(如有必要):

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

创建 Intent 过滤器,然后添加与广播接收器检查内容相同的 Intent:

IntentFilter intentFilter;
...
@Override
protected void onCreate(Bundle savedInstanceState){
    ...
    intentFilter = new 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);
    ...
}

在 Activity 的 onResume() 方法中注册广播接收器,然后在 Activity 的onPause() 方法中取消注册该接收器:

/* register the broadcast receiver with the intent values to be matched */
@Override
protected void onResume() {
    super.onResume();
    registerReceiver(mReceiver, intentFilter);
}
/* unregister the broadcast receiver */
@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(mReceiver);
}

发现对等设备

如要发现可连接的对等设备,请调用 discoverPeers(),以检测范围内的可用对等设备。对此功能的调用为异步操作,如果您已创建 WifiP2pManager.ActionListener,则系统会通过 onSuccess() 和 onFailure() 告知应用成功与否。onSuccess() 方法仅会通知您发现进程已成功,但不会提供有关其发现的实际对等设备(如有)的任何信息:

manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
    @Override
    public void onSuccess() {
        ...
    }

    @Override
    public void onFailure(int reasonCode) {
        ...
    }
});

如果发现进程成功并检测到对等设备,则系统会广播 WIFI_P2P_PEERS_CHANGED_ACTION Intent,您可以在广播接收器中侦听该 Intent,以获取对等设备列表。当应用接收到 WIFI_P2P_PEERS_CHANGED_ACTION Intent 时,您可以通过 requestPeers() 请求已发现对等设备的列表。以下代码展示如何完成此项设置:

PeerListListener myPeerListListener;
...
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 (manager != null) {
        manager.requestPeers(channel, myPeerListListener);
    }
}

requestPeers() 方法也为异步操作,并可在对等设备列表可用时通过 onPeersAvailable()(定义见 WifiP2pManager.PeerListListener 接口)通知您的 Activity。onPeersAvailable() 方法为您提供 WifiP2pDeviceList,您可对其进行迭代以查找希望连接的对等设备。

连接到对等设备

获取可能对等设备的列表,且已确定您要连接的设备后,调用connect() 方法即可连接到相应设备。调用此方法需要使用 WifiP2pConfig 对象,其中包含要连接的设备的信息。您可以通过 WifiP2pManager.ActionListener 获知连接是否成功。以下代码展示如何创建与所需设备的连接:

//obtain a peer from the WifiP2pDeviceList
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
    }
});

建立网络连接

当P2P连接完成后,IP地址是由DHCPServer分配的,一般是Group Owner所在的设备开启DHCPServer给自己和对端(GC端)分贝IP地址。
如果当前设置是GC(Group Client),那么可通过WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION广播直接拿到GO端的IP地址

        if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
			NetworkInfo networkInfo = (NetworkInfo) intent
                    .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
            WifiP2pInfo p2pInfo =
                    (WifiP2pInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
            WifiP2pGroup p2pGroup =
                    (WifiP2pGroup) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);

            if (networkInfo.isConnected()) {
                device_addr[0] = p2pInfo.groupOwnerAddress.getHostAddress();
                mController.setGroupModule(false);
                WifiP2pDevice device = p2pGroup.getOwner();
                if (device != null && device.getWfdInfo() != null) {
                    mPort = String.valueOf(device.getWfdInfo().getControlPort());
                    mIp = p2pInfo.groupOwnerAddress.getHostAddress();
                } else {
                    Log.d(TAG, "device or device wfdInfo is null");
                }
			}
		}

如果当前设备是GO(Group Owner)那么就需要用其他方法获取到对端IP地址了,Android 原生未提供直接获取GC IP的方法
如果可以修改AOSP源码的话,可以通过DHCPServer给GC分配IP地址时,把地址给到当前的程序:
Android 源码:packages/modules/NetworkStack/src/android/net/dhcp/DhcpServer.java
transmitAck方法中添加以下code
private boolean transmitAck(@NonNull DhcpPacket packet, @NonNull DhcpLease lease, @NonNull MacAddress clientMac)

Intent notifyMiracast = new Intent(WIFI_P2P_IP_ADDR_CHANGED_ACTION);
notifyMiracast.putExtra(WIFI_P2P_PEER_IP_EXTRA,lease.getNetAddr().toString().substring(1));
notifyMiracast.putExtra(WIFI_P2P_PEER_MAC_EXTRA,lease.getHwAddr().toString());
Log.d(TAG,"sendBroadcast IP " + notifyMiracast.getStringExtra(WIFI_P2P_PEER_IP_EXTRA)
                + "MAC " + notifyMiracast.getStringExtra(WIFI_P2P_PEER_MAC_EXTRA));
mContext.sendBroadcast(notifyMiracast);

也可以读取ARP方法来获取IP

private String macAddressFromRoute(String macAddress){
	String ipAddress = null;
	BufferedReader reader = null;
	try {
		reader = new BufferedReader(new FileReader("/proc/net/arp"));
		// Skip over the line bearing column titles
		String line = reader.readLine();
		while ((line = reader.readLine()) != null) {
			String[] tokens = line.split("[ ]+");
				if (tokens.length < 6) {
					continue;
				}
			// ARP column format is
			// Address HWType HWAddress Flags Mask IFace
			String ip = tokens[0];
			String mac = tokens[3];
			if (mac.equals(macAddress)) {
				ipAddress = ip;
				break;
			}
		}
		if (ipAddress == null) {
			loge("Did not find remoteAddress {" + macAddress+ "} in /proc/net/arp");
		}
	} catch (FileNotFoundException e) {
		loge("Could not open /proc/net/arp to lookup mac address");
	} catch (IOException e) {
		loge("Could not read /proc/net/arp to lookup mac address");
	} finally {
		try {
			if (reader != null) {
				reader.close();
			}
		} catch (IOException e) {
			// Do nothing
		}
    }
	return ipAddress;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值