美文网首页
Android p2p简单实现

Android p2p简单实现

作者: DON_1007 | 来源:发表于2020-06-17 17:57 被阅读0次

    p2p技术,可以让具备相应硬件的 Android 4.0(API 级别 14)或更高版本设备在没有中间接入点的情况下,通过 wlan 进行直接互联。使用这些 api,您可以实现支持 wlan p2p 的设备间相互发现和连接,从而获得比蓝牙连接更远距离的高速连接通信效果。对于多人游戏或照片共享等需要在用户之间共享数据的应用而言,这一技术非常有用。

    一、服务发布

    一个设备上可以有多个p2p服务存在,通过addLocalService可以在设备上发布自己的p2p服务,通过removeLocalService可以移除自己的p2p服务

    // 获取 WifiP2pManager
    mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
    // 获取 WifiP2pManager.Channel
    mChannel = mManager.initialize(this, getMainLooper(), null);
    Map<String, String> map = new HashMap<>();
    map.put("port", "8888");
    // 构造一个 WifiP2pDnsSdServiceInfo,可通过此对象发布自己的信息,比如上面的 port 8888 
    p2pDnsSdServiceInfo = WifiP2pDnsSdServiceInfo.newInstance(serviceName, serviceType, map);
    // 发布自己的p2p服务
    mManager.addLocalService(mChannel, p2pDnsSdServiceInfo, new WifiP2pManager.ActionListener() {
                            @Override
                            public void onSuccess() {
                                Log.i(TAG, "startServer onSuccess");
                            }
    
                            @Override
                            public void onFailure(int reason) {
                                Log.i(TAG, "startServer onFailure:" + reason);
                            }
                        });
    

    serviceName是服务的名字,可以自定义,这里设置为 DON_TEST
    serviceType是服务类型,格式为 _<protocol>._<transportlayer>,表示协议+传输类型。

    例如:

    • _ipp._tcp,协议为ipp(打印机服务),通过tcp传输数据
    • _presence._tcp 协议为presence(xmpp.org的消息传递协议),通过tcp传输数据

    二、设备&服务搜索

    1、注册广播

    p2p设备信息及连接状态的变化,需要通过监听系统广播来实现,常用到的广播有以下几个:

    • WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION p2p网络状态广播
    • WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION p2p设备列表修改广播
    • WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION 当前设备信息修改广播
    • WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION p2p连接状态广播
            IntentFilter 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);
            registerReceiver(broadCastReceiver, intentFilter);
    
    2、搜索设备

    通过方法discoverPeers可搜索设备周围的p2p设备

    mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
                   @Override
                   public void onSuccess() {
                         Log.i(TAG, "search onSuccess");
                   }
      
                   @Override
                   public void onFailure(int reason) {
                          Log.i(TAG, "search onFailure:" + reason);
                   }
     });
    

    discoverPeers会触发广播 WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION,收到此广播后,通过方法requestPeers可获取周边p2p设备列表

        private WifiP2pManager.PeerListListener peerListListener = new WifiP2pManager.PeerListListener() {
            @Override
            public void onPeersAvailable(WifiP2pDeviceList wifiP2pDeviceList) {
                // 获取设备列表
                if (wifiP2pDeviceList != null && !wifiP2pDeviceList.getDeviceList().isEmpty()) {
                    mainActivity.updateDevices(wifiP2pDeviceList.getDeviceList());
                }
            }
        };
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            Log.i(TAG, "onReceive " + action);
            if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {// p2p状态
                int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
                if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                    // Wifi P2P is enabled
                    Log.i(TAG, "Wifi P2P is enabled");
                } else {
                    // Wi-Fi P2P is not enabled
                    Log.i(TAG, "Wifi P2P is not enabled");
                }
            } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {// p2p设备变化监听
                if (mManager != null) {
                    mManager.requestPeers(mChannel, peerListListener);
                }
            } 
        }
    
    3、搜索服务

    通过 一、服务发布,发布了一个名为 DON_TEST 的服务,通过discoverServices方法就可以搜索到这个服务,同时需要通过setDnsSdResponseListeners方法设置监听WifiP2pManager.DnsSdServiceResponseListenerWifiP2pManager.DnsSdTxtRecordListener,在搜索过程中这两个监听会回调搜索到的服务信息。

    WifiP2pManager.DnsSdServiceResponseListener用于监听搜索到的服务信息

        boolean isConnecting = false;
        private WifiP2pManager.DnsSdServiceResponseListener dnsListener = new WifiP2pManager.DnsSdServiceResponseListener() {
            @Override
            public void onDnsSdServiceAvailable(String instanceName, String registrationType, WifiP2pDevice device) {
                Log.i(TAG, "onDnsSdServiceAvailable " + instanceName + " " + isConnecting);
                if (isConnecting) {
                    return;
                }
                if (serviceName.equals(instanceName)) {
                    isConnecting = true;
                    connect(device);
                }
            }
        };
    

    WifiP2pManager.DnsSdTxtRecordListener用于获取服务发布携带的额外信息,例如 一、服务发布 中的携带的信息 port 8888

      private WifiP2pManager.DnsSdTxtRecordListener recordListener = new WifiP2pManager.DnsSdTxtRecordListener() {
            @Override
            public void onDnsSdTxtRecordAvailable(String fullDomain, Map record, WifiP2pDevice device) {
                Log.i(TAG, "onDnsSdTxtRecordAvailable " + fullDomain);
            }
        };
    
            mManager.setDnsSdResponseListeners(mChannel, dnsListener, recordListener);
            serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
            mManager.addServiceRequest(mChannel, serviceRequest, new WifiP2pManager.ActionListener() {
                @Override
                public void onSuccess() {
    
                }
    
                @Override
                public void onFailure(int reason) {
    
                }
            });
            // 搜索服务
            mManager.discoverServices(mChannel, new WifiP2pManager.ActionListener() {
                @Override
                public void onSuccess() {
                    Log.i(TAG, "search onSuccess");
                }
    
                @Override
                public void onFailure(int reason) {
                    Log.i(TAG, "search onFailure:" + reason);
                }
            });
    
    

    三、文件传输

    1、设备连接

    通过WifiP2pManager.DnsSdServiceResponseListener可以搜索到可用的服务信息,通过方法 onDnsSdServiceAvailable 中的参数 WifiP2pDevice可获取到服务所在的设备信息,通过 connect方法可以连接该设备

        public void connect(WifiP2pDevice device) {
            Log.i(TAG, "connect " + device.deviceName);
            WifiP2pConfig config = new WifiP2pConfig();
            config.deviceAddress = device.deviceAddress;
            config.wps.setup = WpsInfo.PBC;
            mManager.connect(mChannel, config, new WifiP2pManager.ActionListener() {
    
                @Override
                public void onSuccess() {
                    //success logic
                    Log.i(TAG, "connect success");
                }
    
                @Override
                public void onFailure(int reason) {
                    //failure logic
                    Log.i(TAG, "connect onFailure " + reason);
                    isConnecting = false;
                }
            });
        }
    

    connect调用成功之后,如果是首次连接,对端手机会显示一个弹窗,用户选择允许连接之后,两部手机手机才能建立连接。
    建立连接之后,系统会发送广播WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION,收到这个广播之后,可通过requestConnectionInfo方法获取连接信息 WifiP2pInfo,通过WifiP2pInfo可获取对方的ip

        public void requestConnectionInfo() {
            Log.i(TAG, "requestConnectionInfo");
            mManager.requestConnectionInfo(mChannel, new WifiP2pManager.ConnectionInfoListener() {
                @Override
                public void onConnectionInfoAvailable(WifiP2pInfo wifiP2pInfo) {
                    try {
                        InetAddress address = InetAddress.getByName(wifiP2pInfo.groupOwnerAddress.getHostAddress());
                        Log.i(TAG, "address " + address.toString());
                    } catch (Exception e) {
                        Log.w(TAG, e);
                    }
                }
            });
        }
    
    2、创建群组

    连接到了一起,此时系统会自动创建一个群组(Group)并随机指定一台设备为群主(GroupOwner)。此时,对于两台设备来说,群主的IP地址是可知的(系统回调函数中有提供),但客户端的IP地址需要再来通过其他方法来主动获取。

    可以通过创建群组,指定某台设备作为服务器端(群主),即指定某台设备用来接收文件,因此,服务器端要主动创建群组,并等待客户端的连接。

            mManager.createGroup(mChannel, new WifiP2pManager.ActionListener() {
                @Override
                public void onSuccess() {
                    Log.i(TAG, "createGroup onSuccess");
                }
    
                @Override
                public void onFailure(int reason) {
                    Log.i(TAG, "createGroup onFailure: " + reason);
                }
            });
    
    3、传输文件

    server端

    package com.don.p2pdemo;
    
    import android.content.Context;
    import android.content.Intent;
    import android.net.Uri;
    import android.os.AsyncTask;
    import android.os.Environment;
    import android.util.Log;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class FileServerAsyncTask extends AsyncTask {
        private final String TAG = "p2p:FileServerAsyncTask";
    
        private Context context;
    
        public FileServerAsyncTask(Context context) {
            this.context = context;
        }
    
        @Override
        protected String doInBackground(Object[] 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() + "/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.w(TAG, e);
                return null;
            }
        }
    
        private void copyFile(InputStream inputStream, FileOutputStream fileStream) throws IOException {
            byte[] bytes = new byte[1024];
            int index = 0;
            while ((index = inputStream.read(bytes)) != -1) {
                fileStream.write(bytes, 0, index);
            }
            fileStream.flush();
        }
    
        /**
         * Start activity that can handle the JPEG image
         */
        @Override
        protected void onPostExecute(Object result) {
            if (result != null) {
                Intent intent = new Intent();
                intent.setAction(android.content.Intent.ACTION_VIEW);
                intent.setDataAndType(Uri.parse("file://" + result), "image/*");
                context.startActivity(intent);
            }
        }
    }
    

    客户端

        boolean isSending = false;
        public void sendImage(String host, int port) {
            if (isSending) {
                return;
            }
            Log.i(TAG, "sendImage " + host + " " + port);
            Context context = this.getApplicationContext();
            int len;
            Socket socket = null;
            byte buf[] = new byte[1024];
            try {
                socket = new Socket(host, port);
                OutputStream outputStream = socket.getOutputStream();
                InputStream inputStream = context.getAssets().open("icon.png");
                while ((len = inputStream.read(buf)) != -1) {
                    outputStream.write(buf, 0, len);
                }
                outputStream.close();
                inputStream.close();
                Log.i(TAG, "sendImage close");
            } catch (Exception e) {
                Log.w(TAG, e);
            } finally {
                if (socket != null) {
                    if (socket.isConnected()) {
                        try {
                            socket.close();
                        } catch (IOException e) {
                            //catch logic
                        }
                    }
                }
            }
            isSending = false;
            Log.i(TAG, "sendImage end");
        }
    

    在两部手机通过p2p连接之后,客户端传输文件到服务端

        public void requestConnectionInfo() {
            Log.i(TAG, "requestConnectionInfo");
            mManager.requestConnectionInfo(mChannel, new WifiP2pManager.ConnectionInfoListener() {
                @Override
                public void onConnectionInfoAvailable(final WifiP2pInfo wifiP2pInfo) {
                    Log.i(TAG, "onConnectionInfoAvailable 群主:" + wifiP2pInfo.isGroupOwner);
                    if (wifiP2pInfo.isGroupOwner) {
                        AsyncTask asyncTask = new FileServerAsyncTask(MainActivity.this);
                        asyncTask.execute();
                    } else {
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    InetAddress address = InetAddress.getByName(wifiP2pInfo.groupOwnerAddress.getHostAddress());
                                    Log.i(TAG, "address " + address.toString());
                                    sendImage(address.toString().replace("/",""), 8888);
                                } catch (Exception e) {
                                    Log.w(TAG, e);
                                }
                            }
                        }).start();
                    }
                }
            });
        }
    
    

    参考:
    https://developer.android.google.cn/guide/topics/connectivity/wifip2p#java
    https://blog.csdn.net/weixin_33735676/article/details/87990760

    相关文章

      网友评论

          本文标题:Android p2p简单实现

          本文链接:https://www.haomeiwen.com/subject/nhmtbctx.html