一个Socket连接管理器(心跳机制)

Unity网络通信:TCP长连接的心跳检测与断线重连机制实现 在网络编程中,TCP长连接是实现实时数据交互的常用技术,尤其在游戏开发、物联网和数字孪生等场景中至关重要。其原理是通过保持客户端与服务器之间的持久连接,实现低延迟的双向通信。然而,网络环境的不稳定性常导致连接假死或异常断开,直接影响应用稳定性。为解决这一问题,心跳检测机制应运而生,它通过定期发送轻量级探测包来主动监测连接活性,一旦超时未响应则触发断线判定。结合指数退避等智能重连策略,系统能在网络波动后自动恢复,显著提升用户体验和系统韧性。本文以Unity引擎和C#为例,深入解析如何构建具备工业级韧性的网络模 阅读详情

为了方便开发人员使用,我在闲暇之余写了一个简单的Socket连接管理的处理程序,程序简单仅供参考!

其他更多精彩,请你访问我的博客:http://cuisuqiang.iteye.com/

 

处理机说明:

读取配置文件,根据配置文件信息保持一个连接配置变量集合。

根据连接变量集合初始化相应的连接。

启动一个线程,处理检查连接的有效性,处理方法是发送心跳包,如发现不能使用的连接,则设置该连接为占用,并启动一个线程去不断的初始化该连接。

用户使用接口根据名称去获得连接,如果是保持的连接则直接从连接集合获取,并检查有效性。如果是不用保持的,则去创建一个连接返回。

备注:如果返回了空的信息,则说明没有正常的连接可以返回,你可以尝试不断的获取。

点击连接到我的博客查看更多内容 !

 

下面来看看代码:

首先创建一个实体对象,对应配置信息

Java代码   收藏代码
  1. package com.socket;  
  2. /** 
  3.  * @说明 连接的公共属性 
  4.  * @author cuisuqiang 
  5.  * @version 1.0 
  6.  * @since 
  7.  */  
  8. public class SocketEntity {  
  9.     /** 
  10.      * 连接的名字,以名字作为Key 
  11.      */  
  12.     private String name;  
  13.     /** 
  14.      * 连接的IP 
  15.      */  
  16.     private String ip;  
  17.     /** 
  18.      * 连接的端口 
  19.      */  
  20.     private int port;  
  21.     /** 
  22.      * 是否保持连接 
  23.      */  
  24.     private boolean keepConn;  
  25.   
  26.     public String getName() {  
  27.         return name;  
  28.     }  
  29.     public void setName(String name) {  
  30.         this.name = name;  
  31.     }  
  32.     public String getIp() {  
  33.         return ip;  
  34.     }  
  35.     public void setIp(String ip) {  
  36.         this.ip = ip;  
  37.     }     
  38.     public int getPort() {  
  39.         return port;  
  40.     }  
  41.     public void setPort(int port) {  
  42.         this.port = port;  
  43.     }  
  44.     public boolean isKeepConn() {  
  45.         return keepConn;  
  46.     }  
  47.     public void setKeepConn(boolean keepConn) {  
  48.         this.keepConn = keepConn;  
  49.     }  
  50. }  

 

创建配置文件socket.properties

socket1_isKeep属性相当于Spring中的单例还是每次创建

 

 

Java代码   收藏代码
  1. # @author cuisuqiang  
  2.   
  3. socket1=socket1  
  4. socket1_ip=127.0.0.1  
  5. socket1_port=8001  
  6. socket1_isKeep=1  
  7.   
  8. socket2=socket2  
  9. socket2_ip=127.0.0.1  
  10. socket2_port=8001  
  11. socket2_isKeep=0  
  12.   
  13. # 连接的数量,一定要和实际配置的数量匹配  
  14. socketConnCount=2  
  15. # 公共的检测间隔 秒  
  16. commonCheckTime=5  

 

然后初始化配置信息和连接,同时该类中有静态变量来保持连接对象

