昨天三點(diǎn)鐘才睡覺(jué)的贡茅,現(xiàn)在胸口感覺(jué)悶悶的,兄弟們其做,我是不是要GG了顶考?如果我G了,求大佬們給我燒個(gè)女朋友妖泄,
1.在使用Socket連接客戶端和服務(wù)器端的時(shí)候驹沿,如果服務(wù)端斷開(kāi)了連接,我們客戶端是收不到任何回調(diào)消息的蹈胡,只是在你發(fā)送消息給服務(wù)器的時(shí)候渊季,會(huì)走異常,表示發(fā)送失敗罚渐。
2.所以要判斷服務(wù)器是否在線却汉,就需要客戶端不停的發(fā)送心跳消息給服務(wù)器,服務(wù)器收到心跳消息荷并,就立馬回復(fù)給你消息合砂,這樣就 能知道雙方是否都在線。
3.如果在一段時(shí)間內(nèi)源织,還是沒(méi)有收到服務(wù)器回復(fù)的消息翩伪,就表示服務(wù)器可能已經(jīng)死了,這時(shí)候你可能需要去做一些提示信息給Android前臺(tái)谈息。
4.在這一段時(shí)間內(nèi)缘屹,你可以不停的嘗試重新建立Socket連接,即斷線重連侠仇。
上代碼吧:
首先正常創(chuàng)建一個(gè)Activity轻姿,并創(chuàng)建一個(gè)TcpService服務(wù),在服務(wù)中去進(jìn)行Socket的相關(guān)操作逻炊。在connection中回調(diào)的clientBinder 對(duì)象互亮,就是Activity和Service通訊的橋梁。上一篇我們是在Activity里去進(jìn)行Socket測(cè)試的嗅骄,用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這個(gè)對(duì)象溺森,然后調(diào)用clientBinder.startConnect()建立Socket連接慕爬。
public void startConnect() {
//在子線程進(jìn)行網(wǎng)絡(luò)操作
// Service也是運(yùn)行在主線程窑眯,千萬(wàn)不要以為Service意思跟后臺(tái)運(yùn)行很像,就以為Service運(yùn)行在后臺(tái)子線程
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());
// 創(chuàng)建讀取服務(wù)器心跳的線程
mReadThread = new ReadThread();
mReadThread.start();
//開(kāi)啟心跳,每隔3秒鐘發(fā)送一次心跳
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);
}
}
};
這里我創(chuàng)建了一個(gè)線程池医窿,用線程池去處理Socket的需要聯(lián)網(wǎng)的Runnable磅甩。然后就是正常的創(chuàng)建Socket連接,新建讀取線程以及開(kāi)啟mHeartRunnable姥卢。在異常處理里面卷要,如果建立Socket失敗,就發(fā)送一個(gè)延時(shí)消息独榴,重新去創(chuàng)建連接僧叉。下面我們看一下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,"我收到來(lái)自服務(wù)器的消息: " +str);
//收到心跳消息以后棺榔,首先移除斷連消息瓶堕,然后創(chuàng)建一個(gè)新的60秒后執(zhí)行斷連的消息。
//這樣每次收到心跳后都會(huì)重新創(chuàng)建一個(gè)60秒的延時(shí)消息症歇,在60秒后還沒(méi)收到心跳消息郎笆,表明服務(wù)器已死,就會(huì)執(zhí)行斷開(kāi)Socket連接
//在60秒鐘內(nèi)如果收到過(guò)一次心跳消息忘晤,就表明服務(wù)器還活著宛蚓,可以繼續(xù)與之通訊。
mHandler.removeCallbacks(disConnectRunnable);
mHandler.postDelayed(disConnectRunnable, mHeart_spacetime * 40);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
ReadThread主要處理的是disConnectRunnable设塔,接收到服務(wù)器的心跳消息就移除斷連任務(wù)凄吏,然后重新創(chuàng)建一個(gè)新的斷連任務(wù)。在指定的時(shí)間內(nèi)沒(méi)有收到服務(wù)端的心跳消息壹置,斷連任務(wù)就會(huì)執(zhí)行竞思。反之表谊,則會(huì)又進(jìn)入一個(gè) "移除舊的—?jiǎng)?chuàng)建新的" 的循環(huán)钞护。當(dāng)然,這個(gè)發(fā)送心跳消息的時(shí)間間隔(mHeart_spacetime )肯定是要小于這個(gè)斷連任務(wù)延時(shí)時(shí)間的(mHeart_spacetime * 40)爆办。接下來(lái)难咕,看一下mHeartRunnable,心跳發(fā)送失敗以后立馬執(zhí)行重連操作距辆。
private Runnable mHeartRunnable = new Runnable() {
@Override
public void run() {
sendData();
}
};
private void sendData() {
mExecutorService.execute(new Runnable() {
@Override
public void run() {
try {
bos.write("給你一張過(guò)去的CD,聽(tīng)聽(tīng)那時(shí)我們的愛(ài)情余佃!".getBytes());
//一定不能忘記這步操作
bos.flush();
//發(fā)送成功以后,重新建立一個(gè)心跳消息
mHandler.postDelayed(mHeartRunnable, mHeart_spacetime);
Log.d(TAG, "我發(fā)送給服務(wù)器的消息: 給你一張過(guò)去的CD,聽(tīng)聽(tīng)那時(shí)我們的愛(ài)情跨算!");
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "心跳任務(wù)發(fā)送失敗爆土,正在嘗試第"+ tryCount + "次重連");
//mExecutorService.schedule(connectRunnable,mHeart_spacetime, TimeUnit.SECONDS);
mExecutorService.execute(connectRunnable);
}
}
});
}
下面來(lái)看一下效果圖:
最后妄均,附上TcpService類(lèi)和AppServer類(lèi)的代碼:
客戶端代碼
/**
* 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; //心跳間隔時(shí)間
private BufferedInputStream bis;
private BufferedOutputStream bos;
private ReadThread mReadThread;
private Handler mHandler = new Handler();
private Socket mSocket;
private ExecutorService mExecutorService;
private int tryCount = 0;//重試次數(shù)
public void startConnect() {
//在子線程進(jìn)行網(wǎng)絡(luò)操作
// Service也是運(yùn)行在主線程,千萬(wàn)不要以為Service意思跟后臺(tái)運(yùn)行很像哪自,就以為Service運(yùn)行在后臺(tái)子線程
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());
// 創(chuàng)建讀取服務(wù)器心跳的線程
mReadThread = new ReadThread();
mReadThread.start();
//開(kāi)啟心跳,每隔15秒鐘發(fā)送一次心跳
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,"我收到來(lái)自服務(wù)器的消息: " +str);
//收到心跳消息以后丰包,首先移除斷連消息,然后創(chuàng)建一個(gè)新的60秒后執(zhí)行斷連的消息壤巷。
//這樣每次收到心跳后都會(huì)重新創(chuàng)建一個(gè)60秒的延時(shí)消息烫沙,在60秒后還沒(méi)收到心跳消息,表明服務(wù)器已死隙笆,就會(huì)執(zhí)行斷開(kāi)Socket連接
//在60秒鐘內(nèi)如果收到過(guò)一次心跳消息锌蓄,就表明服務(wù)器還活著,可以繼續(xù)與之通訊撑柔。
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("給你一張過(guò)去的CD,聽(tīng)聽(tīng)那時(shí)我們的愛(ài)情瘸爽!".getBytes());
//一定不能忘記這步操作
bos.flush();
//發(fā)送成功以后,重新建立一個(gè)心跳消息
mHandler.postDelayed(mHeartRunnable, mHeart_spacetime);
Log.d(TAG, "我發(fā)送給服務(wù)器的消息: 給你一張過(guò)去的CD,聽(tīng)聽(tīng)那時(shí)我們的愛(ài)情铅忿!");
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "心跳任務(wù)發(fā)送失敗剪决,正在嘗試第"+ 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, "正在執(zhí)行斷連: disConnect");
//執(zhí)行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();
}
}
});
}
}
}
服務(wù)端代碼:
/**
* 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);
//收到客戶端發(fā)送的請(qǐng)求后,立馬回一條心跳給客戶端
bos.write("有時(shí)會(huì)突然忘了檀训,我依然愛(ài)著你柑潦!".getBytes());
bos.flush();
}
} catch (Exception e) {
e.printStackTrace();
break;
}
}
}
}
}