WiFi_Direct 直连开发实战

Wifi直连 开发

背景

近期有个项目,场景是 无网络情况下,实现一个手机与多个手机之间的交互。
采取的技术就是 wifi直连+socket通信 和 蓝牙连接+蓝牙通信。
这里只讲WiFi直连相关内容

WiFi直连 简介

  • WLAN 直连,最初称为Wi-Fi P2P(Peer-To-Peer),是Wi-Fi协议簇中的一个,使设备之间能够轻松连接彼此而不再需要一个中介性质的无线接入点(Access Point)。其使用范围从网页浏览到文件传输,以及同时与多个设备进行通信,能够充分发挥Wi-Fi的速度优势。符合此标准的设备即使来自不同的生产厂商,亦可实现轻松互联。
  • Wlan直连通过wifi网络传输、共享文件的一种技术, 具有传输速度快、效率高。
  • 作用原理类似于蓝牙,设备连接之后就能相互传文件了,WLAN直连的传输速度是近乎蓝牙速度的100倍。

相关权限

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> 
    <uses-permission android:name="android.permission.CHANGE_NETWORK_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.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

注意动态权限申请

Wifi直连 服务端

1、初始化直连及广播

定义回调及处理回调值

private SocektClientCallback socketChange = new SocektClientCallback() {
        @Override
        public void reStartClientThread() {
        }

        @Override
        public void socketStatus(String mac, boolean iscollect) { //iscollect 是否连接socket
            //有用户sockt退出或者加入
            //更新连接用户列表
            adapter.notifyItemChanged(ii, iscollect); 
        }

    };

    private BroadCallbackListener broadCallbackListener = new BroadCallbackListener() {

        @Override
        public void getMyMsg(WifiP2pDevice device) {
        }

        @Override
        public void updateList(ArrayList<WifiP2pDevice> list) {
            MyLog.e("mylog", "刷新连接用户的列表:" + list.size());
            //更新直连 连接列表
            adapter.notifyDataSetChanged();
        }

        @Override
        public void updateStatus(String s1, String s2) {
            MyLog.e("mylog", "服务端获取列表:" + s1);
            Tools.showToast(context,"服务端获取列表:"+s1);
        }
    };

初始化广播及直连

private void initBroadcast() {
        mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
        mChannel = mManager.initialize(this, getMainLooper(), null);
        mReceiver = new WiFiDirectBroadcastReceiverS(mManager, mChannel, this, broadCallbackListener);
        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);
    }

对应服务端广播类

public class WiFiDirectBroadcastReceiverS extends BroadcastReceiver {
    private WifiP2pManager manager;
    private WifiP2pManager.Channel channel;
    private SetMapActivity activity;
    private ArrayList<WifiP2pDevice> peers = new ArrayList<>();
    private BroadCallbackListener broadCallbackListener;