Java代码   收藏代码
  1. package com.socket;  
  2. import java.util.ArrayList;  
  3. import java.util.LinkedHashMap;  
  4. import java.util.List;  
  5. import java.util.Map;  
  6. import java.util.Properties;  
  7. import java.util.concurrent.ExecutorService;  
  8. import java.util.concurrent.Executors;  
  9. import org.apache.commons.logging.Log;  
  10. import org.apache.commons.logging.LogFactory;  
  11. /** 
  12.  * @说明 变量保持 
  13.  * @author cuisuqiang 
  14.  * @version 1.0 
  15.  * @since 
  16.  */  
  17. public class SocketKeep {  
  18.     private static Log logger = LogFactory.getLog(SocketKeep.class);  
  19.     /** 
  20.      * 配置信息加载 
  21.      */  
  22.     public static List<SocketEntity> socketEntityList = new ArrayList<SocketEntity>();  
  23.     /** 
  24.      * 连接对象保持,只保持需要系统保持的连接 
  25.      */  
  26.     public static Map<String, SocketCui> socketMap = new LinkedHashMap<String, SocketCui>();  
  27.     /** 
  28.      * 连接对象是否锁定 1:锁定,其他未锁定 
  29.      */  
  30.     public static Map<String, String> socketIsLock = new LinkedHashMap<String, String>();  
  31.     /** 
  32.      * 共用连接检测间隔 
  33.      */  
  34.     public static int commonCheckTime = 2;  
  35.     /** 
  36.      * 连接的数量,一定要和实际配置的数量匹配 
  37.      */  
  38.     public static int socketConnCount = 0;    
  39.     public static ExecutorService executorService = null;// 线程池  
  40.     /** 
  41.      * 初始化所有连接信息 
  42.      */  
  43.     public static void initSocketKeep() {  
  44.         Properties properties = null;  
  45.         try {  
  46.             properties = new Properties();  
  47.             properties.load(SocketKeep.class.getClassLoader().getResourceAsStream("socket.properties"));  
  48.             logger.warn("加载socket.properties文件成功!");  
  49.         } catch (Exception e) {  
  50.             logger.error("加载socket.properties文件失败!", e);  
  51.             properties = null;  
  52.         }  
  53.         if (null != properties) {  
  54.             try {  
  55.                 commonCheckTime = Integer.parseInt(properties.getProperty("commonCheckTime"));  
  56.                 socketConnCount = Integer.parseInt(properties.getProperty("socketConnCount"));  
  57.                 executorService = Executors.newFixedThreadPool(socketConnCount + 1);  
  58.             } catch (Exception e) {  
  59.                 executorService = Executors.newFixedThreadPool(1);  
  60.                 logger.error("解析共用信息时错误!", e);  
  61.                 // 系统忽略这两个属性的加载异常  
  62.             }  
  63.             SocketEntity socketEntity = null;  
  64.             for (int i = 1; i <= socketConnCount; i++) {  
  65.                 String name = properties.getProperty("socket" + i);  
  66.                 if(null != name){  
  67.                     socketEntity = new SocketEntity();  
  68.                     String ip = properties.getProperty("socket" + i + "_ip");  
  69.                     String port = properties.getProperty("socket" + i + "_port");  
  70.                     String isKeep = properties.getProperty("socket" + i + "_isKeep");  
  71.                       
  72.                     socketEntity.setName(name);  
  73.                     socketEntity.setIp(ip);  
  74.                     socketEntity.setPort(Integer.parseInt(port));  
  75.                     boolean keepConn = false;  
  76.                     if(null != isKeep && "1".equals(isKeep)){  
  77.                         keepConn = true;  
  78.                     }  
  79.                     socketEntity.setKeepConn(keepConn);   
  80.                     socketEntityList.add(socketEntity);  
  81.                 }  
  82.             }  
  83.         }  
  84.         logger.warn("加载Socket连接配置信息结束!");         
  85.         logger.warn("开始初始化Socket连接!");  
  86.         SocketCui socket = null;  
  87.         for(SocketEntity socketEntity : socketEntityList){  
  88.             if(null != socketEntity && socketEntity.isKeepConn()){  
  89.                 try {  
  90.                     socket = new SocketCui(socketEntity.getIp(),socketEntity.getPort());  
  91.                     socket.setSoTimeout(0);  
  92.                     socket.setKeepAlive(true);  
  93.                     socket.setName(socketEntity.getName());  
  94.                 } catch (Exception e) {  
  95.                     logger.error("初始化某个连接时错误!错误的连接将放弃!资源名称:" + socketEntity.getName(), e);  
  96.                     socket = null;  
  97.                 }  
  98.                 if(null != socket){  
  99.                     socketMap.put(socketEntity.getName(), socket);  
  100.                 }else{  
  101.                     socketMap.put(socketEntity.getName(), new SocketCui());                   
  102.                 }  
  103.                 socketIsLock.put(socketEntity.getName(), "0");  
  104.             }  
  105.         }         
  106.         // 开始执行检查  
  107.         executorService.execute(new CheckThread());  
  108.         logger.warn("初始化Socket连接结束!");  
  109.     }  
  110. }  

 

启动的线程是用于检查连接的

