实现原理:

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

客户端:
       Client通过持有Socket的对象,可以随时(使用sendObject方法)发送Massage Object(消息)给服务端。
       如果keepAliveDelay毫秒(程序中是2秒)内未发送任何数据,则自动发送一个KeepAlive Object(心跳)给服务端,用于维持连接。
       由于,我们向服务端,可以发送很多不同的消息对象,服务端也可以返回不同的对象。所以,对于返回对象的处理,要编写具体的ObjectAction实现类进行处理。通过Client.addActionMap方法进行添加。这样,程序会回调处理。

服务端:
        由于客户端会定时(keepAliveDelay毫秒)发送维持连接的信息过来,所以,服务端要有一个检测机制。
        即当服务端receiveTimeDelay毫秒(程序中是3秒)内未接收任何数据,则自动断开与客户端的连接。
         ActionMapping的原理与客户端相似(相同)。

通过添加相应的ObjectAction实现类,可以实现不同对象的响应、应答过程。

心跳反映的代码:

**
*
* 维持连接的消息对象(心跳对象)
*/
public class KeepAlive implements Serializable{ private static final long serialVersionUID = -2813120366138988480L; /* 覆盖该方法,仅用于测试使用。
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"\t维持连接包";
} }

客户端的代码:

public class Client {

    /**
* 处理服务端发回的对象,可实现该接口。
*/
public static interface ObjectAction{
void doAction(Object obj,Client client);
} public static final class DefaultObjectAction implements ObjectAction{
public void doAction(Object obj,Client client) {
System.out.println("处理:\t"+obj.toString());
}
} public static void main(String[] args) throws UnknownHostException, IOException {
String serverIp = "127.0.0.1";
int port = 65432;
Client client = new Client(serverIp,port);
client.start();
} private String serverIp;
private int port;
private Socket socket;
private boolean running=false; //连接状态 private long lastSendTime; //最后一次发送数据的时间 //用于保存接收消息对象类型及该类型消息处理的对象
private ConcurrentHashMap<Class, ObjectAction> actionMapping = new ConcurrentHashMap<Class,ObjectAction>(); public Client(String serverIp, int port) {
this.serverIp=serverIp;
this.port=port;
} public void start() throws UnknownHostException, IOException {
if(running)return;
socket = new Socket(serverIp,port);
System.out.println("本地端口:"+socket.getLocalPort());
lastSendTime=System.currentTimeMillis();
running=true;
new Thread(new KeepAliveWatchDog()).start(); //保持长连接的线程,每隔2秒项服务器发一个一个保持连接的心跳消息
new Thread(new ReceiveWatchDog()).start(); //接受消息的线程,处理消息
} public void stop(){
if(running)running=false;
} /**
* 添加接收对象的处理对象。
* @param cls 待处理的对象,其所属的类。
* @param action 处理过程对象。
*/
public void addActionMap(Class<Object> cls,ObjectAction action){
actionMapping.put(cls, action);
} public void sendObject(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(obj);
System.out.println("发送:\t"+obj);
oos.flush();
} class KeepAliveWatchDog implements Runnable{
long checkDelay = 10;
long keepAliveDelay = 2000;
public void run() {
while(running){
if(System.currentTimeMillis()-lastSendTime>keepAliveDelay){
try {
Client.this.sendObject(new KeepAlive());
} catch (IOException e) {
e.printStackTrace();
Client.this.stop();
}
lastSendTime = System.currentTimeMillis();
}else{
try {
Thread.sleep(checkDelay);
} catch (InterruptedException e) {
e.printStackTrace();
Client.this.stop();
}
}
}
}
} class ReceiveWatchDog implements Runnable{
public void run() {
while(running){
try {
InputStream in = socket.getInputStream();
if(in.available()>0){
ObjectInputStream ois = new ObjectInputStream(in);
Object obj = ois.readObject();
System.out.println("接收:\t"+obj);
ObjectAction oa = actionMapping.get(obj.getClass());
oa = oa==null?new DefaultObjectAction():oa;
oa.doAction(obj, Client.this);
}else{
Thread.sleep(10);
}
} catch (Exception e) {
e.printStackTrace();
Client.this.stop();
}
}
}
} }

服务短的代码:

public class Server {

    /**
* 要处理客户端发来的对象,并返回一个对象,可实现该接口。
*/
public interface ObjectAction{
Object doAction(Object rev, Server server);
} public static final class DefaultObjectAction implements ObjectAction{
public Object doAction(Object rev,Server server) {
System.out.println("处理并返回:"+rev);
return rev;
}
} public static void main(String[] args) {
int port = 65432;
Server server = new Server(port);
server.start();
} private int port;
private volatile boolean running=false;
private long receiveTimeDelay=3000;
private ConcurrentHashMap<Class, ObjectAction> actionMapping = new ConcurrentHashMap<Class,ObjectAction>();
private Thread connWatchDog; public Server(int port) {
this.port = port;
} public void start(){
if(running)return;
running=true;
connWatchDog = new Thread(new ConnWatchDog());
connWatchDog.start();
} @SuppressWarnings("deprecation")
public void stop(){
if(running)running=false;
if(connWatchDog!=null)connWatchDog.stop();
} public void addActionMap(Class<Object> cls,ObjectAction action){
actionMapping.put(cls, action);
} class ConnWatchDog implements Runnable{
public void run(){
try {
ServerSocket ss = new ServerSocket(port,5);
while(running){
Socket s = ss.accept();
new Thread(new SocketAction(s)).start();
}
} catch (IOException e) {
e.printStackTrace();
Server.this.stop();
} }
} class SocketAction implements Runnable{
Socket s;
boolean run=true;
long lastReceiveTime = System.currentTimeMillis();
public SocketAction(Socket s) {
this.s = s;
}
public void run() {
while(running && run){
if(System.currentTimeMillis()-lastReceiveTime>receiveTimeDelay){
overThis();
}else{
try {
InputStream in = s.getInputStream();
if(in.available()>0){
ObjectInputStream ois = new ObjectInputStream(in);
Object obj = ois.readObject();
lastReceiveTime = System.currentTimeMillis();
System.out.println("接收:\t"+obj);
ObjectAction oa = actionMapping.get(obj.getClass());
oa = oa==null?new DefaultObjectAction():oa;
Object out = oa.doAction(obj,Server.this);
if(out!=null){
ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
oos.writeObject(out);
oos.flush();
}
}else{
Thread.sleep(10);
}
} catch (Exception e) {
e.printStackTrace();
overThis();
}
}
}
} private void overThis() {
if(run)run=false;
if(s!=null){
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("关闭:"+s.getRemoteSocketAddress());
} } }

JAVA实现长连接(含心跳检测)Demo的更多相关文章