    public WiFiDirectBroadcastReceiverS(WifiP2pManager manager, WifiP2pManager.Channel channel,
                                        SetMapActivity activity, BroadCallbackListener broadCallbackListener) {
        super();
        this.manager=manager;
        this.channel=channel;
        this.activity=activity;
        //回调函数,注册广播的时候传进来
        this.broadCallbackListener=broadCallbackListener;
    }

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

        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals (action)) {
            //检测 WIFI 功能是否被打开
            int state=intent.getIntExtra (WifiP2pManager.EXTRA_WIFI_STATE, -1);
            if (state==WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                Log.e("mylog","Wi-Fi Direct 可用");
            } else {
                Log.e("mylog","Wi-Fi Direct 不可用");
            }
        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals (action)) {
            //获取当前可用连接点的列表
            Log.e("mylog","获取列表返回的广播");
            try{
                manager.requestPeers(channel, new WifiP2pManager.PeerListListener() {
                    @Override
                    public void onPeersAvailable(WifiP2pDeviceList wifiP2pDeviceList) {
                        Collection<WifiP2pDevice> devices =  wifiP2pDeviceList.getDeviceList();
                        //将获取到的列表传回Activity,并做展示或者连接
                        if(devices.size() == 0){
                            broadCallbackListener.updateStatus("0","0");
                        }else{
                            broadCallbackListener.updateStatus(devices.size()+"",devices.size()+"");
                        }
                    }
                });
            }catch (SecurityException e){
                e.printStackTrace();
            }
        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals (action)) {
            //建立或者断开连接
            if (manager == null) {
                return;
            }
            NetworkInfo networkInfo = (NetworkInfo) intent
                    .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
            manager.requestConnectionInfo(channel, new WifiP2pManager.ConnectionInfoListener() {
            	//该方法是判断当前设备是群主还是组员
            	//可以通过创建组来确定设备为群主
            	//如果希望是组员,那么主动连接服务端时,需要设置参数权重为1
                @Override
                public void onConnectionInfoAvailable(WifiP2pInfo wifiP2pInfo) {
                    if(wifiP2pInfo.groupFormed && wifiP2pInfo.isGroupOwner){
                        Log.e("mylog","群主");
                    }else if(wifiP2pInfo.groupFormed){
                        //isConnectServer = true;
                        Log.e("mylog","组员");
                    }
                }
            });
            if (networkInfo.isConnected()) {
                MyLog.e("mylog","WIFI Direct 连接!");
                peers.clear();
                try{
                	//该方法只有服务端才能调用成功,主要是获取连接到的设备的信息
                    manager.requestGroupInfo(channel, new WifiP2pManager.GroupInfoListener() {
                        @Override
                        public void onGroupInfoAvailable(WifiP2pGroup wifiP2pGroup) {
                            if(wifiP2pGroup != null){
                                peers.addAll(wifiP2pGroup.getClientList());
                                MyLog.e("mylog","客户端数量 :"+peers.size());
                                broadCallbackListener.updateList(peers);
                            }
                        }
                    });
                }catch (SecurityException e){
                    e.printStackTrace();
                }
            }else{
                MyLog.e("mylog","WIFI Direct 断开!");
                peers.clear();
                try{
                    manager.requestGroupInfo(channel, new WifiP2pManager.GroupInfoListener() {
                        @Override
                        public void onGroupInfoAvailable(WifiP2pGroup wifiP2pGroup) {
                            if(wifiP2pGroup != null){
                                Collection<WifiP2pDevice> collection = wifiP2pGroup.getClientList();
                                peers.addAll(collection);
                            }
                            broadCallbackListener.updateList(peers);
                        }
                    });
                }catch (SecurityException e){
                    e.printStackTrace();
                }
            }

        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals (action)) {
            //当前设备的 WIFI 状态发生变化
            WifiP2pDevice device = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
            MyLog.e("mylog","wifi发生变化:name:"+device.deviceName+",add:"+device.deviceAddress);
            broadCallbackListener.getMyMsg(device);
        }
    }

}
2、获取连接点列表
try {
            mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
                @Override
                public void onSuccess() {
                    MyLog.e("mylog", "启动获取列表线程成功");
                }

                @Override
                public void onFailure(int reasonCode) {
                    switch (reasonCode) {
                        case WifiP2pManager.ERROR:
                            MyLog.e("mylog", "启动获取列表线程失败:error");
                            break;
                        case WifiP2pManager.P2P_UNSUPPORTED:
                            MyLog.e("mylog", "启动获取列表线程失败:P2P unsupported");
                            break;
                        case WifiP2pManager.BUSY:
                            MyLog.e("mylog", "启动获取列表线程失败:busy");
                            break;
                        default:
                            MyLog.e("mylog", "启动获取列表线程失败");
                            break;
                    }
                }
            });
        } catch (SecurityException e) {
            e.printStackTrace();
        }

该方法只是开启一个获取列表的线程,如果获取成功,则触发广播 WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION,可以在该广播内获取列表等操作,初始化广播中讲到。
注意:只有服务端和客户端同时处于搜索列表状态,才能获取到对方的连接点

3、Socket初始化

Socket服务类