Java代码   收藏代码
  1. package com.socket;  
  2. import org.apache.commons.logging.Log;  
  3. import org.apache.commons.logging.LogFactory;  
  4. /** 
  5.  * @说明 轮询检测某个连接当前是否可用 
  6.  * @author cuisuqiang 
  7.  * @version 1.0 
  8.  * @since 当遇到一个错误的连接,将会启动重连,同时挂起该连接的使用 
  9.  */  
  10. public class CheckThread  implements Runnable {  
  11.     private static Log logger = LogFactory.getLog(CheckThread.class);     
  12.     public void run() {  
  13.         while(true){  
  14.             SocketCui socket = null;  
  15.             for(SocketEntity socketEntity : SocketKeep.socketEntityList){  
  16.                 if(null != socketEntity && socketEntity.isKeepConn()){  
  17.                     String isLock = SocketKeep.socketIsLock.get(socketEntity.getName());  
  18.                     // 如果当前未被使用  
  19.                     if(!"1".equals(isLock)){  
  20.                         // 锁定引用  
  21.                         SocketKeep.socketIsLock.put(socketEntity.getName(), "1");  
  22.                         socket = SocketKeep.socketMap.get(socketEntity.getName());  
  23.                         try {  
  24.                             // 发送一个心跳包  
  25.                             socket.sendUrgentData(0xFF);  
  26.                             // 释放资源  
  27.                             SocketKeep.socketIsLock.put(socketEntity.getName(), "0");  
  28.                         } catch (Exception e) {  
  29.                             logger.error("检查连接时异常!启动重连!资源名称:" + socketEntity.getName(), e);  
  30.                             // 如果异常,应该建立一个线程去初始化该连接  
  31.                             InitSocket initS = new InitSocket(socketEntity.getName());  
  32.                             new Thread(initS).start();  
  33.                         }  
  34.                     }  
  35.                 }  
  36.             }  
  37.             // 执行间隔  
  38.             try {  
  39.                 logger.error("本次检测结束!");  
  40.                 Thread.sleep(SocketKeep.commonCheckTime * 1000);  
  41.             } catch (Exception e) {  
  42.             }             
  43.         }  
  44.     }  
  45. }  

 

当检查线程发现无效的连接时会启动新的线程初始化该连接

Java代码   收藏代码
  1. package com.socket;  
  2. import org.apache.commons.logging.Log;  
  3. import org.apache.commons.logging.LogFactory;  
  4. /** 
  5.  * @说明 负责初始化失效的连接 
  6.  * @author cuisuqiang 
  7.  * @version 1.0 
  8.  * @since 
  9.  */  
  10. public class InitSocket implements Runnable{      
  11.     private static Log logger = LogFactory.getLog(InitSocket.class);      
  12.     /** 
  13.      * 是否有某个连接的配置信息,只有有配置信息才能建立连接 
  14.      */  
  15.     private static boolean isHave = false;    
  16.     private SocketEntity socketEntity = null;     
  17.     private String name;      
  18.     public InitSocket(String name){  
  19.         this.name = name;  
  20.         // 检测是否有某个连接的配置信息  
  21.         for(SocketEntity socketEntity : SocketKeep.socketEntityList){  
  22.             if(null != socketEntity && socketEntity.isKeepConn()){  
  23.                 if(socketEntity.getName().equals(name)){  
  24.                     this.setSocketEntity(socketEntity);  
  25.                     isHave = true;  
  26.                 }  
  27.             }  
  28.         }  
  29.     }  
  30.     public void run() {  
  31.         boolean isError = true;  
  32.         SocketCui socket = null;  
  33.         if(isHave){  
  34.             while(isError){  
  35.                 try {  
  36.                     socket = new SocketCui(this.getSocketEntity().getIp(),this.getSocketEntity().getPort());  
  37.                     socket.setSoTimeout(0);  
  38.                     socket.setKeepAlive(true);  
  39.                     socket.setName(this.name);  
  40.                     // 发送一个心跳包  
  41.                     socket.sendUrgentData(0xFF);                          
  42.                 } catch (Exception e) {  
  43.                     logger.error("建立资源连接时错误!资源:" + this.name, e);  
  44.                     socket = null;  
  45.                 }  
  46.                 if(null != socket){  
  47.                     SocketKeep.socketMap.put(this.getSocketEntity().getName(), socket);  
  48.                     // 设置连接当前可用  
  49.                     SocketKeep.socketIsLock.put(this.getSocketEntity().getName(), "0");  
  50.                     logger.warn("建立资源连接成功!资源名称:" + this.name);  
  51.                     isError = false;  
  52.                 }  
  53.                 try {  
  54.                     Thread.sleep(2 * 1000);  
  55.                 } catch (Exception e) {  
  56.                 }  
  57.                   
  58.             }  
  59.         }else{  
  60.             logger.error("没有发现指定资源的配置信息!资源名称:" + this.name);  
  61.         }  
  62.         logger.warn("初始化资源执行结束!资源名称:" + this.name);  
  63.     }  
  64.     public SocketEntity getSocketEntity() {  
  65.         return socketEntity;  
  66.     }  
  67.     public void setSocketEntity(SocketEntity socketEntity) {  
  68.         this.socketEntity = socketEntity;  
  69.     }  
  70. }  

 

