JAVA实现长连接(含心跳检测)Demo

实现原理:

       长连接的维持,是要客户端程序,定时向服务端程序,发送一个维持连接包的。
       如果,长时间未发送维持连接包,服务端程序将断开连接。

客户端:
       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&lt;Class, ObjectAction&gt; actionMapping = <span style="color: #0000ff">new</span> ConcurrentHashMap&lt;Class,ObjectAction&gt;<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&lt;Object&gt;<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&gt;<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()&gt;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&lt;Class, ObjectAction&gt; actionMapping = <span style="color: #0000ff">new</span> ConcurrentHashMap&lt;Class,ObjectAction&gt;<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&lt;Object&gt;<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 &amp;&amp;<span style="color: #000000"> run){
            </span><span style="color: #0000ff">if</span>(System.currentTimeMillis()-lastReceiveTime&gt;<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()&gt;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());
    }
    
}

}

复制代码

 

posted @ 2018-10-30 13:16  星朝  阅读(8891)  评论(0编辑  收藏  举报