public class SocketServer {
    private Activity activity;
    private static ServerSocket server;
    private boolean isClint=false;
    public static Handler ServerHandler;
    private BufferedReader br;
    private SocektClientCallback socketChange;
    //socket组
    public static Map<String,Socket> clients = new HashMap<String,Socket>();
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            String mac = (String)msg.obj;
            boolean iscollect;
            if(msg.what == 1)
                iscollect = true;
            else
                iscollect = false;
            MyLog.e("mylog","socket 状态:"+iscollect+",mac:"+mac);
            socketChange.socketStatus(mac,iscollect);//断开socket
            return false;
        }
    });

    /**
     * @steps bind();绑定端口号
     * @effect 初始化服务端
     * @param port 端口号
     * */
    public SocketServer(int port, SocektClientCallback socketChange, Activity activity){
        try {
            this.activity = activity;
            if(server == null)
                server= new ServerSocket ( port );
            isClint=true;
            this.socketChange = socketChange;
        }catch (IOException e){
            e.printStackTrace ();
        }
       
    }

    /**
     * @steps listen();
     * @effect socket监听数据
     * */
    public void beginListen() throws Exception {
        /**
         * accept();
         * 接受请求
         * */
        Log.e("mylog","------调用accept-------");
        //如果没有客户端连接,accept就会阻塞
        Socket socket = server.accept();
        BufferedReader br = new BufferedReader(new InputStreamReader(
                socket.getInputStream(), "utf-8"));
        try{
            String stringId = br.readLine();
            clients.put(stringId,socket);
            MyLog.e("mylog","存入sockey:"+stringId+","+socket);
            Message msg = handler.obtainMessage(); //Message.obtain();
            msg.what = 1;// 消息标识
            msg.obj = stringId;// 消息存放
            handler.sendMessage(msg);
            LocalThreadPools.getInstance(activity).execute(new Runnable() {
                @Override
                public void run() {
                    Log.e("mylog", "加入--------------------当前在线:" + clients.size());
                    String mac = stringId;
                    try {
                        /**
                         * 实现数据循环接收
                         * */
                        String content = null;
                        while (true){
                            if((content = br.readLine())!= null){
                                returnMessage(content);
                            }else {
                                returnMessage(mac); //更新这个客户端状态
                                clients.remove(socket);
                                break;
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        MyLog.e("mylog", "用户("+mac+")退出("+e.getMessage()+")");
                        clients.remove(socket);
                    } catch (NullPointerException e) {
                        MyLog.e("mylog", "空指针:" + e.getMessage());
                        clients.remove(socket);
                    } catch (Exception e){
                        MyLog.e("mylog","其他异常:"+e.getMessage());
                        clients.remove(socket);
                    } finally {
                        MyLog.e("mylog","finally");
                        try {
                            socket.close();
                            br.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
        }catch (Exception e){
            MyLog.e("mylog","获取客户端表示失败:"+e.getMessage());
        }
    }


    /**
     * @steps write();
     * @effect socket服务端发送信息
     * */
    public void sendMessage(final String chat, File file, String checkedId) {
        for(String key: clients.keySet()){
            Log.e("mylog","key:"+key+","+clients.get(key));
        }
        Socket socket = clients.get(checkedId);
        if(socket == null){
            Log.e("mylog","socket is null");
        }else{
            Log.e("mylog","选择的客户端:"+socket+",数据:"+chat);
            Thread thread=new Thread ( new Runnable ( ) {
                @Override
                public void run() {
                    try {
                        OutputStream out= socket.getOutputStream ();
                        if( file != null && file.exists()){
                            byte[] buffer = null;
                            FileInputStream fis = new FileInputStream(file);
                            ByteArrayOutputStream bos = new ByteArrayOutputStream();
                            byte[] b = new byte[1024];
                            int n;
                            while ((n = fis.read(b)) != -1) {
                                bos.write(b, 0, n);
                            }
                            fis.close();
                            bos.close();
                            buffer = bos.toByteArray();
                            String photoByte = chat+Base64.getEncoder().encodeToString(buffer);
                            out.write ( (photoByte+"\n").getBytes("utf-8") );
                        }else
                            out.write ( (chat+"\n").getBytes("utf-8") );
                        out.flush ();
                    } catch (IOException e) {
                        e.printStackTrace ();
                        //todo 这里能知道是否断开,但是需要服务端主动发送消息
                        //如何客户端关闭,返回:Socket is closed
                        MyLog.e("mylog","服务端发送消息报错:"+e.getMessage());
                    }
                }
            } );
            thread.start ();
        }

    }

    /**
     * @steps read();
     * @effect socket服务端得到返回数据并发送到主界面
     * */
    public void returnMessage(String chat){
        Message msg=new Message ();
        msg.obj=chat;
        ServerHandler.sendMessage ( msg );
    }

    public void closeSocketServer() throws IOException {
        server.close();
    }
}

初始化SocketSerice类

/**
     * 初始化SocketService
     */
    public void initSocketServer() {
        try {
            if (server == null) {
                Log.e("mylog", "初始化SocketServer");
                server = new SocketServer(9999, socketChange, this);
                LocalThreadPools.getInstance(this).execute(new Runnable() {
                    @Override
                    public void run() {
                        while (true) {
                            try {
                                server.beginListen();
                            } catch (Exception e) {
                                e.printStackTrace();
                                MyLog.e("mylog", "beginListen:" + e.toString());
                            }
                        }
                    }
                });
                MyLog.e("mylog", "服务器启动成功");
            }
        } catch (Exception e) {
            e.printStackTrace();
            MyLog.e("mylog", "beginlisten,err:" + e.toString());
        }
    }

然后处理客户端发送的消息

/**socket收到消息线程*/
        SocketServer.ServerHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                String con = msg.obj.toString();
                MyLog.e("mylog", "服务端接收" + con);
                //客户端发送的消息可以在这里处理
            }
        };

服务端给客户端发送消息

/*** 服务端发送消息 ****/
    public void sendMsg(String s, File file, String ad) {
        /**socket发送数据*/
        if (server == null) {
            Log.e("mylog", "服务端为空");
            Tools.showToast(context, "服务器未启动");
        } else {
            server.sendMessage(s, file, ad);
        }
    }
4、创建组和移除组

我个人使用过程中,创建组 经常失败。

public void createGroup() {
        if(mManager!=null)
        mManager.createGroup(mChannel, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
                MyLog.e("mylog", "创建组成功");
                Tools.showToast(context, "创建组成功");
            }

            @Override
            public void onFailure(int reason) {
                MyLog.e("mylog", "创建组失败:" + reason);
                Tools.showToast(context, "创建组失败");
            }
        });
    }

    public void removeGroup() {
        mManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
                Log.e("mylog","移除组成功");
                Tools.showToast(context,"移除组成功");
            }

            @Override
            public void onFailure(int reason) {
                Log.e("mylog","移除组失败:"+reason);
                Tools.showToast(context,"移除组失败");
            }
        });
    }