同时注意,用户在使用连接后会调用关闭方法。我们是不能让连接关闭的,要保持常连接。所以如果用户指定的是保持这个连接,那么返回的连接对象就不是原来的Socket对象了,我们要重写这个对象

Java代码   收藏代码
  1. package com.socket;  
  2. import java.io.IOException;  
  3. import java.net.Socket;  
  4. import java.net.UnknownHostException;  
  5. /** 
  6.  * @说明 被重新定义的连接对象,增加了名字这个属性,重写了关闭的方法 
  7.  * @author cuisuqiang 
  8.  * @version 1.0 
  9.  * @since 
  10.  */  
  11. public class SocketCui extends Socket{    
  12.     /** 
  13.      * 为对象增加名称属性 
  14.      */  
  15.     private String name;      
  16.     public SocketCui() {  
  17.     }     
  18.     public SocketCui(String ip,int port) throws UnknownHostException, IOException{  
  19.         super(ip, port);  
  20.     }     
  21.     /** 
  22.      * 覆盖关闭的方法 
  23.      */  
  24.     @Override  
  25.     public synchronized void close() throws IOException {  
  26.         SocketKeep.socketIsLock.put(this.name, "0");  
  27.     }  
  28.     public String getName() {  
  29.         return name;  
  30.     }  
  31.     public void setName(String name) {  
  32.         this.name = name;  
  33.     }  
  34. }  

 这样,关闭时只会解除其占用,而不会真正关闭该连接。

 

我们来写一个服务端,这个服务端一直接受连接,并检查连接的有效性,当失效时不处理

同时打印接收到的连接信息

Java代码   收藏代码
  1. package com.test;  
  2. import java.net.*;  
  3. import java.text.SimpleDateFormat;  
  4. import java.util.Date;  
  5. /** 
  6.  * @说明 服务端,始终接受连接 
  7.  * @author cuisuqiang 
  8.  * @version 1.0 
  9.  * @since  
  10.  */  
  11. public class ServiceTest {  
  12.     public static void main(String[] args) {  
  13.         try {  
  14.             ServerSocket ss1 = new ServerSocket(8001);  
  15.             Runnable accumelatora1 = new Accumulatort(ss1);  
  16.             Thread threada = new Thread(accumelatora1, "ThreadA");  
  17.             threada.start();  
  18.             System.out.println("服务启动完毕!");  
  19.         } catch (Exception e) {  
  20.             e.printStackTrace();  
  21.         }  
  22.     }  
  23. }  
  24. class Accumulatort implements Runnable {  
  25.     ServerSocket ss = null;  
  26.     public Accumulatort(ServerSocket s) {  
  27.         this.ss = s;  
  28.     }  
  29.     @SuppressWarnings("unchecked")  
  30.     public void run() {  
  31.         try {  
  32.             SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  33.             while (true) {  
  34.                 Socket s = ss.accept();  
  35.                 System.out.println(format.format(new Date()) + " " + "---------收到请求!");  
  36.                 new Thread(new ServiceImpl(s)).start();  
  37.             }  
  38.         } catch (Exception e) {  
  39.             e.printStackTrace();  
  40.         }  
  41.     }  
  42. }  

 服务端处理连接的实现类

Java代码   收藏代码
  1. package com.test;  
  2. import java.net.Socket;  
  3. /** 
  4.  * @说明 循环发送心跳包保持连接属性 
  5.  * @author cuisuqiang 
  6.  * @version 1.0 
  7.  * @since  
  8.  */  
  9. public class ServiceImpl implements Runnable {  
  10.     Socket socket = null;  
  11.     public ServiceImpl(Socket s) {  
  12.         this.socket = s;  
  13.     }  
  14.     public void run() {  
  15.         boolean isKeep = true;  
  16.         try {  
  17.             while (isKeep) {  
  18.                 socket.sendUrgentData(0xFF);  
  19.                 Thread.sleep(1 * 1000);  
  20.             }  
  21.         } catch (Exception e) {  
  22.             isKeep = false;  
  23.         }  
  24.     }  
  25. }  

 

