实现原理:
长连接的维持,是要客户端程序,定时向服务端程序,发送一个维持连接包的。
如果,长时间未发送维持连接包,服务端程序将断开连接。
客户端:
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()); } }
}