Wifi直连 客户端

1、初始化直连及广播、

定义回调及处理

private SocektClientCallback restart = new SocektClientCallback() {
        @Override
        public void reStartClientThread() {
            //startClientThread();
            MyLog.e("mylog", "客户端退出了,需要重连");
            isCreateSocket = false;
        }

        @Override
        public void socketStatus(String mac, boolean b) {
         
        }
    };
    private BroadCallbackListener broadCallbackListener = new BroadCallbackListener() {

        @Override
        public void getMyMsg(WifiP2pDevice device) {
            /*MyAddress = device.deviceAddress;
            MyName = device.deviceName;*/
        }

        @Override
        public void updateList(ArrayList<WifiP2pDevice> list) {
        }

        @Override
        public void updateStatus(String s1, String s2) {
            if ("成功".equals(s1)) {
                if (!isCreateSocket)
                    startClientThread();
            } else if("断开".equals(s1)){
                isCreateSocket = false;
                Tools.showToast(context, "wifi直连未连接!");
            } else if("0".equals(s1)){
                isCreateSocket = false;
                Tools.showToast(context, "获取wifi直连列表为空!");
                Dialog.hideWaitDialog();
            } else if("不匹配".equals(s1)){
                isCreateSocket = false;
                Tools.showToast(context, "母机地址不匹配!");
                Dialog.hideWaitDialog();
            } else {
                isCreateSocket = false;
            }
        }
    };

