文章目录

  • Wifi直连 开发
    • 背景
    • WiFi直连 简介
    • 相关权限
    • Wifi直连 服务端
      • 1、初始化直连及广播
      • 2、获取连接点列表
      • 3、Socket初始化
      • 4、创建组和移除组
    • Wifi直连 客户端
      • 1、初始化直连及广播、
      • 2、获取连接点列表
      • 3、初始化客户端socket
    • 心得
      • 1、无法获取WIFI直连MAC地址
      • 2、两个手机无法连接wifi直连
      • 3、蓝牙影响wifi直连广播
      • 4、服务端无法同时处理多个客户端请求

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() {@Overridepublic void reStartClientThread() {}@Overridepublic void socketStatus(String mac, boolean iscollect) { //iscollect 是否连接socket//有用户sockt退出或者加入//更新连接用户列表adapter.notifyItemChanged(ii, iscollect); }};private BroadCallbackListener broadCallbackListener = new BroadCallbackListener() {@Overridepublic void getMyMsg(WifiP2pDevice device) {}@Overridepublic void updateList(ArrayList<WifiP2pDevice> list) {MyLog.e("mylog", "刷新连接用户的列表:" + list.size());//更新直连 连接列表adapter.notifyDataSetChanged();}@Overridepublic 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;}@Overridepublic 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() {@Overridepublic 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@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void onSuccess() {MyLog.e("mylog", "启动获取列表线程成功");}@Overridepublic 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() {@Overridepublic boolean handleMessage(@NonNull Message msg) {String mac = (String)msg.obj;boolean iscollect;if(msg.what == 1)iscollect = true;elseiscollect = false;MyLog.e("mylog","socket 状态:"+iscollect+",mac:"+mac);socketChange.socketStatus(mac,iscollect);//断开socketreturn 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() {@Overridepublic 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 ( ) {@Overridepublic 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") );}elseout.write ( (chat+"\n").getBytes("utf-8") );out.flush ();} catch (IOException e) {e.printStackTrace ();//todo 这里能知道是否断开,但是需要服务端主动发送消息//如何客户端关闭,返回:Socket is closedMyLog.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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void onSuccess() {MyLog.e("mylog", "创建组成功");Tools.showToast(context, "创建组成功");}@Overridepublic void onFailure(int reason) {MyLog.e("mylog", "创建组失败:" + reason);Tools.showToast(context, "创建组失败");}});}public void removeGroup() {mManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {@Overridepublic void onSuccess() {Log.e("mylog","移除组成功");Tools.showToast(context,"移除组成功");}@Overridepublic void onFailure(int reason) {Log.e("mylog","移除组失败:"+reason);Tools.showToast(context,"移除组失败");}});}

Wifi直连 客户端

1、初始化直连及广播、

定义回调及处理

private SocektClientCallback restart = new SocektClientCallback() {@Overridepublic void reStartClientThread() {//startClientThread();MyLog.e("mylog", "客户端退出了,需要重连");isCreateSocket = false;}@Overridepublic void socketStatus(String mac, boolean b) {}};private BroadCallbackListener broadCallbackListener = new BroadCallbackListener() {@Overridepublic void getMyMsg(WifiP2pDevice device) {/*MyAddress = device.deviceAddress;MyName = device.deviceName;*/}@Overridepublic void updateList(ArrayList<WifiP2pDevice> list) {}@Overridepublic 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;}@Overridepublic 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() {@Overridepublic 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; //因为是客户端,所以设置为1config.wps.setup = WpsInfo.PBC;manager.connect(channel, config, new WifiP2pManager.ActionListener() {@Overridepublic void onSuccess() {Log.e("mylog","启动connect线程成功");}@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void onSuccess() {MyLog.e("mylog", "启动获取列表线程成功");}@Overridepublic 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;           //IPprivate 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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、服务端无法同时处理多个客户端请求

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

WiFi_Direct 直连开发实战相关推荐

  1. [Python3网络爬虫开发实战] 7-动态渲染页面爬取-4-使用Selenium爬取淘宝商品

    在前一章中,我们已经成功尝试分析Ajax来抓取相关数据,但是并不是所有页面都可以通过分析Ajax来完成抓取.比如,淘宝,它的整个页面数据确实也是通过Ajax获取的,但是这些Ajax接口参数比较复杂,可 ...

  2. 【Python3网络爬虫开发实战】3-基本库的使用 1.2-处理异常

    前一节我们了解了请求的发送过程,但是在网络不好的情况下,如果出现了异常,该怎么办呢?这时如果不处理这些异常,程序很可能因报错而终止运行,所以异常处理还是十分有必要的. urllib的error模块定义 ...

  3. 微信小程序|开发实战篇之五-slide-view滑动菜单组件

    开发实战篇之五 前言 1.微信小程序操作dom元素 1.1 slide-view组件的wxml骨架文件 1.1.1 涉及movable-view组件属性 1.2 slide-view组件的js文件 1 ...

  4. 微信小程序|开发实战篇之二

    开发实战篇之二 前言 1.零碎知识点和优化点 1.1 ES6模板字符串 1.2 ES6扩展运算符 1.3 独立更新like组件状态 1.4 自定义组件支持hidden 2.音乐music组件开发 2. ...

  5. 【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

    文章目录 一.前言 二.实现方案 1.无主之地,第一人称视角 2.我之前做的摇杆控制 3.第一人称视角 + 摇杆控制 三.开始实战 1.资源获取:Unity AssetStore 2.Low Poly ...

  6. 【SQL开发实战技巧】系列(二):简单单表查询

    系列文章目录 [SQL开发实战技巧]系列(一):关于SQL不得不说的那些事 [SQL开发实战技巧]系列(二):简单单表查询 [SQL开发实战技巧]系列(三):SQL排序的那些事 [SQL开发实战技巧] ...

  7. 数控技术课设:数控系统刀补功能的软件实现及其仿真--数控仿真程序开发实战

    数控技术:数控系统刀补功能的软件实现及其仿真–数控仿真程序开发实战 主要功能是传入一个.dxf文件,可以读取该文件显示其外形轮廓,然后通过数控技术的逐点比较法对该路径进行插补,还可以对其进行刀补路径的 ...

  8. Django企业开发实战 高效Python Web框架指南 笔记 (一)

    Django企业开发实战 高效Python Web框架指南 笔记 (一) 内容: 作者是 the5fire,他的博客地址:https://www.the5fire.com/957.html 2016年 ...

  9. Spring 3.x企业应用开发实战

    Java技术大系 Spring 3.x企业应用开发实战 陈雄华    林开雄      著 Publishing House ofElectronics Industry 北京·  BEIJING 内 ...

最新文章

  1. 数据结构与算法 / 总章
  2. Linux恢复win分区,找到了linux分区顺序错乱修复方法
  3. 谷歌已推送 Android Q Beta 1
  4. 一个简单的crontab
  5. sed系列:行或者模式匹配删除特定行
  6. springboot启动的时候运行一些代码
  7. python中http_Python中的HTTP错误
  8. MyBatisPlus中的TypeHandler
  9. WS小世界网络模型构造算法
  10. 台式计算机内置无线网卡,台式机内置无线网卡和外置的区别
  11. 跳过Nexus7第一次开机设置的网络验证
  12. python爬虫扇贝单词库
  13. 学以致用:C语言能干点儿啥?
  14. Mybatis-Plus如何使用
  15. 屏幕翻拍_带有现代翻拍的前5大经典合作游戏
  16. 4g物联网卡助力“现代农业”发展壮大
  17. 海信研发前端工程师面试经验总结
  18. 社会财富分配问题模拟(蒙特卡洛思想)
  19. 小米(红米)AC2100 硬路由配置的坑
  20. P型半导体,N型半导体,PN结原理简述

热门文章

  1. 相似性度量方法(欧式距离等各种距离)
  2. 神经网络和深度学习(5)-- 逻辑回归
  3. 虚拟机如何访问主机服务器,主机怎么访问虚拟机
  4. 数据分析综述:联邦学习中的数据安全和隐私保护问题
  5. Spring 源码分析 (一)——迈向 Spring 之路
  6. 北京海纳互联网研究中心:中国数字对讲机市场纷杂,MOTO继续领跑高端
  7. 网站加速最佳实践 – 减少DNS查找
  8. 快慢指针(删除数组重复项)
  9. php larval框架运行环境,Laravel框架的运行环境配置(一)
  10. VC基于HOOPS开发3D浏览器