Connectivity-----Wi-Fi Peer-to-Peer

Wifi peer-to-peer允许Android 4.0(API level14)或以后的拥有wifi硬件的设备通过wifi直接互相连接,而不用中间媒介(Android的wifi P2p框架符合wifi联盟的认证计划)。通过使用该框架的api,你可以在相互间都支持wifi p2p时发现并连接其他设备,并且比蓝牙更远距离且更高速度地互相通信。这对于用户之间共享数据是非常有用的,例如多玩家游戏或者照片分享app。

wifi p2p模块的api由一下部分组成:

1允许你发现,请求和连接到点的方法(定义在WifiP2pManager类中);

2允许你监听WifiP2pManager类中方法是否成功执行的监听器。当调用WifiP2pManager的方法时,每个方法都会收到一个指定的监听器(通过函数参数传入)。

3通知你特定事件的Intent(由wifi P2p框架通知),例如连接失败,发现新的连接点等

你经常需要一起使用这三个部分的api。例如,你可以提供一个WifiP2pManager.ActionListenerdiscoverPeers()方法,这样你就会被 ActionListener.onSuccess() and ActionListener.onFailure()  方法通知。如果discoverPeers()方法发现可连接点的列表改变时一个WIFI_P2P_PEERS_CHANGED_ACTION intent也会被广播。

API Overview

WifiP2pManager对象为你提供许多方法,这使你能够和wifi设备通信(例如发现和连接点)。下面列举方法:

Table 1.Wi-Fi P2P Methods

Method Description
initialize() Registers the application with the Wi-Fi framework. This must be called before calling any other Wi-Fi P2P method.
connect() Starts a peer-to-peer connection with a device with the specified configuration.
cancelConnect() Cancels any ongoing peer-to-peer group negotiation.
requestConnectInfo() Requests a device's connection information.
createGroup() Creates a peer-to-peer group with the current device as the group owner.
removeGroup() Removes the current peer-to-peer group.
requestGroupInfo() Requests peer-to-peer group information.
discoverPeers() Initiates peer discovery
requestPeers() Requests the current list of discovered peers.
为了Wifi P2p框架可以通知你的Activity方法调用的状态,WifiP2pManager的方法要求你传进一个监听器。可用的监听器接口和对应的WifiP2pMethod方法,列举如下:

Table 2. Wi-Fi P2P Listeners

Listener interface Associated actions
WifiP2pManager.ActionListener connect()cancelConnect()createGroup(),removeGroup(), and discoverPeers()
WifiP2pManager.ChannelListener initialize()
WifiP2pManager.ConnectionInfoListener requestConnectInfo()
WifiP2pManager.GroupInfoListener requestGroupInfo()
WifiP2pManager.PeerListListener requestPeers()
api定义了许多当特定的wifi事件发生时,用来广播的意图。例如:一个点被发现或者设备的wifi状态改变。你可以注册一个广播接收器来接收这些意图。

Table 3. Wi-Fi P2P Intents

Intent Description
WIFI_P2P_CONNECTION_CHANGED_ACTION Broadcast when the state of the device's Wi-Fi connection changes.
WIFI_P2P_PEERS_CHANGED_ACTION Broadcast when you call discoverPeers(). You usually want to call requestPeers() to get an updated list of peers if you handle this intent in your application.
WIFI_P2P_STATE_CHANGED_ACTION Broadcast when Wi-Fi P2P is enabled or disabled on the device.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION Broadcast when a device's details have changed, such as the device's name.

Creating a Broadcast Receiver for Wi-Fi P2P Intents