初始化广播及直连

private void initBroadcast() {
        mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
        mChannel = mManager.initialize(this, getMainLooper(), null);
        mReceiver = new WiFiDirectBroadcastReceiverC(mManager, mChannel, this, broadCallbackListener);
        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);
    }

对应客户端广播类

public class WiFiDirectBroadcastReceiverC extends BroadcastReceiver implements changebroad {
    private WifiP2pManager manager;
    private WifiP2pManager.Channel channel;
    private FlightConfigActivity activity;
    private ArrayList<WifiP2pDevice> peers = new ArrayList<>();
    public boolean isCollectDev = false; //防止重复弹设备列表
    private BroadCallbackListener broadCallbackListener;

    public WiFiDirectBroadcastReceiverC(WifiP2pManager manager, WifiP2pManager.Channel channel,
                                        FlightConfigActivity activity, BroadCallbackListener broadCallbackListener) {
        super();
        this.manager=manager;
        this.channel=channel;
        this.activity=activity;
        this.broadCallbackListener = broadCallbackListener;
    }

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

        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals (action)) {
            //检测 WIFI 功能是否被打开
            int state=intent.getIntExtra (WifiP2pManager.EXTRA_WIFI_STATE, -1);
            if (state==WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                MyLog.e("mylog","Wi-Fi Direct 可用");
            } else {
                MyLog.e("mylog","Wi-Fi Direct 不可用");
            }
        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals (action)) {
            //获取当前可用连接点的列表
            MyLog.e("mylog","获取列表返回的广播"+(manager != null)+","+!isCollectDev);
            if (manager !=null && !isCollectDev) {
                try{
                    manager.requestPeers(channel, new WifiP2pManager.PeerListListener() {
                        @Override
                        public void onPeersAvailable(WifiP2pDeviceList wifiP2pDeviceList) {
                            Collection<WifiP2pDevice> devices =  wifiP2pDeviceList.getDeviceList();
                           	//需要连接的服务端 直连mac地址
                            String qrcode = DownloadData.getQRCode_Str();
                            boolean ishave = false;
                            MyLog.e("mylog","列表siz:"+devices.size());
                            for(WifiP2pDevice device: devices){
                                if(device.deviceAddress.equals(qrcode)) {
                                    ishave = true;
                                    isCollectDev = true;
                                    break;
                                }
                            }
                            if(ishave){ //如果点对点列表中存在服务端,就主动连接
                                WifiP2pConfig config = new WifiP2pConfig();
                                config.deviceAddress = qrcode;
                                config.groupOwnerIntent = 1; //因为是客户端,所以设置为1
                                config.wps.setup = WpsInfo.PBC;
                                manager.connect(channel, config, new WifiP2pManager.ActionListener() {
                                    @Override
                                    public void onSuccess() {
                                        Log.e("mylog","启动connect线程成功");
                                    }
                                    @Override
                                    public void onFailure(int i) {
                                        MyLog.e("mylog","启动connect线程失败:"+i);
                                    }
                                });
                            }
                        }
                    });
                }catch (SecurityException e){
                    e.printStackTrace();
                }
            }
        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals (action)) {
            //建立或者断开连接
            MyLog.e("mylog","wifi direct 连接或者断开");
            if (manager == null) {
                return;
            }
            NetworkInfo networkInfo = (NetworkInfo) intent
                    .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
            if (networkInfo.isConnected()) {
                MyLog.e("mylog","WIFI Direct 连接!");
                Dialog.hideWaitDialog();
                if(!DownloadData.getQRCode_Str().equals("")){//通过扫码连接成功
                    manager.requestConnectionInfo(channel, new WifiP2pManager.ConnectionInfoListener() {
                        @Override
                        public void onConnectionInfoAvailable(WifiP2pInfo wifiP2pInfo) {
                            broadCallbackListener.updateStatus("成功","成功");
                            if(wifiP2pInfo.groupFormed && wifiP2pInfo.isGroupOwner){
                                MyLog.e("mylog","群主");
                            }else if(wifiP2pInfo.groupFormed){
                                //isConnectServer = true;
                                MyLog.e("mylog","组员");
                            }
                        }
                    });
                }
            }else{
                MyLog.e("mylog","WIFI Direct 断开!");
                broadCallbackListener.updateStatus("断开","断开");
            }

        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals (action)) {
            //当前设备的 WIFI 状态发生变化
            MyLog.e("mylog","获取设备信息");
            WifiP2pDevice device = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
            broadCallbackListener.getMyMsg(device);
        }
    }
}
2、获取连接点列表