  1. 正确理解IM长连接的心跳及重连机制,并动手实现(有完整IM源码)

    1.引言 说道“心跳”这个词大家都不陌生,当然不是指男女之间的心跳,而是和长连接相关的.顾名思义就是证明是否还活着的依据. 什么场景下需要心跳呢?目前我们接触到的大多是一些基于长连接的应用需要心跳来“ ...

  2. 基于netty实现的长连接,心跳机制及重连机制

    技术:maven3.0.5 + netty4.1.33 + jdk1.8   概述 Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速 ...

  3. 【Socket】关于socket长连接的心跳包

    TCP的socket本身就是长连接的,那么为什么还要心跳包呢? 在smack里有个30s发送一个空消息的线程,同样关于心跳包(keepalive) 据网络搜索到的资料解释如下 内网机器如果不主动向外发 ...

  4. 微信小程序中如何使用WebSocket实现长连接(含完整源码)

    本文由腾讯云技术团队原创,感谢作者的分享. 1.前言   微信小程序提供了一套在微信上运行小程序的解决方案,有比较完整的框架.组件以及 API,在这个平台上面的想象空间很大.腾讯云研究了一番之后,发现 ...

  5. java ajax长连接请求服务器数据

    Servlet 3.0笔记之异步请求Comet推送长轮询(long polling)篇 Comet另一种形式为长轮询(long polling),客户端会与服务器建立一个持久的连接,直到服务器端有数据 ...

  6. java socket 长连接 短连接

    长连接 是一旦一个客户端登陆上服务器,其与服务器之间的连接就不关闭,不管他们之间进行了多少次交易,直到客户端退出登陆或网络出现故障.这种技术在联机交易系统实现有利于提高效率. 短连接是客户端每发一个请 ...

  7. java Socket 长连接 心跳包 客户端 信息收发 demo

    今天写了个socket的测试小程序,代码如下 import java.io.IOException; import java.io.InputStream; import java.io.Output ...

  8. Java Socket长连接示例代码

    SocketListenerPusher.java代码如下: import java.io.IOException; import java.net.InetSocketAddress; import ...

  9. Java socket长连接代码实现

    服务器端程序: import java.io.*; import java.net.*; import java.util.*; public class ChatServer { boolean s ...

随机推荐

  1. Apache Kylin本地启动

    首先:kylin是一种Online Analytics Platform.    kylin 在Apache的首页是http://kylin.apache.org/cn/.    kylin git代 ...

  2. python twilio 短信群发 知识留存

    1. win7 32位系统,傻瓜安装Anaconda2(python 2.7) 2. 打开cmd, 输入命令pip install twilio,在线安装twilio 3. 打开Anaconda2的S ...

  3. leetcode921

    public class Solution { public int MinAddToMakeValid(string S) { Stack<char> ST = new Stack< ...

  4. 「小程序JAVA实战」小程序的留言和评价功能(70)

    转自:https://idig8.com/2018/10/28/xiaochengxujavashizhanxiaochengxudeliuyanhepingjiagongneng69/ 目前小程序这 ...

  5. maven常用命令集合

    作者:ydlmlh 原文:http://ydlmlh.iteye.com/blog/2158973 抽了点时间,整理了一些maven常用命令参数,以便参考:参考了maven官网和网上其他一些maven ...

  6. linux vim操作技巧

    安装: NERDTree 从 http://www.vim.org/scripts/script.php?script_id=1658  下载 unzip NERD_tree.zip cd ~/.vi ...

  7. angular性能优化心得

    原文出处 脏数据检查 != 轮询检查更新 谈起angular的脏检查机制(dirty-checking), 常见的误解就是认为: ng是定时轮询去检查model是否变更.其实,ng只有在指定事件触发后 ...

  8. 使用ES-Hadoop 6.5.4编写MR将数据索引到ES

    目录 1. 开发环境 2. 下载地址 3. 使用示例 4. 参考文献 1. 开发环境 Elasticsearch 6.5.4 ES-Hadoop 6.5.4 Hadoop 2.0.0 2. 下载地址 ...

  9. archlinux错误:无法提交处理 (无效或已损坏的软件包)

    1.首先更新一下密钥,如果没有安装archlinux-keyring,请及时安装 sudo pacman-key --refresh-keys 2.重新加载相应的签名密钥 sudo pacman-ke ...

  10. intellij idea15,SVN commit file提示No changes detected

    备注:我用的intellij 15,已经配置了SVN.且我的工程是从SVN导出的   遇到的问题:Subversion提交代码时提示No changes detected,当然新文件想要add也是选项 ...