先启动服务端,然后我们再写一个测试端,这个测试类会不断去管理器中获取相应的连接,同时打印连接信息

通过打印的连接信息,我们可以知道获取的是不是同一个对象

同时如果你一直获取单例的对象,那么可能出现该连接被检查线程占用的情况

Java代码   收藏代码
  1. package com.test;  
  2. import java.net.Socket;  
  3. import java.text.SimpleDateFormat;  
  4. import java.util.Date;  
  5. import com.socket.CommonSocket;  
  6. import com.socket.SocketKeep;  
  7. /** 
  8.  * @说明 循环去请求获得相应的连接然后打印连接地址 
  9.  * @author cuisuqiang 
  10.  * @version 1.0 
  11.  * @since  
  12.  */  
  13. public class GetSocketTest {  
  14.     public static void main(String[] args) {  
  15.         SocketKeep.initSocketKeep();  
  16.         while(true){              
  17.             SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  18.             try {                 
  19.                 Socket socket1 = CommonSocket.getSocketByName("socket1");  
  20.                 if(null != socket1){  
  21.                     System.out.println(format.format(new Date()) + " " + socket1.toString());  
  22.                     socket1.close();      
  23.                 }  
  24.             } catch (Exception e) {  
  25.                 e.printStackTrace();  
  26.             }  
  27.             try {  
  28.                 Socket socket2 = CommonSocket.getSocketByName("socket2");  
  29.                 if(null != socket2){  
  30.                     System.out.println(format.format(new Date()) + " " + socket2.toString());  
  31.                     socket2.close();  
  32.                 }  
  33.             } catch (Exception e) {  
  34.                 e.printStackTrace();  
  35.             }  
  36.             try {  
  37.                 Thread.sleep(1000);  
  38.             } catch (Exception e) {  
  39.                   
  40.             }  
  41.         }  
  42.     }  
  43. }  

 

当然别忘了加日志的包!

 

我们来看一下服务端的打印信息

Java代码   收藏代码
  1. 服务启动完毕!  
  2. 2012-04-18 16:59:48 ---------收到请求!  
  3. 2012-04-18 16:59:48 ---------收到请求!  
  4. 2012-04-18 16:59:49 ---------收到请求!  
  5. 2012-04-18 16:59:50 ---------收到请求!  
  6. 2012-04-18 16:59:51 ---------收到请求!  
  7. 2012-04-18 16:59:52 ---------收到请求!  
  8. 2012-04-18 16:59:53 ---------收到请求!  
  9. 2012-04-18 16:59:54 ---------收到请求!  
  10. 2012-04-18 16:59:55 ---------收到请求!  
  11. 2012-04-18 16:59:56 ---------收到请求!  
  12. 2012-04-18 16:59:57 ---------收到请求!  
  13. 2012-04-18 16:59:58 ---------收到请求!  
  14. 2012-04-18 16:59:59 ---------收到请求!  

 

我们可以看到,除刚开始外,会一直收到连接,因为我们一直在获取一个非单例的连接对象

我们再看一下客户端打印信息

Java代码   收藏代码
  1. 2012-04-18 16:59:48 Socket[addr=/127.0.0.1,port=8001,localport=4389]  
  2. 2012-04-18 16:59:48 Socket[addr=/127.0.0.1,port=8001,localport=4390]  
  3. 2012-04-18 16:59:49 Socket[addr=/127.0.0.1,port=8001,localport=4389]  
  4. 2012-04-18 16:59:49 Socket[addr=/127.0.0.1,port=8001,localport=4391]  
  5. 2012-04-18 16:59:50 Socket[addr=/127.0.0.1,port=8001,localport=4389]  
  6. 2012-04-18 16:59:50 Socket[addr=/127.0.0.1,port=8001,localport=4392]  
  7. 2012-04-18 16:59:51 Socket[addr=/127.0.0.1,port=8001,localport=4389]  
  8. 2012-04-18 16:59:51 Socket[addr=/127.0.0.1,port=8001,localport=4393]  
  9. 2012-04-18 16:59:52 Socket[addr=/127.0.0.1,port=8001,localport=4389]  
  10. 2012-04-18 16:59:52 Socket[addr=/127.0.0.1,port=8001,localport=4394]  
  11. 2012-04-18 16:59:53 Socket[addr=/127.0.0.1,port=8001,localport=4389]  
  12. 2012-04-18 16:59:53 Socket[addr=/127.0.0.1,port=8001,localport=4395]  
  13. 2012-04-18 16:59:54 Socket[addr=/127.0.0.1,port=8001,localport=4389]  
  14. 2012-04-18 16:59:54 Socket[addr=/127.0.0.1,port=8001,localport=4396]  
  15. 2012-04-18 16:59:55 Socket[addr=/127.0.0.1,port=8001,localport=4389]  
  16. 2012-04-18 16:59:55 Socket[addr=/127.0.0.1,port=8001,localport=4397]  
  17. 2012-04-18 16:59:56 Socket[addr=/127.0.0.1,port=8001,localport=4389]  
  18. 2012-04-18 16:59:56 Socket[addr=/127.0.0.1,port=8001,localport=4398]  
  19. 2012-04-18 16:59:57 Socket[addr=/127.0.0.1,port=8001,localport=4389]  
  20. 2012-04-18 16:59:57 Socket[addr=/127.0.0.1,port=8001,localport=4399]  
  21. 2012-04-18 16:59:58 Socket[addr=/127.0.0.1,port=8001,localport=4400]  
  22. 2012-04-18 16:59:59 Socket[addr=/127.0.0.1,port=8001,localport=4389]  
  23. 2012-04-18 16:59:59 Socket[addr=/127.0.0.1,port=8001,localport=4401]  

 