通过调用获取连接点列表,来触发广播,从而连接到服务端

try {
            mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
                @Override
                public void onSuccess() {
                    MyLog.e("mylog", "启动获取列表线程成功");
                }

                @Override
                public void onFailure(int reasonCode) {
                    switch (reasonCode) {
                        case WifiP2pManager.ERROR:
                            MyLog.e("mylog", "启动获取列表线程失败:error");
                            break;
                        case WifiP2pManager.P2P_UNSUPPORTED:
                            MyLog.e("mylog", "启动获取列表线程失败:P2P unsupported");
                            break;
                        case WifiP2pManager.BUSY:
                            MyLog.e("mylog", "启动获取列表线程失败:busy");
                            break;
                        default:
                            MyLog.e("mylog", "启动获取列表线程失败");
                            break;
                    }
                }
            });
        } catch (SecurityException e) {
            e.printStackTrace();
        }
3、初始化客户端socket

客户端socket类

public class SocketClient {
    private Socket client;
    private Context context;
    private int port;           //IP
    private String site;            //端口
    public static Handler mHandler;
    private boolean isClient=false;
    private OutputStream out;
    private Activity activity;
    private Timer timer = new Timer();
    private TimerTask task;
    private SocektClientCallback clientCallback;
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
           if(msg.what == 1)
               clientCallback.reStartClientThread();
            return false;
        }
    });

    public SocketClient(Activity activity, SocektClientCallback clientCallback){
        this.activity = activity;
        this.clientCallback = clientCallback;
    }

    /**
     * @effect 开启线程建立连接开启客户端
     * */
    public void openClientThread(String str){
        LocalThreadPools.getInstance(activity).execute(new Runnable() {
            @Override
            public void run() {
                try {
                    /**
                     *  connect()步骤
                     * */
                    client=new Socket ( site,port );
                    MyLog.e("mylog","创建客户端:"+client);
//                    client.setSoTimeout ( 5000 );//设置超时时间
                    if (client!=null) {
                        isClient=true;
                        forOut();
                        sendMsg(str);
                        sendBeatData();
                        Dialog.hideWaitDialog();
                        forIn(); //无限循环
                    }else {
                        isClient=false;
                        Toast.makeText ( context,"网络连接失败", Toast.LENGTH_LONG ).show ();
                    }
                }catch (Exception e) {
                    e.printStackTrace ();
                    Dialog.hideWaitDialog();
                }
            }
        });
    }

    /**
     * 调用时向类里传值
     * */
    public void clintValue(Context context, String site, int port)
    {
        this.context=context;
        this.site=site;
        this.port=port;
    }

    /**
     * @effect 得到输出字符串
     * */
    public void forOut()
    {
        try {
            out= client.getOutputStream ();
        }catch (IOException e){
            e.printStackTrace ();
            Log.i ( "socket","8" );
        }
    }

    /**
     * @steps read();
     * @effect 得到输入字符串
     * */
    public void forIn(){
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    client.getInputStream(), "utf-8"));
            String content = null;
            MyLog.e("mylog","开启for循环");
            while ((content = br.readLine()) != null) {
                Message msg = new Message ( );
                msg.obj =content ;
                mHandler.sendMessage ( msg );
            }
        } catch (Exception e) {
            e.printStackTrace();//todo 可以检测到客户端socket断开,然后重连
            handler.sendEmptyMessage(1);
            MyLog.e("mylog","客户端获取数据失败:"+e.getMessage());
        }
    }

    /**
     * @steps write();
     * @effect 发送消息
     * */
    public void sendMsg(final String str) {
        Log.e("mylog","开启发送信息线程:"+str);
        LocalThreadPools.getInstance(activity).execute(new Runnable() {
            @Override
            public void run() {
                if (client!=null) {
                    try {
                        out.write ( (str+"\n").getBytes("UTF-8"));
                        out.flush();
                        Log.e ( "outtt",out+"" );
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }else{
                    isClient=false;
                    Log.e("mylog","网络连接失败");
                }
            }
        });
    }

    //发送心跳
    private void sendBeatData() {
        if (timer == null) {
            timer = new Timer();
        }

        if (task == null) {
            Log.e("mylog","创建心跳");
            task = new TimerTask() {
                @Override
                public void run() {
                    if (client!=null) {
                        try {
                            /*这里的编码方式根据你的需求去改*/
                            out.write ( ("------------------------------------------------❤"+"\n")
                                    .getBytes("UTF-8"));
                            out.flush();
                        } catch (Exception e) {
                            /*发送失败说明socket断开了或者出现了其他错误*/
                            /*重连*/
                            e.printStackTrace();
                        }
                    }else{
                        Log.e("mylog","心跳程序:client is null");
                    }
                }
            };
        }
        timer.schedule(task, 60*1000, 60*1000); //延迟一分钟,每隔一分钟执行初次
    }

    public void closeClientSocket() throws IOException {
        client.shutdownInput();
        client.close();
    }
}

