實現原理:
長連接的維持,是要客戶端程序,定時向服務端程序,發送一個維持連接包的。
如果,長時間未發送維持連接包,服務端程序將斷開連接。
客戶端:
Client通過持有Socket的對象,可以隨時(使用sendObject方法)發送Massage Object(消息)給服務端。
如果keepAliveDelay毫秒(程序中是2秒)內未發送任何數據,則自動發送一個KeepAlive Object(心跳)給服務端,用於維持連接。
由於,我們向服務端,可以發送很多不同的消息對象,服務端也可以返回不同的對象。所以,對於返回對象的處理,要編寫具體的ObjectAction實現類進行處理。通過Client.addActionMap方法進行添加。這樣,程序會回調處理。
服務端:
由於客戶端會定時(keepAliveDelay毫秒)發送維持連接的信息過來,所以,服務端要有一個檢測機制。
即當服務端receiveTimeDelay毫秒(程序中是3秒)內未接收任何數據,則自動斷開與客戶端的連接。
ActionMapping的原理與客戶端相似(相同)。
通過添加相應的ObjectAction實現類,可以實現不同對象的響應、應答過程。
心跳反映的代碼:
** * * 維持連接的消息對象(心跳對象) */ public class KeepAlive implements Serializable{</span><span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">final</span> <span style="color: #0000ff">long</span> serialVersionUID = -2813120366138988480L<span style="color: #000000">; </span><span style="color: #008000">/*</span><span style="color: #008000"> 覆蓋該方法,僅用於測試使用。 * @see java.lang.Object#toString() </span><span style="color: #008000">*/</span><span style="color: #000000"> @Override </span><span style="color: #0000ff">public</span><span style="color: #000000"> String toString() { </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">new</span> SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(<span style="color: #0000ff">new</span> Date())+"\t維持連接包"<span style="color: #000000">; }
}
客戶端的代碼:
public class Client {</span><span style="color: #008000">/**</span><span style="color: #008000"> * 處理服務端發回的對象,可實現該接口。 </span><span style="color: #008000">*/</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">interface</span><span style="color: #000000"> ObjectAction{ </span><span style="color: #0000ff">void</span><span style="color: #000000"> doAction(Object obj,Client client); } </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">final</span> <span style="color: #0000ff">class</span> DefaultObjectAction <span style="color: #0000ff">implements</span><span style="color: #000000"> ObjectAction{ </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> doAction(Object obj,Client client) { System.out.println(</span>"處理:\t"+<span style="color: #000000">obj.toString()); } } </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span> main(String[] args) <span style="color: #0000ff">throws</span><span style="color: #000000"> UnknownHostException, IOException { String serverIp </span>= "127.0.0.1"<span style="color: #000000">; </span><span style="color: #0000ff">int</span> port = 65432<span style="color: #000000">; Client client </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> Client(serverIp,port); client.start(); } </span><span style="color: #0000ff">private</span><span style="color: #000000"> String serverIp; </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">int</span><span style="color: #000000"> port; </span><span style="color: #0000ff">private</span><span style="color: #000000"> Socket socket; </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">boolean</span> running=<span style="color: #0000ff">false</span>; <span style="color: #008000">//</span><span style="color: #008000">連接狀態</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">long</span> lastSendTime; <span style="color: #008000">//</span><span style="color: #008000">最后一次發送數據的時間 </span><span style="color: #008000">//</span><span style="color: #008000">用於保存接收消息對象類型及該類型消息處理的對象</span> <span style="color: #0000ff">private</span> ConcurrentHashMap<Class, ObjectAction> actionMapping = <span style="color: #0000ff">new</span> ConcurrentHashMap<Class,ObjectAction><span style="color: #000000">(); </span><span style="color: #0000ff">public</span> Client(String serverIp, <span style="color: #0000ff">int</span><span style="color: #000000"> port) { </span><span style="color: #0000ff">this</span>.serverIp=<span style="color: #000000">serverIp; </span><span style="color: #0000ff">this</span>.port=<span style="color: #000000">port; } </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> start() <span style="color: #0000ff">throws</span><span style="color: #000000"> UnknownHostException, IOException { </span><span style="color: #0000ff">if</span>(running)<span style="color: #0000ff">return</span><span style="color: #000000">; socket </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> Socket(serverIp,port); System.out.println(</span>"本地端口:"+<span style="color: #000000">socket.getLocalPort()); lastSendTime</span>=<span style="color: #000000">System.currentTimeMillis(); running</span>=<span style="color: #0000ff">true</span><span style="color: #000000">; </span><span style="color: #0000ff">new</span> Thread(<span style="color: #0000ff">new</span> KeepAliveWatchDog()).start(); <span style="color: #008000">//</span><span style="color: #008000">保持長連接的線程,每隔2秒項服務器發一個一個保持連接的心跳消息</span> <span style="color: #0000ff">new</span> Thread(<span style="color: #0000ff">new</span> ReceiveWatchDog()).start(); <span style="color: #008000">//</span><span style="color: #008000">接受消息的線程,處理消息</span>
}
</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> stop(){ </span><span style="color: #0000ff">if</span>(running)running=<span style="color: #0000ff">false</span><span style="color: #000000">; } </span><span style="color: #008000">/**</span><span style="color: #008000"> * 添加接收對象的處理對象。 * </span><span style="color: #808080">@param</span><span style="color: #008000"> cls 待處理的對象,其所屬的類。 * </span><span style="color: #808080">@param</span><span style="color: #008000"> action 處理過程對象。 </span><span style="color: #008000">*/</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> addActionMap(Class<Object><span style="color: #000000"> cls,ObjectAction action){ actionMapping.put(cls, action); } </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> sendObject(Object obj) <span style="color: #0000ff">throws</span><span style="color: #000000"> IOException { ObjectOutputStream oos </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> ObjectOutputStream(socket.getOutputStream()); oos.writeObject(obj); System.out.println(</span>"發送:\t"+<span style="color: #000000">obj); oos.flush(); } </span><span style="color: #0000ff">class</span> KeepAliveWatchDog <span style="color: #0000ff">implements</span><span style="color: #000000"> Runnable{ </span><span style="color: #0000ff">long</span> checkDelay = 10<span style="color: #000000">; </span><span style="color: #0000ff">long</span> keepAliveDelay = 2000<span style="color: #000000">; </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> run() { </span><span style="color: #0000ff">while</span><span style="color: #000000">(running){ </span><span style="color: #0000ff">if</span>(System.currentTimeMillis()-lastSendTime><span style="color: #000000">keepAliveDelay){ </span><span style="color: #0000ff">try</span><span style="color: #000000"> { Client.</span><span style="color: #0000ff">this</span>.sendObject(<span style="color: #0000ff">new</span><span style="color: #000000"> KeepAlive()); } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (IOException e) { e.printStackTrace(); Client.</span><span style="color: #0000ff">this</span><span style="color: #000000">.stop(); } lastSendTime </span>=<span style="color: #000000"> System.currentTimeMillis(); }</span><span style="color: #0000ff">else</span><span style="color: #000000">{ </span><span style="color: #0000ff">try</span><span style="color: #000000"> { Thread.sleep(checkDelay); } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (InterruptedException e) { e.printStackTrace(); Client.</span><span style="color: #0000ff">this</span><span style="color: #000000">.stop(); } } } } } </span><span style="color: #0000ff">class</span> ReceiveWatchDog <span style="color: #0000ff">implements</span><span style="color: #000000"> Runnable{ </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> run() { </span><span style="color: #0000ff">while</span><span style="color: #000000">(running){ </span><span style="color: #0000ff">try</span><span style="color: #000000"> { InputStream in </span>=<span style="color: #000000"> socket.getInputStream(); </span><span style="color: #0000ff">if</span>(in.available()>0<span style="color: #000000">){ ObjectInputStream ois </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> ObjectInputStream(in); Object obj </span>=<span style="color: #000000"> ois.readObject(); System.out.println(</span>"接收:\t"+<span style="color: #000000">obj); ObjectAction oa </span>=<span style="color: #000000"> actionMapping.get(obj.getClass()); oa </span>= oa==<span style="color: #0000ff">null</span>?<span style="color: #0000ff">new</span><span style="color: #000000"> DefaultObjectAction():oa; oa.doAction(obj, Client.</span><span style="color: #0000ff">this</span><span style="color: #000000">); }</span><span style="color: #0000ff">else</span><span style="color: #000000">{ Thread.sleep(</span>10<span style="color: #000000">); } } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (Exception e) { e.printStackTrace(); Client.</span><span style="color: #0000ff">this</span><span style="color: #000000">.stop(); } } } }
}
服務短的代碼:
public class Server {</span><span style="color: #008000">/**</span><span style="color: #008000"> * 要處理客戶端發來的對象,並返回一個對象,可實現該接口。 </span><span style="color: #008000">*/</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">interface</span><span style="color: #000000"> ObjectAction{ Object doAction(Object rev, Server server); } </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">final</span> <span style="color: #0000ff">class</span> DefaultObjectAction <span style="color: #0000ff">implements</span><span style="color: #000000"> ObjectAction{ </span><span style="color: #0000ff">public</span><span style="color: #000000"> Object doAction(Object rev,Server server) { System.out.println(</span>"處理並返回:"+<span style="color: #000000">rev); </span><span style="color: #0000ff">return</span><span style="color: #000000"> rev; } } </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span><span style="color: #000000"> main(String[] args) { </span><span style="color: #0000ff">int</span> port = 65432<span style="color: #000000">; Server server </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> Server(port); server.start(); } </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">int</span><span style="color: #000000"> port; </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">volatile</span> <span style="color: #0000ff">boolean</span> running=<span style="color: #0000ff">false</span><span style="color: #000000">; </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">long</span> receiveTimeDelay=3000<span style="color: #000000">; </span><span style="color: #0000ff">private</span> ConcurrentHashMap<Class, ObjectAction> actionMapping = <span style="color: #0000ff">new</span> ConcurrentHashMap<Class,ObjectAction><span style="color: #000000">(); </span><span style="color: #0000ff">private</span><span style="color: #000000"> Thread connWatchDog; </span><span style="color: #0000ff">public</span> Server(<span style="color: #0000ff">int</span><span style="color: #000000"> port) { </span><span style="color: #0000ff">this</span>.port =<span style="color: #000000"> port; } </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> start(){ </span><span style="color: #0000ff">if</span>(running)<span style="color: #0000ff">return</span><span style="color: #000000">; running</span>=<span style="color: #0000ff">true</span><span style="color: #000000">; connWatchDog </span>= <span style="color: #0000ff">new</span> Thread(<span style="color: #0000ff">new</span><span style="color: #000000"> ConnWatchDog()); connWatchDog.start(); } @SuppressWarnings(</span>"deprecation"<span style="color: #000000">) </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> stop(){ </span><span style="color: #0000ff">if</span>(running)running=<span style="color: #0000ff">false</span><span style="color: #000000">; </span><span style="color: #0000ff">if</span>(connWatchDog!=<span style="color: #0000ff">null</span><span style="color: #000000">)connWatchDog.stop(); } </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> addActionMap(Class<Object><span style="color: #000000"> cls,ObjectAction action){ actionMapping.put(cls, action); } </span><span style="color: #0000ff">class</span> ConnWatchDog <span style="color: #0000ff">implements</span><span style="color: #000000"> Runnable{ </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> run(){ </span><span style="color: #0000ff">try</span><span style="color: #000000"> { ServerSocket ss </span>= <span style="color: #0000ff">new</span> ServerSocket(port,5<span style="color: #000000">); </span><span style="color: #0000ff">while</span><span style="color: #000000">(running){ Socket s </span>=<span style="color: #000000"> ss.accept(); </span><span style="color: #0000ff">new</span> Thread(<span style="color: #0000ff">new</span><span style="color: #000000"> SocketAction(s)).start(); } } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (IOException e) { e.printStackTrace(); Server.</span><span style="color: #0000ff">this</span><span style="color: #000000">.stop(); } } } </span><span style="color: #0000ff">class</span> SocketAction <span style="color: #0000ff">implements</span><span style="color: #000000"> Runnable{ Socket s; </span><span style="color: #0000ff">boolean</span> run=<span style="color: #0000ff">true</span><span style="color: #000000">; </span><span style="color: #0000ff">long</span> lastReceiveTime =<span style="color: #000000"> System.currentTimeMillis(); </span><span style="color: #0000ff">public</span><span style="color: #000000"> SocketAction(Socket s) { </span><span style="color: #0000ff">this</span>.s =<span style="color: #000000"> s; } </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> run() { </span><span style="color: #0000ff">while</span>(running &&<span style="color: #000000"> run){ </span><span style="color: #0000ff">if</span>(System.currentTimeMillis()-lastReceiveTime><span style="color: #000000">receiveTimeDelay){ overThis(); }</span><span style="color: #0000ff">else</span><span style="color: #000000">{ </span><span style="color: #0000ff">try</span><span style="color: #000000"> { InputStream in </span>=<span style="color: #000000"> s.getInputStream(); </span><span style="color: #0000ff">if</span>(in.available()>0<span style="color: #000000">){ ObjectInputStream ois </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> ObjectInputStream(in); Object obj </span>=<span style="color: #000000"> ois.readObject(); lastReceiveTime </span>=<span style="color: #000000"> System.currentTimeMillis(); System.out.println(</span>"接收:\t"+<span style="color: #000000">obj); ObjectAction oa </span>=<span style="color: #000000"> actionMapping.get(obj.getClass()); oa </span>= oa==<span style="color: #0000ff">null</span>?<span style="color: #0000ff">new</span><span style="color: #000000"> DefaultObjectAction():oa; Object out </span>= oa.doAction(obj,Server.<span style="color: #0000ff">this</span><span style="color: #000000">); </span><span style="color: #0000ff">if</span>(out!=<span style="color: #0000ff">null</span><span style="color: #000000">){ ObjectOutputStream oos </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> ObjectOutputStream(s.getOutputStream()); oos.writeObject(out); oos.flush(); } }</span><span style="color: #0000ff">else</span><span style="color: #000000">{ Thread.sleep(</span>10<span style="color: #000000">); } } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (Exception e) { e.printStackTrace(); overThis(); } } } } </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">void</span><span style="color: #000000"> overThis() { </span><span style="color: #0000ff">if</span>(run)run=<span style="color: #0000ff">false</span><span style="color: #000000">; </span><span style="color: #0000ff">if</span>(s!=<span style="color: #0000ff">null</span><span style="color: #000000">){ </span><span style="color: #0000ff">try</span><span style="color: #000000"> { s.close(); } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (IOException e) { e.printStackTrace(); } } System.out.println(</span>"關閉:"+<span style="color: #000000">s.getRemoteSocketAddress()); } }
}