可以看到,socket1是一直被保持的,而socket2每次创建一个

再看一下日志

Java代码   收藏代码
  1. 2012-04-18 16:59:48 [com.socket.SocketKeep]-[WARN] 加载socket.properties文件成功!  
  2. 2012-04-18 16:59:48 [com.socket.SocketKeep]-[WARN] 加载Socket连接配置信息结束!  
  3. 2012-04-18 16:59:48 [com.socket.SocketKeep]-[WARN] 开始初始化Socket连接!  
  4. 2012-04-18 16:59:48 [com.socket.SocketKeep]-[WARN] 初始化Socket连接结束!  
  5. 2012-04-18 16:59:48 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2  
  6. 2012-04-18 16:59:48 [com.socket.CheckThread]-[ERROR] 本次检测结束!  
  7. 2012-04-18 16:59:49 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2  
  8. 2012-04-18 16:59:50 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2  
  9. 2012-04-18 16:59:51 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2  
  10. 2012-04-18 16:59:52 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2  
  11. 2012-04-18 16:59:53 [com.socket.CheckThread]-[ERROR] 本次检测结束!  
  12. 2012-04-18 16:59:53 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2  
  13. 2012-04-18 16:59:54 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2  
  14. 2012-04-18 16:59:55 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2  
  15. 2012-04-18 16:59:56 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2  
  16. 2012-04-18 16:59:57 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2  
  17. 2012-04-18 16:59:58 [com.socket.CommonSocket]-[WARN] 当前连接正被占用,请稍候尝试!资源名称:socket1  
  18. 2012-04-18 16:59:58 [com.socket.CheckThread]-[ERROR] 本次检测结束!  
  19. 2012-04-18 16:59:58 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2  
  20. 2012-04-18 16:59:59 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2  

 

可以看到,socket2会一直被重新创建,而且socket1会发现被占用的情况。

你也可以中断服务端然后再重启服务端试试,看看效果。

 

如果你喜欢该博客,欢迎您访问我的博客:http://cuisuqiang.iteye.com/ !

源码下面可以下载,仅供参考!转载说明出处,谢谢合作!

Socket 心跳管理器 阅读详情

相关推荐

SuperSocket网络通信ClientEngine客户端部分

SuperSocket网络通信ClientEngine客户端部分

C#/.NET SuperSocket的使用

首先安装:SuperSocket.Engine SuperSoket的三大对象: Session: 每一个用户连接就是一个Session AppServer: Socket服务器实例 Commands: 客户端向服务器发送消息的命令集合 首先在配置文件加入如下配置 <configSections> <section name="superSocket" type="S...

一点一滴 2万+

FPGA 上的 YOLOv8 实时目标检测:从模型优化到硬件加速全解析

本文全面解析了将YOLOv8目标检测模型部署到FPGA进行硬件加速的完整流程。通过模型裁剪、混合精度量化等优化手段,结合HLS设计专用计算引擎与数据流,实现了高能效比的实时推理。文章深入探讨了软硬协同部署、性能调试与优化技巧,为追求极致实时性与低功耗的边缘AI应用提供了从算法到硬件的实战指南。

weixin_29031161的博客 604

Socket心跳包机制

