背景:
两个设备,其中一个设备开启热点,另外一个手设备接热点,两个设备间进行数据通信(不需要流量)。原理:开热点的设备相当于路由器,作为服务端,连接的设备作为客户端,获取路由器的IP建立Socket 连接,开始双方通信。
实现:
一、服务端
1.创建一个SocketAdmin来管理通讯连接
public class SocketAdmin {
private final static String TAG = "SocketAdmin";
public static SocketAdmin socketAdmin;
private EncryptEcbBean mEncryptEcbBean;
private ConnectThread mConnectThread;
private ListenerThread mListenerThread;
private WifiApInfo mWifiApInfo;
private boolean mIsHeart = false; // 心跳线程运行状态
private boolean mIsReceive = false; // 心跳监控线程状态
private long mStartTime = 0; // 接收心跳标志位
private Thread mHeartThread;
private Thread mReceiveThread;
public static SocketAdmin getInstance() {
if (socketAdmin == null) {
socketAdmin = new SocketAdmin();
}
return socketAdmin;
}
public SocketAdmin() {
getEncryptBean();
mWifiApInfo = new WifiApInfo();
}
@SuppressLint("HandlerLeak")
private final Handler mHandler = new Handler() {
@Override
public void handleMessage( Message msg) {
String message = "";
String data = "";
switch (msg.what) {
// 有设备正在连接热点
case WifiAp.DEVICE_CONNECTING:
Bundle bundle = msg.getData();
String ip = bundle.getString("ip");
Log.i(TAG, "ip:" + ip + "尝试连接");
if (mConnectThread != null) {
stopConnectThread();
}
if (mListenerThread != null) {
startConnectThread(mListenerThread.getSocket(), this);
}
break;
// 有设备连上热点
case WifiAp.DEVICE_CONNECTED:
Log.i(TAG, "设备连接成功");
mWifiApInfo.setRemoteControlAp(1);
startHeartThread();
startReceiveThread();
break;
// 发送消息成功
case WifiAp.SEND_MSG_SUCCESS:
message = msg.getData().getString("MSG");
data = getMsg(message);
Log.i(TAG, "发送消息成功:" + data);
break;
// 发送消息失败
case WifiAp.SEND_MSG_ERROR:
message = msg.getData().getString("MSG");
data = getMsg(message);
Log.i(TAG, "发送消息失败:" + data);
break;
// 获取新消息
case WifiAp.GET_MSG:
message = msg.getData().getString("MSG");
data = getMsg(message);
Log.i(TAG, "收到消息:" + data);
if (Objects.equals(data, WifiAp.HEART_SERVER)) {
mStartTime = System.currentTimeMillis();
}
break;
}
}
};
/**
* 心跳线程
*/
private void getHeardThread() {
stopHeartThread();
mIsHeart = true;
mHeartThread = new Thread(new Runnable() {
@Override
public void run() {
long startTime = System.currentTimeMillis();
while (mIsHeart) {
// 每隔4s发送心跳
if (System.currentTimeMillis() - startTime > 4000) {
startTime = System.currentTimeMillis();
sendMsg(WifiAp.HEART_SERVER);
}
}
}
});
}
/**
* 心跳接收线程
*/
private void getReceiveThread() {
stopReceiveThread();
mIsReceive = true;
mReceiveThread = new Thread(new Runnable() {
@Override
public void run() {
mStartTime = System.currentTimeMillis();
while (mIsReceive) {
// 若超时未检测到客户端发送的心跳,则变更通讯连接状态
if (System.currentTimeMillis() - mStartTime > 5000) {
Log.i(TAG, "Socket断开连接");
mIsReceive = false;
// 变更通讯连接状态为已断开
mWifiApInfo.setRemoteControlAp(2);
stopHeartThread();
stopConnectThread();
}
}
}
});
}
public ConnectThread getConnectThread() {
return mConnectThread;
}
public ListenerThread getListenerThread() {
return mListenerThread;
}
public WifiApInfo getWifiApInfo() {
return mWifiApInfo;
}
public void setWifiApInfo(WifiApInfo wifiApInfo) {
this.mWifiApInfo = wifiApInfo;
}
public void setStartTime(long mStartTime) {
this.mStartTime = mStartTime;
}
/**
* 初始化当前热点信息
*/
public void initWifiIo() {
if (WifiAdmin.getInstance().isApOn()) {
WifiConfiguration config = WifiAdmin.getInstance().getWifiApConfiguration();
if (config != null) {
mWifiApInfo.setWifiInfo(config.SSID, config.preSharedKey);
} else {
mWifiApInfo.setWifiInfo("", "");
}
} else {
mWifiApInfo.setWifiInfo("", "");
}
}
/**
* 开启Socket连接监听线程
*/
public void startListenerThread() {
stopListenerThread();
mListenerThread = null;
mListenerThread = new ListenerThread(WifiAp.PORT, mHandler);
mListenerThread.start();
}
/**
* 关闭Socket连接监听线程
*/
public void stopListenerThread() {
if (mListenerThread != null) {
mListenerThread.setRunning(false);
try {
mListenerThread.join();
} catch (InterruptedException i) {
Log.e(TAG, i.toString());
}
mListenerThread.close();
}
}
/**
* 开启Socket连接线程
*
*/
public void startConnectThread(Socket socket, Handler handler) {
stopConnectThread();
mConnectThread = null;
mConnectThread = new ConnectThread(socket, handler);
mConnectThread.start();
}
/**
* 关闭Socket连接线程
*
*/
public void stopConnectThread() {
if (mConnectThread != null) {
mConnectThread.setRunning(false);
try {
mConnectThread.join();
} catch (InterruptedException i) {
Log.e(TAG, i.toString());
}
mConnectThread.close();
}
}
/**
* 初始化对称加密算法
*/
private boolean getEncryptBean() {
if (mEncryptEcbBean != null) {
return true;
}
try {
mEncryptEcbBean = new EncryptEcbBean();
return true;
} catch (Exception e) {
Log.e(TAG, e.toString());
Log.i(TAG, "加密初始化失败");
return false;
}
}
/**
* 开启心跳线程
*/
public void startHeartThread() {
getHeardThread();
mHeartThread.start();
}
/**
* 关闭心跳线程
*/
public void stopHeartThread() {
mIsHeart = false;
try {
if (mHeartThread != null) {
mHeartThread.join();
mHeartThread = null;
}
} catch (InterruptedException i) {
Log.e(TAG, i.toString());
}
}
/**
* 开启心跳监控线程
*/
public void startReceiveThread() {
getReceiveThread();
mReceiveThread.start();
}
/**
* 关闭心跳监控线程
*/
public void stopReceiveThread() {
mIsReceive = false;
try {
if (mReceiveThread != null) {
mReceiveThread.join();
mReceiveThread = null;
}
} catch (InterruptedException i) {
Log.e(TAG, i.toString());
}
}
/**
* 发送消息
* @param msg 消息
* @return 结果
*/
public boolean sendMsg(String msg) {
if (!getEncryptBean()) {
return false;
}
if (mConnectThread == null) {
Log.e(TAG, "发送消息:mConnectThread未初始化");
return false;
}
try {
byte[] msgBytes = msg.getBytes(StandardCharsets.UTF_8);
byte[] encrypted = mEncryptEcbBean.encrypt(msgBytes);
String data = ByteUtils.bytesToHexString(encrypted);
Log.i(TAG, "encrypted(加密):" + data);
mConnectThread.sendData(data);
return true;
} catch (Exception e) {
Log.e(TAG, e.toString());
return false;
}
}
/**
* 获取消息
* @param msg 消息(加密)
* @return 消息(解密)
*/
public String getMsg(String msg) {
if (!getEncryptBean()) {
return "";
}
try {
byte[] bytes1 = ByteUtils.hexStringToBytes(msg);
byte[] bytes2 = mEncryptEcbBean.decrypt(bytes1);
return new String(bytes2, StandardCharsets.UTF_8);
} catch (Exception e) {
Log.e(TAG, e.toString());
return "";
}
}
public void close() {
mWifiApInfo.setRemoteControlAp(2);
stopHeartThread();
stopReceiveThread();
stopConnectThread();
stopListenerThread();
mConnectThread = null;
mListenerThread = null;
mHandler.removeCallbacksAndMessages(null);
}
}
其中对发送和接收的数据使用了对称加密算法,如不需要直接删除即可。
2.WifiAp 如下。
public class WifiAp {
public static final int PORT = 54321;
public static final int DEVICE_CONNECTING = 1; // 有设备正在连接热点
public static final int DEVICE_CONNECTED = 2; // 有设备连上热点
public static final int SEND_MSG_SUCCESS = 3; // 发送消息成功
public static final int SEND_MSG_ERROR = 4; // 发送消息失败
public static final int GET_MSG = 6; // 获取新消息
public final static String HEART_SERVER = "HEART_SERVER"; // 心跳
}
3.ListenerThread 通讯监听线程如下。
public class ListenerThread extends Thread {
private final static String TAG = "ListenerThread";
private ServerSocket serverSocket = null;
private Handler handler;
private Socket socket;
private boolean isRunning = true;
public ListenerThread(int port, Handler handler) {
setName(TAG);
this.handler = handler;
try {
serverSocket = new ServerSocket(port);
} catch (IOException e) {
e.printStackTrace();
}
}
public void setRunning(boolean running) {
isRunning = running;
}
@Override
public void run() {
while (isRunning) {
try {
Log.i(TAG, "阻塞");
//阻塞,等待设备连接
if (serverSocket != null)
socket = serverSocket.accept();
String ip = socket.getInetAddress().getHostAddress();
Log.i(TAG, "ip:" + ip + "设备尝试连接");
Message message = Message.obtain();
message.what = WifiAp.DEVICE_CONNECTING;
Bundle bundle = new Bundle();
bundle.putString("ip", ip);
message.setData(bundle);
handler.sendMessage(message);
} catch (IOException e) {
Log.i(TAG, "error:" + e.getMessage());
e.printStackTrace();
}
}
}
public Socket getSocket() {
return socket;
}
public void close() {
try {
isRunning = false;
if (socket != null) {
socket.close();
}
if (serverSocket != null) {
serverSocket.close();
}
if (handler != null) {
handler.removeCallbacksAndMessages(null);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.连接线程如下。
public class ConnectThread extends Thread {
private final static String TAG = "ConnectThread";
private final Socket socket;
private Handler handler;
private InputStream inputStream;
private OutputStream outputStream;
private boolean isRunning = true;
public ConnectThread(Socket socket, Handler handler) {
setName(TAG);
Log.i(TAG, TAG);
this.socket = socket;
this.handler = handler;
}
public void setRunning(boolean running) {
isRunning = running;
}
@Override
public void run() {
if (socket == null) {
return;
}
handler.sendEmptyMessage(WifiAp.DEVICE_CONNECTED);
try {
//获取数据流
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
byte[] buffer = new byte[1024];
int bytes;
while (isRunning) {
//读取数据
bytes = inputStream.read(buffer);
if (bytes > 0) {
final byte[] data = new byte[bytes];
System.arraycopy(buffer, 0, data, 0, bytes);
Message message = Message.obtain();
message.what = WifiAp.GET_MSG;
Bundle bundle = new Bundle();
bundle.putString("MSG", new String(data));
message.setData(bundle);
handler.sendMessage(message);
Log.i(TAG, "读取到数据:" + new String(data));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发送数据
*/
public void sendData(String msg) {
Log.i(TAG, "发送数据:" + (outputStream == null));
if (outputStream != null) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
outputStream.write(msg.getBytes());
Log.i(TAG, "发送消息:" + msg);
Message message = Message.obtain();
message.what = WifiAp.SEND_MSG_SUCCESS;
Bundle bundle = new Bundle();
bundle.putString("MSG", new String(msg));
message.setData(bundle);
handler.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
Message message = Message.obtain();
message.what = WifiAp.SEND_MSG_ERROR;
Bundle bundle = new Bundle();
bundle.putString("MSG", new String(msg));
message.setData(bundle);
handler.sendMessage(message);
}
}
});
thread.start();
}
}
public boolean close() {
try {
isRunning = false;
if (socket != null) {
socket.close();
}
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
if (handler != null) {
handler.removeCallbacksAndMessages(null);
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
}
5.在确保热点打开的情况下,只需在MainActivity中开启监听线程即可。
SocketAdmin.getInstance().startListenerThread()
6.在MainActivity中发送数据
/**
* 判断socket是否连接
*/
private boolean isSocketEnabled() {
if (SocketAdmin.getInstance().getWifiApInfo().getRemoteControlAp() != 1) {
ToastUtil.showToast("未建立通信连接,请先连接", 2000);
return false;
}
return true;
}
private void initClick() {
mBinding.btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isSocketEnabled()) {
return;
}
String msg = "test";
if (SocketAdmin.getInstance().sendMsg(msg)) {
Log.i(TAG, "发送成功");
} else {
Log.i(TAG, "发送失败");
}
}
});
}
二、客户端
1.创建一个SocketAdmin来管理通讯连接
public class SocketAdmin {
private final static String TAG = "SocketAdmin";
public static SocketAdmin socketAdmin;
private EncryptEcbBean mEncryptEcbBean;
private ConnectThread mConnectThread;
private ListenerThread mListenerThread;
private WifiApInfo mWifiApInfo;
private boolean mIsHeart = false; // 心跳线程运行状态
private boolean mIsReceive = false; // 心跳监控线程状态
private long mStartTime = 0; // 接收心跳标志位
private Thread mHeartThread;
private Thread mReceiveThread;
public static SocketAdmin getInstance() {
if (socketAdmin == null) {
socketAdmin = new SocketAdmin();
}
return socketAdmin;
}
public SocketAdmin() {
getEncryptBean();
mWifiApInfo = new WifiApInfo();
}
@SuppressLint("HandlerLeak")
private final Handler mHandler = new Handler() {
@Override
public void handleMessage( Message msg) {
String message = "";
String data = "";
switch (msg.what) {
case WifiAp.DEVICE_CONNECTED:
Log.i(TAG, "设备连接成功");
mWifiApInfo.setRemoteControlAp(1, false);
startReceiveThread();
break;
case WifiAp.SEND_MSG_SUCCESS:
message = msg.getData().getString("MSG");
data = getMsg(message);
Log.i(TAG, "发送消息成功:" + data);
break;
case WifiAp.SEND_MSG_ERROR:
message = msg.getData().getString("MSG");
data = getMsg(message);
Log.i(TAG, "发送消息失败:" + data);
break;
case WifiAp.GET_MSG:
message = msg.getData().getString("MSG");
data = getMsg(message);
Log.i(TAG, "收到消息:" + data);
if (Objects.equals(data, WifiAp.HEART_SERVER)) {
setStartTime(System.currentTimeMillis());
sendMsg(WifiAp.HEART_SERVER);
}
break;
}
}
};
private void getHeardThread() {
stopHeartThread();
mIsHeart = true;
mHeartThread = new Thread(new Runnable() {
@Override
public void run() {
long startTime = System.currentTimeMillis();
while (mIsHeart) {
if (System.currentTimeMillis() - startTime > 4000) {
startTime = System.currentTimeMillis();
sendMsg(WifiAp.HEART_SERVER);
mWifiApInfo.setReConnect(false);
}
}
}
});
}
private void getReceiveThread() {
stopReceiveThread();
mIsReceive = true;
mReceiveThread = new Thread(new Runnable() {
@Override
public void run() {
mStartTime = System.currentTimeMillis();
while (mIsReceive) {
if (System.currentTimeMillis() - mStartTime > 5000) {
Log.i(TAG, "Socket断开连接");
mIsReceive = false;
mWifiApInfo.setRemoteControlAp(2, false);
stopConnectThread();
}
}
}
});
}
public WifiApInfo getWifiApInfo() {
return mWifiApInfo;
}
public ConnectThread getConnectThread() {
return mConnectThread;
}
public ListenerThread getListenerThread() {
return mListenerThread;
}
public void setStartTime(long mStartTime) {
this.mStartTime = mStartTime;
}
public void startListenerThread(Handler handler) {
stopListenerThread();
mListenerThread = null;
mListenerThread = new ListenerThread(WifiAp.PORT, handler);
mListenerThread.start();
}
public void stopListenerThread() {
if (mListenerThread != null) {
mListenerThread.setRunning(false);
try {
mListenerThread.join();
} catch (InterruptedException i) {
Log.e(TAG, i.toString());
}
mListenerThread.close();
}
}
public void startConnectThread(Socket socket) {
mConnectThread = null;
mConnectThread = new ConnectThread(socket, mHandler);
mConnectThread.start();
}
public void stopConnectThread() {
if (mConnectThread != null) {
mConnectThread.setRunning(false);
try {
mConnectThread.join();
} catch (InterruptedException i) {
Log.e(TAG, i.toString());
}
mConnectThread.close();
}
}
private boolean getEncryptBean() {
if (mEncryptEcbBean != null) {
return true;
}
try {
mEncryptEcbBean = new EncryptEcbBean();
return true;
} catch (Exception e) {
Log.e(TAG, e.toString());
Log.i(TAG, "加密初始化失败");
return false;
}
}
/**
* 开启心跳线程
*/
public void startHeartThread() {
getHeardThread();
mHeartThread.start();
}
/**
* 关闭心跳线程
*/
public void stopHeartThread() {
mIsHeart = false;
try {
if (mHeartThread != null) {
mHeartThread.join();
mHeartThread = null;
}
} catch (InterruptedException i) {
Log.e(TAG, i.toString());
}
}
/**
* 开启心跳监控线程
*/
public void startReceiveThread() {
getReceiveThread();
mReceiveThread.start();
}
/**
* 关闭心跳监控线程
*/
public void stopReceiveThread() {
mIsReceive = false;
try {
if (mReceiveThread != null) {
mReceiveThread.join();
mReceiveThread = null;
}
} catch (InterruptedException i) {
Log.e(TAG, i.toString());
}
}
/**
* 发送消息
* @param msg 消息
* @return 结果
*/
public boolean sendMsg(String msg) {
if (!getEncryptBean()) {
return false;
}
if (mConnectThread == null) {
Log.e(TAG, "发送消息:mConnectThread未初始化");
return false;
}
try {
byte[] msgBytes = msg.getBytes(StandardCharsets.UTF_8);
byte[] encrypted = mEncryptEcbBean.encrypt(msgBytes);
String data = ByteUtils.bytesToHexString(encrypted);
Log.i(TAG, "encrypted(加密):" + data);
mConnectThread.sendData(data);
return true;
} catch (Exception e) {
Log.e(TAG, e.toString());
return false;
}
}
/**
* 获取消息
* @param msg 消息(加密)
* @return 消息(解密)
*/
public String getMsg(String msg) {
if (!getEncryptBean()) {
return "";
}
try {
byte[] bytes1 = ByteUtils.hexStringToBytes(msg);
byte[] bytes2 = mEncryptEcbBean.decrypt(bytes1);
return new String(bytes2, StandardCharsets.UTF_8);
} catch (Exception e) {
Log.e(TAG, e.toString());
return "";
}
}
public void close() {
mWifiApInfo.setRemoteControlAp(2, false);
stopConnectThread();
stopListenerThread();
stopHeartThread();
stopReceiveThread();
mConnectThread = null;
mListenerThread = null;
}
}
2.ConnectThread线程和上面一致,客户端无需使用ListenerThread线程。
3.确保已连接指定热点的情况下,在MainActivity中直接调用连接线程即可。
private void connectSocket() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Socket socket = new Socket(WifiAdmin.getInstance().getWifiRouteIPAddress(), WifiAp.PORT);
SocketAdmin.getInstance().startConnectThread(socket);
} catch (IOException e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
ToastUtil.showToast("Socket通信连接失败", 2000);
}
});
}
}
}).start();
}
/**
* wifi获取已连接网络路由ip地址
*
*/
public String getWifiRouteIPAddress() {
WifiManager wifiManager = (WifiManager) Utils.getApp().getApplicationContext().getSystemService(Context.WIFI_SERVICE)
DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
String routeIp = Formatter.formatIpAddress(dhcpInfo.gateway);
Log.i("route ip", "wifi route ip:" + routeIp);
return routeIp;
}
4.在MainActivity中发送数据
/**
* 判断socket是否连接
*/
private boolean isSocketEnabled() {
if (SocketAdmin.getInstance().getWifiApInfo().getRemoteControlAp() != 1) {
ToastUtil.showToast("未建立通信连接,请先连接", 2000);
return false;
}
return true;
}
private void initClick() {
mBinding.btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isSocketEnabled()) {
return;
}
String msg = "test";
if (SocketAdmin.getInstance().sendMsg(msg)) {
Log.i(TAG, "发送成功");
} else {
Log.i(TAG, "发送失败");
}
}
});
}