WiFi_Direct 直连开发实战
文章目錄
- 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 直连开发实战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小米模式—互联网思维下的中国“智造”
- 下一篇: STM32学习笔记---GPIO