心跳包的发送,通常有两种技术: 方法1:应用层自己实现的心跳包 由应用程序自己发送心跳包来检测连接是否正常,大致的方法是:服务器在一个 Timer事件中定时 向客户端发送一个短小精悍的数据包,然后启动一个低级别的线程,在该线程中不断检测客户端的回应, 如果在一定时间内没有收到客户端的回应,即认为客户端已经掉线;同样,如果客户端在一定时间内没 有收到服务器的心跳包,则认为连接不可用。 方法2

kobejayandy的专栏 3300

TI C2000 , .cmd文件,.TI.ramfunc,将程序分配为在RAM中运行/Flash运行的配置方法和原理讲解,(如28375S ,280049,28335等等芯片)

1 基础知识讲解 1.1 在RAM中运行还是在flash中运行指的是什么? 1.1.1 背景:程序编译完之后生成的.bin文件,.bin文件中包含代码段,代码段中全是汇编指令,而汇编指令中包含多个函数,各函数头的地址不同,如存在A函数和B函数,假设RAM地址区间是0x0000-0x8000,flash的地址区间是0xA000-0xF000,A函数要调用B函数,则A函数调用B函数的那段汇编指令中包含的是B函数头的存储地址,A函数和B函数都在flash中有存储 1.1.2 情况1:B函数是配置为在RAM中运行,

weixin_43776284的博客 8065

5.6华为OD机试真题 新系统 - 匹配命令行前缀关键字 (Java/Py/C/C++/Js/Go)

5.6华为OD机试真题 新系统 - 匹配命令行前缀关键字本文整理华为 OD 机试新系统机考真题库,涵盖高频考点精析与完整题目内容,提供 C 语言、Java、C++、Python、Go、JavaScript 多语言源码实现,搭配详细解题思路讲解,并支持在线 OJ 刷题练习,助力高效备考华为 OD 机试新系统。

算法大师! 2125

socket入门到精通(二)心跳机制

  今天和大家一块学习一下即时通讯中的心跳机制。有一些小伙伴私下问为什么不直接开始上干货。我这边个人感觉是socket是一个比较大,牵扯的知识点比较多,从外围一点一点的了解学习socket,然后完全掌握。 心跳机制   心跳机制的原理很简单:客户端每隔N秒向服务端发送一个心跳消息,服务端收到心跳消息后,回复同样的心跳消息给客户端。如果服务端或客户端在M秒(M&gt;N)内都没有收到包括心跳消息在内...

1万+

华为OD机试 新系统真题-收集灵草(Java/Py/C++/Go/C/Js)

华为OD机试 新系统真题【收集灵草】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题【收集灵草】多语言题解。

qq_45776114的博客 544

一个Socket连接管理器

为了方便开发人员使用,我在闲暇之余写了一个简单的Socket连接管理的处理程序,程序简单仅供参考! 其他更多精彩,请你访问我的博客:http://cuisuqiang.iteye.com/   处理机说明: 读取配置文件,根据配置文件信息保持一个连接配置变量集合。 根据连接变量集合初始化相应的连接。 启动一个线程,处理检查连接的有效性,处理方法是发送心跳包,如发现不能使用的连接,则设...

java小强 294

IEC 104电力规约详细解读(五) - 遥控

主站下发遥控选择命令,子站进行遥控选择返校,若成功则回答遥控选择成功报文,若失败则回答失败报文。主站下发取消遥控命令或者遥控执行命令行。子站皆以报文的镜像确认。遥控命令用来实现对一个可操作设备状态的改变。在配电自动化中,包括单点命令和双点命令。通常,单点命令用于控制单点信息对象;双点命令用于控制双点信息对象。

张二狗的博客 7349

uniapp - 实现手机端H5网页直播功能效果,拿到后端推流地址或播放资源后进行前端的流畅播放和画面展示功能,uniapp实时推流/拉流直播系统详细教程(提供完整示例代码,一键复制开箱即用!)

uniapp,h5网页,手机端H5网页,uniapp h5直播功能页面,uniapp直播实时推送显示,uniapp h5直播怎么做,怎么使用uniapp去实现移动端手机H5直播,h5 vue+uniapp怎么做直播,uniapp中使用videojs构建H5直播播放器,uniappH5端怎么实现直播推流,uniapp如何实现直播,直播聊天功能,h5平台端直播用什么插件,uniapp手机网页直播用什么组件插件播放器,uniapp手机H5直播播放器详细教程,直播项目,怎么使用vue+uniapp怎么做H5实时直播

✅ 精选推荐文章 8792

Socket心跳包机制总结

跳包之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。     在TCP的机制里面,本身是存在有心跳包的机制的,也就是TCP的选项:SO_KEEPALIVE。系统默认是设置的2小时的心跳频率。但是它检查不到机器断电、网线拔出、防火墙这些断线。而