初始化socket类

private void initWifiDirectClient() {
        MyLog.e("mylog", "初始化wifi direct");
        client = new SocketClient(FlightConfigActivity.this, restart);
        //接受消息
        SocketClient.mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                String con = msg.obj.toString();
                MyLog.e("mylog", "客户端接收消息:" + con);
                mPresenter.delServiceMsg(con);
            }
        };
    }

在wifi直连连接成功的情况下,连接socket

public void startClientThread() {
        try {
            String ip = "192.168.49.1";
            //服务端的IP地址和端口号
            client.clintValue(context, ip, port);
            ClientOpen = true;
            //开启客户端接收消息线程
            client.openClientThread(myApplication.getPHONE_ID());
            isCreateSocket = true;
            Tools.showToast(context, "连接成功!");
        } catch (Exception e) {
            isCreateSocket = false;
            Toast.makeText(context, "请检查ip及地址", Toast.LENGTH_SHORT).show();
            Dialog.hideWaitDialog();
            e.printStackTrace();
        }
    }

客户端给服务端发送消息

SocketCliet 的sendMsg方法

心得

1、无法获取WIFI直连MAC地址

客户端连接服务端,需要知道服务端的 直连MAC地址(注意 直连MAC地址与WIFIMAC地址不同),直连MAC地址是在直连广播WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION中获取

if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals (action)) {
            //当前设备的 WIFI 状态发生变化
            MyLog.e("mylog","获取wifi direct 信息");
            WifiP2pDevice device = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
            //直连mac地址
            String wifidirect_mac = device.deviceAddress;
        }

但是有些手机无法触发该广播或者无法获取wifi直连MAC地址。Android10版本的手机就无法获取

2、两个手机无法连接wifi直连

在使用中发现,一些手机无法获取连接点列表,或者返回的列表大小为0.
还有些情况获取到列表,也能看到对方,但是通过manager.connect方法无法连接。
针对这种情况,我处理的一种方式就是 自动跳转到WiFi界面,提示主动找到wifi直连并连接目标手机。然后在创建socket通信。

3、蓝牙影响wifi直连广播

之前的项目是 蓝牙和WiFi都是用。仅打开wifi情况下,使用wifi直连通信正常。打开蓝牙后,使用wifi直连发现无法获取相应广播,导致WiFi直连无法使用。这种情况必须关闭蓝牙才行。

4、服务端无法同时处理多个客户端请求

服务端的数据处理是在主线程中,所以如果多个客户端同时请求服务端,可能需要排队,逐个处理。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值