文章目录
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、服务端无法同时处理多个客户端请求
服务端的数据处理是在主线程中,所以如果多个客户端同时请求服务端,可能需要排队,逐个处理。