Java如何实现长连接

实现原理:

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

客户端:

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

服务端:

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

心跳反映的代码:

package com.java.excel.keepalive;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
    *
    * 维持连接的消息对象(心跳对象)
    */
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维持连接包";
  }

}

服务端

package com.java.excel.keepalive;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ConcurrentHashMap;

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());
    }

  }

}

客户端

package com.java.excel.keepalive;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.ConcurrentHashMap;

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 = 1000;
    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();
        }
      }
    }
  }

}

以上就是Java如何实现长连接的详细内容,更多关于java实现长连接的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java中Spring Boot+Socket实现与html页面的长连接实例详解

    Spring Boot+Socket实现与html页面的长连接,客户端给服务器端发消息,服务器给客户端轮询发送消息,附案例源码 功能介绍 客户端给所有在线用户发送消息客户端给指定在线用户发送消息服务器给客户端发送消息(轮询方式) 注意:socket只是实现一些简单的功能,具体的还需根据自身情况,代码稍微改造下 项目搭建 项目结构图 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xml

  • java与微信小程序实现websocket长连接

    本文实例为大家分享了java与微信小程序实现websocket长连接的具体代码,供大家参考,具体内容如下 背景: 需要在小程序实现地图固定坐标下实时查看消息 java环境 :tomcat7 jdk1.7 1.java websocket 类 package com.qs.util; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.

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

    实现原理:        长连接的维持,是要客户端程序,定时向服务端程序,发送一个维持连接包的.        如果,长时间未发送维持连接包,服务端程序将断开连接. 客户端:        Client通过持有Socket的对象,可以随时(使用sendObject方法)发送Massage Object(消息)给服务端.        如果keepAliveDelay毫秒(程序中是2秒)内未发送任何数据,则自动发送一个KeepAlive Object(心跳)给服务端,用于维持连接.       

  • java socket长连接中解决read阻塞的3个办法

    解决的方法有3个 : 1 约定发送的数据长度,比如 http的 keepAlive 就是必须依赖这个的 Content-Length 2 设置超时的时间,根据我的经验,只有在Socket级别设置才有效. 复制代码 代码如下: Socket socket = new Socket(host,port); socket.setSoTimeout(100); // 如果超过100毫秒还没有数据,则抛出 SocketTimeoutException 3 让发送端发送完数据后,关闭连接. 这个在Http的

  • Java Web项目中使用Socket通信多线程、长连接的方法

    很多时候在javaweb项目中我们需要用到Socket通信来实现功能,在web中使用Socket我们需要建立一个监听程序,在程序启动时,启动socket监听.我们的应用场景是在java项目中,需要外接如一个硬件设备,通过tcp通信,获取设备传上来的数据,并对数据做回应. 先看一下web的监听代码: import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class

  • Java如何实现长连接

    实现原理: 长连接的维持,是要客户端程序,定时向服务端程序,发送一个维持连接包的. 如果,长时间未发送维持连接包,服务端程序将断开连接. 客户端: Client通过持有Socket的对象,可以随时(使用sendObject方法)发送Massage Object(消息)给服务端. 如果keepAliveDelay毫秒(程序中是2秒)内未发送任何数据,则自动发送一个KeepAlive Object(心跳)给服务端,用于维持连接. 由于,我们向服务端,可以发送很多不同的消息对象,服务端也可以返回不同的

  • Java中Socket设置连接超时的代码分享

    前言 大家都知道Java的Socket如果连接不到指定IP和Port的主机,就会抛出IOException异常,而通常情况下,从连接到出现IOException会有一段不定的时长,就是所谓的超时时长,如果超时时长过长,在开发时会影响测试,如果代码成了产品,又会非常的影响用户体验,那么要怎么设置这个时长呢?下面来一起看看详细的介绍: Socket的类提供了一个方法: public void connect(SocketAddress endpoint, int timeout) throws IO

  • 使用HTTPclient保持长连接

    目录 HTTPclient保持长连接 首先解释一下什么是长连接 如何在java中实现一个长连接呢 httpclient因为保持永久长连接造成连接吊死的问题 添加策略后,问题解决 HTTPclient保持长连接 首先解释一下什么是长连接 当我们向一台服务器发起请求时,我们需要和对方建立一条通道,去传输数据,所谓的短连接,就是说我们建立起了通道,然后在传输完数据,就把通道摧毁,下次需要的时候再重新去建立通道. 长连接呢,就是指,我们建立了一条通道,传递完数据后,先不摧毁,下次如果还需要传输数据,就复

  • Springboot并发调优之大事务和长连接

    目录 1.背景 2.主要参数释义: 2.1 tomcat主要并发参数释义 2.2 数据库连接池参数 2.3 数据库连接数 3.测试程序 4.jmeter测试 4.1.快速组 4.2.慢速组 4.3.对照分析 5.问题与优化 5.1.问题 5.2 .排查 5.3.核心 5.4.调优 6.优化实验 6.1 手动事务 6.2.优化第一组测试 6.3.优化第二组测试 7.总结 1.背景 在当前这个快速开发的环境下,很多时候我们的应用都是测试好好的,正式环境并发一高就一团糟.不了解并发相关参数,看不懂压测

  • Java使用JDBC驱动连接MySQL数据库

    Java使用JDBC驱动连接MySQL数据库的步骤: 1.下载驱动,导入jar包 2.加载驱动 3.设置连接 连接成功后就是一些对数据库中数据的操作 1.下载驱动,导入jar包 当你看到jdbc目录下有相应的jar包说明第一步操作已经完成. 2.加载驱动 3.设置连接 import java.sql.Connection; import java.sql.DriverManager; public class DB_Helper { public static void main(String[

随机推荐