广播接收器允许你对系统的广播进行接收,这样你的app可以对你感兴趣的事件进行处理。基本的广播接收器创建步骤列举如下:
1 创建一个class继承自BroadcastReceiver。对于接收器的构造函数,你很可能想要WifiP2pManager,WifiP2pManager.Channel作为参数和注册接收器的Activity。这个允许你的广播接收器发送更新给你的Activity,并且访问wifi设备,访问通信通道。
2在广播接收器中,挑出你感兴趣的意图并在onReceive方法中进行处理。根据收到的意图执行需要的操作。例如,如果接收器收到 WIFI_P2P_PEERS_CHANGED_ACTION 的意图,你可以调用requestPeers()方法来获取当前可连接点的表。
下面的代码向你展示了如何创建一个典型的广播接收器。在接收到意图时,广播接收器用WifiP2pManager和Activity对象作为参数,并用这两个对象来适当的执行需要的操作:
 * A BroadcastReceiver that notifies of important Wi-Fi p2p events.
 */
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {

    private WifiP2pManager mManager;
    private Channel mChannel;
    private MyWiFiActivity mActivity;

    public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel,
            MyWifiActivity activity) {
        super();
        this.mManager = manager;
        this.mChannel = channel;
        this.mActivity = activity;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
            // Check to see if Wi-Fi is enabled and notify appropriate activity
        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
            // Call WifiP2pManager.requestPeers() to get a list of current peers
        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
            // Respond to new connection or disconnections
        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
            // Respond to this device's wifi state changing
        }
    }
}

Creating a Wi-Fi P2P Application

创建一个wifi P2p app涉及为你的app创建创建和注册广播接收器,发现连接点,连接和发送数据到一个点。下面的部分介绍如何做到:

Initial setup

在使用wifi- P2p api之间,你必须确保你的app可以访问硬件,并且设备支持wifi功能。如果支持wifi你可以获取WifiP2pManager对象,创建并注册的的广播接收器,并开始使用api。
1在Android manifest中请求使用wifi硬件的权限并声明你的app拥有正确的最小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.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
2检查是否支持wifi。一个确认这个的好地方是当你的广播接收器接收到   WIFI_P2P_STATE_CHANGED_ACTION  intent.时,把wifi状态通知你的activity:
@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
        }
    }
    ...
}
3在你的activity的onCreate方法中,获取wifiP2pManager的实例并调用initialize()注册wifi框架。这个方法返回一个 WifiP2pManager.Channel ,这是用来连接你的app和wifi框架的。你应该创建一个广播接收器的实例(用wifiP2pManager和WiFiP2pManager.channel 做构造器参数),这使你的广播接收器把感兴趣的事情通知你的activity并根据它更新。这也使你在必要时能够操作设备的wifi状态:
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(mManager, mChannel, this);
    ...
}
创建一个intent filter并添加与广播接收器要检查的相同的intents:
IntentFilter mIntentFilter;
...
@Override
protected void onCreate(Bundle savedInstanceState){
    ...
    mIntentFilter = new IntentFilter();
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
    ...
}
在onResume()方法中注册你的广播接收器,并在onPause()方法中unregister它:
/* register the broadcast receiver with the intent values to be matched */
@Override
protected void onResume() {
    super.onResume();
    registerReceiver(mReceiver, mIntentFilter);
}
/* unregister the broadcast receiver */
@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(mReceiver);
}
当你获得了wifiP2pManager.Channel并设置了广播接收器,你的app就可以调用wifi模块的app并接受wifi intents。 你现在可以实现你app并通过调用WiFiP2pManager中的方法来 使用wifi特性。下一个部分将介绍如何执行常见的操作,如发现和连接热点。

Discovering peers

