有时候我们的程序要求socket一直保持连接,并且希望在socket断开以后能够重新连接,这个时候就需要用到心跳机制,所谓心跳机制,最简单的做法就是客户端每隔一段时间向服务端发送数据包,为了节约资源我们很多时候发送空数据就好,如果数据不能发送成功说明socket已经断开,这个时候就需要根据具体需求释放资源和重新连接了。
下面给出一个简单的小demo
/**
* 连接服务端
*/
private void connectToServer() {
Thread connectThread = new Thread(new Runnable() {
public void run() {
try {
mSocket = new Socket();
mSocket.connect(
new InetSocketAddress(SOCKET_HOST, SOCKET_PORT));
Log.e(TAG, "连接成功 " + SOCKET_HOST);
mDataOutputStream = new DataOutputStream(
mSocket.getOutputStream());
// 开启线程负责读取服务端数据
mReadThread = new SocketReadThread();
mReadThread.start();
// 心跳检测,检测socket是否连接
mHandler.postDelayed(mHeartBeatRunnable, HEART_BEAT_RATE);
} catch (UnknownHostException e) {
Log.e(TAG, "连接失败 ");
e.printStackTrace();
} catch (IOException e) {
Log.e(TAG, "连接失败 ");
e.printStackTrace();
}
}
});
connectThread.start();
}
上述方法负责创建新的socket实例和开启心跳检测,其中比较重要的代码是
mHandler.postDelayed(mHeartBeatRunnable, HEART_BEAT_RATE);
这里的HEART_BEAT_RATE是一个int常量,表示心跳间隔,mHeartBeatRunnableze则负责心跳检测
// 心跳机制
private SocketReadThread mReadThread;
private static final long HEART_BEAT_RATE = 4 * 1000;
private long sendTime = 0L;
private Runnable mHeartBeatRunnable = new Runnable() {
@Override
public void run() {
if (System.currentTimeMillis() - sendTime >= HEART_BEAT_RATE) {//每隔4秒检测一次
boolean isSuccess = sendHeartBeatMsg("");
if (!isSuccess) {
Log.i(TAG, "连接已断开,正在重连……");
mHandler.removeCallbacks(mHeartBeatRunnable);// 移除线程,重连时保证该线程已停止上次调用时的工作
mReadThread.release();//释放SocketReadThread线程资源
releaseLastSocket();
connectToServer();// 再次调用connectToServer方法,连接服务端
}
}
mHandler.postDelayed(this, HEART_BEAT_RATE);
}
};
/**
* 发送心跳包
*
* @param msg
* @return
*/
public boolean sendHeartBeatMsg(String msg) {
if (null == mSocket) {
return false;
}
try {
if (!mSocket.isClosed() && !mSocket.isOutputShutdown()) {
String message = msg + "\r\n";
mDataOutputStream.write(message.getBytes());
mDataOutputStream.flush();
sendTime = System.currentTimeMillis();
} else {
return false;
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
上述方法是心跳检测的主要方法,调用sendHeartBeatMsg()发送数据到服务端,该方法稍后给出。如果检测到连接断开,则释放各种资源,重新连接。如果连接没有断开则继续检测,非常简单的逻辑
/**
* 断开连接
*
*/
private void releaseLastSocket() {
try {
if (null != mSocket) {
if (!mSocket.isClosed()) {
mSocket.close();
}
}
mSocket = null;
} catch (IOException e) {
e.printStackTrace();
}
}
最后给出SocketReadThread线程,这个线程负责读取服务端发送过来的数据,和心跳机制无关,但在心跳机制中重连处理时,一定要释放它的资源。
public class SocketReadThread extends Thread {
private static final String TAG = "SocketThread";
private volatile boolean mStopThread = false;
public void release() {
mStopThread = true;
releaseLastSocket();
}
@Override
public void run() {
DataInputStream mInputStream = null;
try {
mInputStream = new DataInputStream(mSocket.getInputStream());
Logger.d(TAG, "SocketThread running!");
while (!mStopThread) {
String resultStr = mInputStream.readUTF();
handleStringMsg(resultStr);
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
mSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
if (mInputStream != null) {
try {
mInputStream.close();
mInputStream = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}