全冉 的博客 5万+

Fiori Elements 的 Adapting the UI: Analytical List Page 全解析与实战

摘要:本文详细介绍了如何通过SAP Fiori elements的UI Adaptation功能对Analytical List Page(ALP)进行细粒度适配。重点讲解了页头过滤区、图表区域、表格区域等关键组件的可配置属性,包括过滤区折叠/固定、图表类型限制、表格滚动阈值等实用功能。文章强调这些适配均可通过可视化工具完成,无需修改业务代码,并提供了典型场景的实践清单。适用于SAP Fiori elements for OData V2项目,建议配合SAP Fiori tools的Adaptation E

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 432

Socket连接管理器(心跳机制)(转)

:http://cuisuqiang.iteye.com/ 处理机说明: 读取配置文件,根据配置文件信息保持一个连接配置变量集合。 根据连接变量集合初始化相应的连接。 启动一个线程,处理检查连接的有效性,处理方法是发送心跳包,如发现不能使用的连接,则设置该连接为占用,并启动一个线程去不断的初始化该连接。 用户使用接口根据名称去获得连接,如果是保持的连接则直接从连接集合获取,并检查有效性。

记忆沉思 1273

LAMMPS实例教程—In文件详解

本博文主要介绍专栏 《LAMMPS实例教程—In文件详解》, 针对于 Lammps 可分析的物理过程进行讲解,提供 In 文件以及后处理方法,持续更新。 目前分为8个专题: 1)力学专题、2)传热专题、3)相变专题、4)界面专题、5)吸附专题、6)流动专题、7)物性专题以及8)基础理论专题。

Rockyoung 3万+

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

OkSocket心跳机制与重连策略:构建稳定可靠的网络连接

在Android移动应用开发中,网络连接的稳定性直接关系到用户体验和应用性能。OkSocket作为一款专为Android应用设计的阻塞式Socket客户端库,通过其强大的心跳机制与智能重连策略,为开发者提供了稳定可靠的网络连接解决方案。本文将深入解析OkSocket如何通过心跳检测和重连管理来确保网络连接的持久性和可靠性。 ## 📡 心跳机制:连接健康的守护者 OkSocket的心跳机制是其

gitblog_00658的博客 1095

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

java nio socket长连接_nio实现Socket长连接和心跳

前段时间用bio方式,也就是传统io实现了socket的长连接和心跳,总觉着服务端开启多线程管理socket连接的方式过于消耗资源,数据并发的情况下可能会影响到性能,因此就尝试使用nio改进原来的代码。然而改进的过程却不像我起初设想的那般容易,可以说一波三折,原因主要是nio读写都是字节流,LZ一开始依然通过ObjectOutputStream.writeObject直接向Socket服务端发送数...

weixin_30424461的博客 1108

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Socket心跳包,Socket长连接之旅

原地址:http://blog.youkuaiyun.com/qingzi635533/article/details/24302175 最近公司项目要做一个同步功能,需要涉及到服务器与客户端的长连接,当时头脑里第一联想到的就是Socket啊,虽然Socket自己当时也了解过一点,但是长连接还是没做过啊,更何况心跳包,个个都是以前没有接触过的,于是就在网上找资料学习,终于功夫不负有心人找到了对应的资料,也实...

System.o的博客 3379

心跳重连机制详解

在工控机上位机系统(如 C# 开发的工业监控软件)中,心跳(Heartbeat)与重连(Reconnect)机制是确保通信稳定、系统可靠的核心设计,尤其在工业现场的网络抖动、设备断电、干扰频繁的环境下。这两种机制往往结合使用:心跳用于检测连接存活,重连用于恢复中断连接。下面从原理、工业场景应用、实现步骤、代码示例、优化建议五个维度详细拆解。:心跳检测断开 → 触发重连 → 重连成功后恢复心跳 + 数据同步。:心跳避免“假连接”,重连减少停机时间(从分钟级到秒级)。

专注于前沿技术的探索与实践,热衷于从代码逻辑、架构设计到行业应用等多维度深挖技术本质。期待在技术之路上与你共同成长~ 561

通过心跳包对时间

using UnityEngine; namespace YouYou { /// <summary> /// Socket组件 /// </summary> public class SocketComponent : YouYouBaseComponent, IUpdateComponent { ///...

qq_35647121的博客 719
上一篇: JVM 内存分配详解
下一篇: 解决spring3 mvc中乱码的问题。
java85140031
博客等级 码龄18年 34粉丝 51原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值