昨天三点钟才睡觉的,现在胸口感觉闷闷的,兄弟们,我是不是要GG了?如果我G了,求大佬们给我烧个女朋友,
1.在使用Socket连接客户端和服务器端的时候,如果服务端断开了连接,我们客户端是收不到任何回调消息的,只是在你发送消息给服务器的时候,会走异常,表示发送失败。
2.所以要判断服务器是否在线,就需要客户端不停的发送心跳消息给服务器,服务器收到心跳消息,就立马回复给你消息,这样就 能知道双方是否都在线。
3.如果在一段时间内,还是没有收到服务器回复的消息,就表示服务器可能已经死了,这时候你可能需要去做一些提示信息给Android前台。
4.在这一段时间内,你可以不停的尝试重新建立Socket连接,即断线重连。
上代码吧:
首先正常创建一个Activity,并创建一个TcpService服务,在服务中去进行Socket的相关操作。在connection中回调的clientBinder 对象,就是Activity和Service通讯的桥梁。上一篇我们是在Activity里去进行Socket测试的,用Service显然要比用Activity好的多。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this,TcpService.class);
bindService(intent,connection,BIND_AUTO_CREATE);
}
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
TcpService.ClientBinder clientBinder = (TcpService.ClientBinder) service;
clientBinder.startConnect();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
}
在TcpService的onBind()方法中,我们首先需要返回ClientBinder这个对象,然后调用clientBinder.startConnect()建立Socket连接。
public void startConnect() {
//在子线程进行网络操作
// Service也是运行在主线程,千万不要以为Service意思跟后台运行很像,就以为Service运行在后台子线程
if (mExecutorService == null) {
mExecutorService = Executors.newCachedThreadPool();
}
mExecutorService.execute(connectRunnable);
}
private Runnable connectRunnable = new Runnable() {
@Override
public void run() {
try {
// 建立Socket连接
mSocket = new Socket();
mSocket.connect(new InetSocketAddress("192.168.1.186", 8292), 10);
bis = new BufferedInputStream(mSocket.getInputStream());
bos = new BufferedOutputStream(mSocket.getOutputStream());
// 创建读取服务器心跳的线程
mReadThread = new ReadThread();
mReadThread.start();
//开启心跳,每隔3秒钟发送一次心跳
mHandler.post(mHeartRunnable);
tryCount = 1;
} catch (Exception e) {
tryCount ++ ;
e.printStackTrace();
Log.d(TAG, "Socket连接建立失败,正在尝试第"+ tryCount + "次重连");
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mExecutorService.execute(connectRunnable);
}
},mHeart_spacetime);
}
}
};
这里我创建了一个线程池,用线程池去处理Socket的需要联网的Runnable。然后就是正常的创建Socket连接,新建读取线程以及开启mHeartRunnable。在异常处理里面,如果建立Socket失败,就发送一个延时消息,重新去创建连接。下面我们看一下ReadThread。
public class ReadThread extends Thread {
@Override
public void run() {
int size;
byte[] buffer = new byte[1024];
try {
while ((size = bis.read(buffer)) != -1) {
String str = new String(buffer, 0, size);
Log.d(TAG,"我收到来自服务器的消息: " +str);
//收到心跳消息以后,首先移除断连消息,然后创建一个新的60秒后执行断连的消息。
//这样每次收到心跳后都会重新创建一个60秒的延时消息,在60秒后还没收到心跳消息,表明服务器已死,就会执行断开Socket连接
//在60秒钟内如果收到过一次心跳消息,就表明服务器还活着,可以继续与之通讯。
mHandler.removeCallbacks(disConnectRunnable);
mHandler.postDelayed(disConnectRunnable, mHeart_spacetime * 40);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
ReadThread主要处理的是disConnectRunnable,接收到服务器的心跳消息就移除断连任务,然后重新创建一个新的断连任务。在指定的时间内没有收到服务端的心跳消息,断连任务就会执行。反之,则会又进入一个 "移除旧的—创建新的" 的循环。当然,这个发送心跳消息的时间间隔(mHeart_spacetime )肯定是要小于这个断连任务延时时间的(mHeart_spacetime * 40)。接下来,看一下mHeartRunnable,心跳发送失败以后立马执行重连操作。
private Runnable mHeartRunnable = new Runnable() {
@Override
public void run() {
sendData();
}
};
private void sendData() {
mExecutorService.execute(new Runnable() {
@Override
public void run() {
try {
bos.write("给你一张过去的CD,听听那时我们的爱情!".getBytes());
//一定不能忘记这步操作
bos.flush();
//发送成功以后,重新建立一个心跳消息
mHandler.postDelayed(mHeartRunnable, mHeart_spacetime);
Log.d(TAG, "我发送给服务器的消息: 给你一张过去的CD,听听那时我们的爱情!");
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "心跳任务发送失败,正在尝试第"+ tryCount + "次重连");
//mExecutorService.schedule(connectRunnable,mHeart_spacetime, TimeUnit.SECONDS);
mExecutorService.execute(connectRunnable);
}
}
});
}
下面来看一下效果图:
最后,附上TcpService类和AppServer类的代码:
客户端代码
/**
* Create by Fiora on 2018/10/24 0024
*/
public class TcpService extends Service {
public static final String TAG = TcpService.class.getSimpleName();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new ClientBinder();
}
public class ClientBinder extends Binder {
private int mHeart_spacetime = 3 * 1000; //心跳间隔时间
private BufferedInputStream bis;
private BufferedOutputStream bos;
private ReadThread mReadThread;
private Handler mHandler = new Handler();
private Socket mSocket;
private ExecutorService mExecutorService;
private int tryCount = 0;//重试次数
public void startConnect() {
//在子线程进行网络操作
// Service也是运行在主线程,千万不要以为Service意思跟后台运行很像,就以为Service运行在后台子线程
if (mExecutorService == null) {
mExecutorService = Executors.newCachedThreadPool();
}
mExecutorService.execute(connectRunnable);
}
private Runnable connectRunnable = new Runnable() {
@Override
public void run() {
try {
// 建立Socket连接
mSocket = new Socket();
mSocket.connect(new InetSocketAddress("192.168.1.186", 8292), 10);
bis = new BufferedInputStream(mSocket.getInputStream());
bos = new BufferedOutputStream(mSocket.getOutputStream());
// 创建读取服务器心跳的线程
mReadThread = new ReadThread();
mReadThread.start();
//开启心跳,每隔15秒钟发送一次心跳
mHandler.post(mHeartRunnable);
tryCount = 1;
} catch (Exception e) {
tryCount ++ ;
e.printStackTrace();
Log.d(TAG, "Socket连接建立失败,正在尝试第"+ tryCount + "次重连");
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mExecutorService.execute(connectRunnable);
}
},mHeart_spacetime);
}
}
};
public class ReadThread extends Thread {
@Override
public void run() {
int size;
byte[] buffer = new byte[1024];
try {
while ((size = bis.read(buffer)) != -1) {
String str = new String(buffer, 0, size);
Log.d(TAG,"我收到来自服务器的消息: " +str);
//收到心跳消息以后,首先移除断连消息,然后创建一个新的60秒后执行断连的消息。
//这样每次收到心跳后都会重新创建一个60秒的延时消息,在60秒后还没收到心跳消息,表明服务器已死,就会执行断开Socket连接
//在60秒钟内如果收到过一次心跳消息,就表明服务器还活着,可以继续与之通讯。
mHandler.removeCallbacks(disConnectRunnable);
mHandler.postDelayed(disConnectRunnable, mHeart_spacetime * 40);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private Runnable mHeartRunnable = new Runnable() {
@Override
public void run() {
sendData();
}
};
private void sendData() {
mExecutorService.execute(new Runnable() {
@Override
public void run() {
try {
bos.write("给你一张过去的CD,听听那时我们的爱情!".getBytes());
//一定不能忘记这步操作
bos.flush();
//发送成功以后,重新建立一个心跳消息
mHandler.postDelayed(mHeartRunnable, mHeart_spacetime);
Log.d(TAG, "我发送给服务器的消息: 给你一张过去的CD,听听那时我们的爱情!");
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "心跳任务发送失败,正在尝试第"+ tryCount + "次重连");
//mExecutorService.schedule(connectRunnable,mHeart_spacetime, TimeUnit.SECONDS);
mExecutorService.execute(connectRunnable);
}
}
});
}
private Runnable disConnectRunnable = new Runnable() {
@Override
public void run() {
disConnect();
}
};
private void disConnect() {
mExecutorService.execute(new Runnable() {
@Override
public void run() {
try {
Log.d(TAG, "正在执行断连: disConnect");
//执行Socket断连
mHandler.removeCallbacks(mHeartRunnable);
if (mReadThread != null) {
mReadThread.interrupt();
}
if (bos != null) {
bos.close();
}
if (bis != null) {
bis.close();
}
if (mSocket != null) {
mSocket.shutdownInput();
mSocket.shutdownOutput();
mSocket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
}
服务端代码:
/**
* Create by Fiora on 2018/10/24 0024
*/
public class AppServer {
public static final String TAG = AppServer.class.getSimpleName();
private static BufferedOutputStream bos;
private static BufferedInputStream bis;
private static Socket acceptSocket;
public static void main (String args[]){
try{
ServerSocket serverSocket = new ServerSocket(8292);
while(true) {
acceptSocket = serverSocket.accept();
bos = new BufferedOutputStream(acceptSocket.getOutputStream());
bis = new BufferedInputStream(acceptSocket.getInputStream());
ReadThread readThread = new ReadThread();
readThread.start();
}
}catch (Exception e){
e.printStackTrace();
}
}
private static class ReadThread extends Thread {
@Override
public void run() {
while (true) {
byte[] data = new byte[1024];
int size = 0;
try {
while ((size = bis.read(data)) != -1) {
String str = new String(data,0,size);
System.out.println(TAG+"----"+str);
//收到客户端发送的请求后,立马回一条心跳给客户端
bos.write("有时会突然忘了,我依然爱着你!".getBytes());
bos.flush();
}
} catch (Exception e) {
e.printStackTrace();
break;
}
}
}
}
}