PRE
在利用socket寫通訊程序的時候,想檢測服務器是否還活着。
從網上找了很多資料,都沒有自己合適的,最后自己想了個辦法,不過也相當於截取了心跳檢測的一部分。
這里檢測的是遠程server的連接,而不是本地是否連接成功。首先想到socket類的方法isClosed()、isConnected()、isInputStreamShutdown()、isOutputStreamShutdown()
等,但經過試驗並查看相關文檔,這些方法都是本地端的狀態,無法判斷遠端是否已經斷開連接。
而有一個方法sendUrgentData
,查看文檔后得知它會往輸出流發送一個字節的數據,只要對方Socket的SO_OOBINLINE屬性沒有打開,就會自動舍棄這個字節。
或者,其實如果斷開連接了,你發送過去的數據會收不到,最后client會拋出io異常。
但是,上面兩個方法,拋出異常的時間長達15秒!簡直不能忍。
解決思路
當然,這里的需求環境是:發送數據次數非常少,幾乎只需要判斷一兩次,數據是集中發送的。那么,就可以這樣了:
只要client在發送數據前,先發送自定義的一個測試數據,並自定義一個String之類的變量(初始值null)來接收server發回的數據,client發完測試數據,睡500毫秒(自定義時間)(異步接收服務器消息),然后立刻檢測這個string是不是null,就可以知道server是否收到消息了。
代碼
客戶端APP上的部分代碼
/**客戶端線程,用來建立連接和發送消息 */ public class Client implements Runnable{ Socket s=null; DataInputStream dis=null; DataOutputStream dos=null; private boolean isConnected=false; Thread receiveMessage=null; /**發送消息 * @param str 發送的信息,client僅向server發送兩次消息,這是我自己的應用需求,根據實際情況來做你的。 * @throws IOException * */ public void sendMessage(String str) throws IOException{ dos.writeUTF(str); dos.flush(); } /**斷開連接*/ public void disConnect(){ try { dos.close(); dis.close(); s.close(); } catch (IOException e) { System.out.println("client closed error"); e.printStackTrace(); } } /**建立socket連接,開啟接收數據線程 * */ public void run() { try { s=new Socket(SERVER_HOST_IP,SERVER_HOST_PORT); s.setOOBInline(true); dis=new DataInputStream(s.getInputStream()); dos=new DataOutputStream(s.getOutputStream()); System.out.println("connected!"); isConnected=true; receiveMessage=new Thread(new ReceiveListenerThread()); receiveMessage.start(); //發送imei sendMessage(IMEI); } catch (UnknownHostException e) { System.out.println("fuwuqoweikaiqi"); e.printStackTrace(); } catch (IOException e) { System.out.println("ioerr"); e.printStackTrace(); } } private class ReceiveListenerThread implements Runnable{ //這一部分接收數據的處理,請根據實際情況修改 String data[]=new String[3]; public void run() { try { if(isConnected){ String receivedMessage=dis.readUTF(); System.out.println(receivedMessage); serverStarted=true; data=receivedMessage.split("_"); isLegal=Integer.parseInt(data[0]); num1=Integer.parseInt(data[1]); num2=Integer.parseInt(data[2]); System.out.println(""+isLegal+num1+""+num2); } if(isConnected){ finalOK=dis.readUTF(); } }catch (SocketException e){ System.out.println("exit!"); } catch (IOException e) { e.printStackTrace(); } } }
調用:
Client client=null; /**客戶端網絡線程*/ Thread tClient=null; client=new Client(); tClient=new Thread(client); tClient.start();
服務器上:
/** 這是服務器用來接收處理客戶端的線程類 */ class Client implements Runnable { private Socket s; private DataInputStream dis = null; private DataOutputStream dos = null; private boolean isConnected = false; public Client(Socket s) { this.s = s; try { dis = new DataInputStream(s.getInputStream()); dos = new DataOutputStream(s.getOutputStream()); isConnected = true; } catch (IOException e) { System.out.println("on clients' data in and out have error"); // e.printStackTrace(); } } public void run() { String str; try { // 先檢查是否是合法的客戶端 while (isConnected) { str = dis.readUTF(); str = dis.readUTF(); //此處的具體代碼省略 dos.writeUTF("ok"); dos.flush(); } } } catch (EOFException e) { System.out.println("Client closed"); Home.appendMessage((legalNum + 1) + "號客戶端已經登出"); } catch (IOException e) { e.printStackTrace(); } finally { try { if (dis != null) dis.close(); if (dos != null) dos.close(); if (s != null) s.close(); } catch (IOException e) { e.printStackTrace(); } } } }
客戶端判斷服務器是否還活着代碼:
try { //客戶端發送一個測試信息 client.sendMessage(cookie); System.out.println("send"); //睡1秒 SystemClock.sleep(1000); //這個finalok是在客戶端的接收線程那里處理的。如果不是null說明服務器沒問題 if(finalOK!=null){ client.disConnect(); exit("感謝您的使用,本次已經結束~"); }else{ throw new IOException() ; } //超時檢測 } catch (IOException e) { new AlertDialog.Builder(MainActivity.this).setTitle("提示").setPositiveButton("好的", null).setMessage("服務器關閉了,請聯系管理員並在服務器重新開啟后再次點擊【發送】來提交數據").show(); client.disConnect(); e.printStackTrace(); }
后記:
當初只是簡單記錄了下自己的想法,寫的很簡陋,沒想到慢慢這篇文章還好多人看,深感不安,故更新補充一下。
2015.3.27