为了发现可以连接的热点,你要调用discoverPeers()方法来发现一定范围内可用的热点。调用这个方法是不同步的, 如果你创建了一个 WifiP2pManager.ActionListener那么 会在 onSuccess()   onFailure()方法中对成功和失败时进行相应处理。onSuccess()方法只在discovery()操作成功执行,并且不会提供任何关于实际发现的热点信息,如果有的话:
mManager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
    @Override
    public void onSuccess() {
        ...
    }

    @Override
    public void onFailure(int reasonCode) {
        ...
    }
});
 如果discover过程成功并发现热点,系统将会广播WIFI_P2P_PEERS_CHANGED_ACTION intent,你可以在广播接收器里面监听并获取一个热点列表。当你的app接收 WIFI_P2P_PEERS_CHANGED_ACTIONintent时,你可以通过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 (mManager != null) {
        mManager.requestPeers(mChannel, myPeerListListener);
    }
}
requestPeers()方法也是不同步的,并会在热点表可用时用过onPeersAvailable( WifiP2pManager.PeerListListener  interface中定义 )(在)方法通知你的activity。onPeersAvailable()方法为你提供一个   WifiP2pDeviceList (你可以通过迭代找出你想要的热点);
Connecting to peers
当你获得热点表并找出你要连接的设备后,调用connect()方法来连接到该设备。这个方法需要一个 WifiP2pConfig 对象(该对象拥有要连接的设备的信息)。连接的成功与失败会在WifiP2pManager.ActionListener. 中通知。下面的代码展示如何创建一个连接到一个目标设备:
//obtain a peer from the WifiP2pDeviceList
WifiP2pDevice device;
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
mManager.connect(mChannel, config, new ActionListener() {

    @Override
    public void onSuccess() {
        //success logic
    }

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

Transferring data

一旦连接被建立,你可以用sockets在设备之间传输数据,传输数据的基本步骤如下:
1创建一个ServerSocket。这个socket会等待来自客户端的连接(在指定的端口并阻塞线程直到连接成功),所以这在一个后台线程中执行。
2创建一个客户端端socket,客户端用户使用ip地址和服务端的端口号来连接到服务端。
3从客户端发送数据到服务端,当客户端socket成功连接到服务端的socket时,你可以用字节流的方式向服务端发送数据。
4服务端socket通过accept()方法来等待客户端的连接。这个方法是阻塞式的,所以请在另一个线程里面调用该方法。当一个连接发生时,服务端设备会接收到客户端发来的数据。可以对该数据执行任何操作,如把他存储到一个文件或显示给用户。

下面的例子,根据 Wi-Fi P2P Demo  sample修改而来,想你展示怎样创建这样一个客户端-服务端之间socket通信,并用server从客户端传输一个图片到服务端:
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 {

            /**
             * Create a server socket and wait for client connections. This
             * call blocks until a connection is accepted from a client
             */
            ServerSocket serverSocket = new ServerSocket(8888);
            Socket client = serverSocket.accept();

            /**
             * If this code is reached, a client has connected and transferred data
             * Save the input stream from the client as a JPEG file
             */
            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.getInputStream();
            copyFile(inputstream, new FileOutputStream(f));
            serverSocket.close();
            return f.getAbsolutePath();
        } catch (IOException e) {
            Log.e(WiFiDirectActivity.TAG, e.getMessage());
            return null;
        }
    }

    /**
     * Start activity that can handle the JPEG image
     */
    @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);
        }
    }
}
在客户端,用客户端socket连接到server socket并传输数据,这个例子传输客户端中的一个图片:
Context context = this.getApplicationContext();
String host;
int port;
int len;
Socket socket = new Socket();
byte buf[]  = new byte[1024];
...
try {
    /**
     * Create a client socket with the host,
     * port, and timeout information.
     */
    socket.bind(null);
    socket.connect((new InetSocketAddress(host, port)), 500);

    /**
     * Create a byte stream from a JPEG file and pipe it to the output stream
     * of the socket. This data will be retrieved by the server device.
     */
    OutputStream outputStream = socket.getOutputStream();
    ContentResolver cr = context.getContentResolver();
    InputStream inputStream = null;
    inputStream = cr.openInputStream(Uri.parse("path/to/picture.jpg"));
    while ((len = inputStream.read(buf)) != -1) {
        outputStream.write(buf, 0, len);
    }
    outputStream.close();
    inputStream.close();
} catch (FileNotFoundException e) {
    //catch logic
} catch (IOException e) {
    //catch logic
}

/**
 * Clean up any open sockets when done
 * transferring or if an exception occurred.
 */
finally {
    if (socket != null) {
        if (socket.isConnected()) {
            try {
                socket.close();
            } catch (IOException e) {
                //catch logic
            }
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值