为了方便开发人员使用,我在闲暇之余写了一个简单的Socket连接管理的处理程序,程序简单仅供参考!
其他更多精彩,请你访问我的博客:http://cuisuqiang.iteye.com/
处理机说明:
读取配置文件,根据配置文件信息保持一个连接配置变量集合。
根据连接变量集合初始化相应的连接。
启动一个线程,处理检查连接的有效性,处理方法是发送心跳包,如发现不能使用的连接,则设置该连接为占用,并启动一个线程去不断的初始化该连接。
用户使用接口根据名称去获得连接,如果是保持的连接则直接从连接集合获取,并检查有效性。如果是不用保持的,则去创建一个连接返回。
备注:如果返回了空的信息,则说明没有正常的连接可以返回,你可以尝试不断的获取。
点击连接到我的博客查看更多内容 !
下面来看看代码:
首先创建一个实体对象,对应配置信息
- package com.socket;
-
-
-
-
-
-
- public class SocketEntity {
-
-
-
- private String name;
-
-
-
- private String ip;
-
-
-
- private int port;
-
-
-
- private boolean keepConn;
-
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getIp() {
- return ip;
- }
- public void setIp(String ip) {
- this.ip = ip;
- }
- public int getPort() {
- return port;
- }
- public void setPort(int port) {
- this.port = port;
- }
- public boolean isKeepConn() {
- return keepConn;
- }
- public void setKeepConn(boolean keepConn) {
- this.keepConn = keepConn;
- }
- }
创建配置文件socket.properties
socket1_isKeep属性相当于Spring中的单例还是每次创建
- # @author cuisuqiang
-
- socket1=socket1
- socket1_ip=127.0.0.1
- socket1_port=8001
- socket1_isKeep=1
-
- socket2=socket2
- socket2_ip=127.0.0.1
- socket2_port=8001
- socket2_isKeep=0
-
- # 连接的数量,一定要和实际配置的数量匹配
- socketConnCount=2
- # 公共的检测间隔 秒
- commonCheckTime=5
然后初始化配置信息和连接,同时该类中有静态变量来保持连接对象
- package com.socket;
- import java.util.ArrayList;
- import java.util.LinkedHashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.Properties;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
-
-
-
-
-
- public class SocketKeep {
- private static Log logger = LogFactory.getLog(SocketKeep.class);
-
-
-
- public static List<SocketEntity> socketEntityList = new ArrayList<SocketEntity>();
-
-
-
- public static Map<String, SocketCui> socketMap = new LinkedHashMap<String, SocketCui>();
-
-
-
- public static Map<String, String> socketIsLock = new LinkedHashMap<String, String>();
-
-
-
- public static int commonCheckTime = 2;
-
-
-
- public static int socketConnCount = 0;
- public static ExecutorService executorService = null;
-
-
-
- public static void initSocketKeep() {
- Properties properties = null;
- try {
- properties = new Properties();
- properties.load(SocketKeep.class.getClassLoader().getResourceAsStream("socket.properties"));
- logger.warn("加载socket.properties文件成功!");
- } catch (Exception e) {
- logger.error("加载socket.properties文件失败!", e);
- properties = null;
- }
- if (null != properties) {
- try {
- commonCheckTime = Integer.parseInt(properties.getProperty("commonCheckTime"));
- socketConnCount = Integer.parseInt(properties.getProperty("socketConnCount"));
- executorService = Executors.newFixedThreadPool(socketConnCount + 1);
- } catch (Exception e) {
- executorService = Executors.newFixedThreadPool(1);
- logger.error("解析共用信息时错误!", e);
-
- }
- SocketEntity socketEntity = null;
- for (int i = 1; i <= socketConnCount; i++) {
- String name = properties.getProperty("socket" + i);
- if(null != name){
- socketEntity = new SocketEntity();
- String ip = properties.getProperty("socket" + i + "_ip");
- String port = properties.getProperty("socket" + i + "_port");
- String isKeep = properties.getProperty("socket" + i + "_isKeep");
-
- socketEntity.setName(name);
- socketEntity.setIp(ip);
- socketEntity.setPort(Integer.parseInt(port));
- boolean keepConn = false;
- if(null != isKeep && "1".equals(isKeep)){
- keepConn = true;
- }
- socketEntity.setKeepConn(keepConn);
- socketEntityList.add(socketEntity);
- }
- }
- }
- logger.warn("加载Socket连接配置信息结束!");
- logger.warn("开始初始化Socket连接!");
- SocketCui socket = null;
- for(SocketEntity socketEntity : socketEntityList){
- if(null != socketEntity && socketEntity.isKeepConn()){
- try {
- socket = new SocketCui(socketEntity.getIp(),socketEntity.getPort());
- socket.setSoTimeout(0);
- socket.setKeepAlive(true);
- socket.setName(socketEntity.getName());
- } catch (Exception e) {
- logger.error("初始化某个连接时错误!错误的连接将放弃!资源名称:" + socketEntity.getName(), e);
- socket = null;
- }
- if(null != socket){
- socketMap.put(socketEntity.getName(), socket);
- }else{
- socketMap.put(socketEntity.getName(), new SocketCui());
- }
- socketIsLock.put(socketEntity.getName(), "0");
- }
- }
-
- executorService.execute(new CheckThread());
- logger.warn("初始化Socket连接结束!");
- }
- }
启动的线程是用于检查连接的
- package com.socket;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
-
-
-
-
-
- public class CheckThread implements Runnable {
- private static Log logger = LogFactory.getLog(CheckThread.class);
- public void run() {
- while(true){
- SocketCui socket = null;
- for(SocketEntity socketEntity : SocketKeep.socketEntityList){
- if(null != socketEntity && socketEntity.isKeepConn()){
- String isLock = SocketKeep.socketIsLock.get(socketEntity.getName());
-
- if(!"1".equals(isLock)){
-
- SocketKeep.socketIsLock.put(socketEntity.getName(), "1");
- socket = SocketKeep.socketMap.get(socketEntity.getName());
- try {
-
- socket.sendUrgentData(0xFF);
-
- SocketKeep.socketIsLock.put(socketEntity.getName(), "0");
- } catch (Exception e) {
- logger.error("检查连接时异常!启动重连!资源名称:" + socketEntity.getName(), e);
-
- InitSocket initS = new InitSocket(socketEntity.getName());
- new Thread(initS).start();
- }
- }
- }
- }
-
- try {
- logger.error("本次检测结束!");
- Thread.sleep(SocketKeep.commonCheckTime * 1000);
- } catch (Exception e) {
- }
- }
- }
- }
当检查线程发现无效的连接时会启动新的线程初始化该连接
- package com.socket;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
-
-
-
-
-
- public class InitSocket implements Runnable{
- private static Log logger = LogFactory.getLog(InitSocket.class);
-
-
-
- private static boolean isHave = false;
- private SocketEntity socketEntity = null;
- private String name;
- public InitSocket(String name){
- this.name = name;
-
- for(SocketEntity socketEntity : SocketKeep.socketEntityList){
- if(null != socketEntity && socketEntity.isKeepConn()){
- if(socketEntity.getName().equals(name)){
- this.setSocketEntity(socketEntity);
- isHave = true;
- }
- }
- }
- }
- public void run() {
- boolean isError = true;
- SocketCui socket = null;
- if(isHave){
- while(isError){
- try {
- socket = new SocketCui(this.getSocketEntity().getIp(),this.getSocketEntity().getPort());
- socket.setSoTimeout(0);
- socket.setKeepAlive(true);
- socket.setName(this.name);
-
- socket.sendUrgentData(0xFF);
- } catch (Exception e) {
- logger.error("建立资源连接时错误!资源:" + this.name, e);
- socket = null;
- }
- if(null != socket){
- SocketKeep.socketMap.put(this.getSocketEntity().getName(), socket);
-
- SocketKeep.socketIsLock.put(this.getSocketEntity().getName(), "0");
- logger.warn("建立资源连接成功!资源名称:" + this.name);
- isError = false;
- }
- try {
- Thread.sleep(2 * 1000);
- } catch (Exception e) {
- }
-
- }
- }else{
- logger.error("没有发现指定资源的配置信息!资源名称:" + this.name);
- }
- logger.warn("初始化资源执行结束!资源名称:" + this.name);
- }
- public SocketEntity getSocketEntity() {
- return socketEntity;
- }
- public void setSocketEntity(SocketEntity socketEntity) {
- this.socketEntity = socketEntity;
- }
- }
同时注意,用户在使用连接后会调用关闭方法。我们是不能让连接关闭的,要保持常连接。所以如果用户指定的是保持这个连接,那么返回的连接对象就不是原来的Socket对象了,我们要重写这个对象
- package com.socket;
- import java.io.IOException;
- import java.net.Socket;
- import java.net.UnknownHostException;
-
-
-
-
-
-
- public class SocketCui extends Socket{
-
-
-
- private String name;
- public SocketCui() {
- }
- public SocketCui(String ip,int port) throws UnknownHostException, IOException{
- super(ip, port);
- }
-
-
-
- @Override
- public synchronized void close() throws IOException {
- SocketKeep.socketIsLock.put(this.name, "0");
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
这样,关闭时只会解除其占用,而不会真正关闭该连接。
我们来写一个服务端,这个服务端一直接受连接,并检查连接的有效性,当失效时不处理
同时打印接收到的连接信息
- package com.test;
- import java.net.*;
- import java.text.SimpleDateFormat;
- import java.util.Date;
-
-
-
-
-
-
- public class ServiceTest {
- public static void main(String[] args) {
- try {
- ServerSocket ss1 = new ServerSocket(8001);
- Runnable accumelatora1 = new Accumulatort(ss1);
- Thread threada = new Thread(accumelatora1, "ThreadA");
- threada.start();
- System.out.println("服务启动完毕!");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- class Accumulatort implements Runnable {
- ServerSocket ss = null;
- public Accumulatort(ServerSocket s) {
- this.ss = s;
- }
- @SuppressWarnings("unchecked")
- public void run() {
- try {
- SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- while (true) {
- Socket s = ss.accept();
- System.out.println(format.format(new Date()) + " " + "---------收到请求!");
- new Thread(new ServiceImpl(s)).start();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
服务端处理连接的实现类
- package com.test;
- import java.net.Socket;
-
-
-
-
-
-
- public class ServiceImpl implements Runnable {
- Socket socket = null;
- public ServiceImpl(Socket s) {
- this.socket = s;
- }
- public void run() {
- boolean isKeep = true;
- try {
- while (isKeep) {
- socket.sendUrgentData(0xFF);
- Thread.sleep(1 * 1000);
- }
- } catch (Exception e) {
- isKeep = false;
- }
- }
- }
先启动服务端,然后我们再写一个测试端,这个测试类会不断去管理器中获取相应的连接,同时打印连接信息
通过打印的连接信息,我们可以知道获取的是不是同一个对象
同时如果你一直获取单例的对象,那么可能出现该连接被检查线程占用的情况
- package com.test;
- import java.net.Socket;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import com.socket.CommonSocket;
- import com.socket.SocketKeep;
-
-
-
-
-
-
- public class GetSocketTest {
- public static void main(String[] args) {
- SocketKeep.initSocketKeep();
- while(true){
- SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- try {
- Socket socket1 = CommonSocket.getSocketByName("socket1");
- if(null != socket1){
- System.out.println(format.format(new Date()) + " " + socket1.toString());
- socket1.close();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- try {
- Socket socket2 = CommonSocket.getSocketByName("socket2");
- if(null != socket2){
- System.out.println(format.format(new Date()) + " " + socket2.toString());
- socket2.close();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
-
- }
- }
- }
- }
当然别忘了加日志的包!
我们来看一下服务端的打印信息
- 服务启动完毕!
- 2012-04-18 16:59:48 ---------收到请求!
- 2012-04-18 16:59:48 ---------收到请求!
- 2012-04-18 16:59:49 ---------收到请求!
- 2012-04-18 16:59:50 ---------收到请求!
- 2012-04-18 16:59:51 ---------收到请求!
- 2012-04-18 16:59:52 ---------收到请求!
- 2012-04-18 16:59:53 ---------收到请求!
- 2012-04-18 16:59:54 ---------收到请求!
- 2012-04-18 16:59:55 ---------收到请求!
- 2012-04-18 16:59:56 ---------收到请求!
- 2012-04-18 16:59:57 ---------收到请求!
- 2012-04-18 16:59:58 ---------收到请求!
- 2012-04-18 16:59:59 ---------收到请求!
我们可以看到,除刚开始外,会一直收到连接,因为我们一直在获取一个非单例的连接对象
我们再看一下客户端打印信息
- 2012-04-18 16:59:48 Socket[addr=/127.0.0.1,port=8001,localport=4389]
- 2012-04-18 16:59:48 Socket[addr=/127.0.0.1,port=8001,localport=4390]
- 2012-04-18 16:59:49 Socket[addr=/127.0.0.1,port=8001,localport=4389]
- 2012-04-18 16:59:49 Socket[addr=/127.0.0.1,port=8001,localport=4391]
- 2012-04-18 16:59:50 Socket[addr=/127.0.0.1,port=8001,localport=4389]
- 2012-04-18 16:59:50 Socket[addr=/127.0.0.1,port=8001,localport=4392]
- 2012-04-18 16:59:51 Socket[addr=/127.0.0.1,port=8001,localport=4389]
- 2012-04-18 16:59:51 Socket[addr=/127.0.0.1,port=8001,localport=4393]
- 2012-04-18 16:59:52 Socket[addr=/127.0.0.1,port=8001,localport=4389]
- 2012-04-18 16:59:52 Socket[addr=/127.0.0.1,port=8001,localport=4394]
- 2012-04-18 16:59:53 Socket[addr=/127.0.0.1,port=8001,localport=4389]
- 2012-04-18 16:59:53 Socket[addr=/127.0.0.1,port=8001,localport=4395]
- 2012-04-18 16:59:54 Socket[addr=/127.0.0.1,port=8001,localport=4389]
- 2012-04-18 16:59:54 Socket[addr=/127.0.0.1,port=8001,localport=4396]
- 2012-04-18 16:59:55 Socket[addr=/127.0.0.1,port=8001,localport=4389]
- 2012-04-18 16:59:55 Socket[addr=/127.0.0.1,port=8001,localport=4397]
- 2012-04-18 16:59:56 Socket[addr=/127.0.0.1,port=8001,localport=4389]
- 2012-04-18 16:59:56 Socket[addr=/127.0.0.1,port=8001,localport=4398]
- 2012-04-18 16:59:57 Socket[addr=/127.0.0.1,port=8001,localport=4389]
- 2012-04-18 16:59:57 Socket[addr=/127.0.0.1,port=8001,localport=4399]
- 2012-04-18 16:59:58 Socket[addr=/127.0.0.1,port=8001,localport=4400]
- 2012-04-18 16:59:59 Socket[addr=/127.0.0.1,port=8001,localport=4389]
- 2012-04-18 16:59:59 Socket[addr=/127.0.0.1,port=8001,localport=4401]
可以看到,socket1是一直被保持的,而socket2每次创建一个
再看一下日志
- 2012-04-18 16:59:48 [com.socket.SocketKeep]-[WARN] 加载socket.properties文件成功!
- 2012-04-18 16:59:48 [com.socket.SocketKeep]-[WARN] 加载Socket连接配置信息结束!
- 2012-04-18 16:59:48 [com.socket.SocketKeep]-[WARN] 开始初始化Socket连接!
- 2012-04-18 16:59:48 [com.socket.SocketKeep]-[WARN] 初始化Socket连接结束!
- 2012-04-18 16:59:48 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
- 2012-04-18 16:59:48 [com.socket.CheckThread]-[ERROR] 本次检测结束!
- 2012-04-18 16:59:49 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
- 2012-04-18 16:59:50 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
- 2012-04-18 16:59:51 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
- 2012-04-18 16:59:52 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
- 2012-04-18 16:59:53 [com.socket.CheckThread]-[ERROR] 本次检测结束!
- 2012-04-18 16:59:53 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
- 2012-04-18 16:59:54 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
- 2012-04-18 16:59:55 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
- 2012-04-18 16:59:56 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
- 2012-04-18 16:59:57 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
- 2012-04-18 16:59:58 [com.socket.CommonSocket]-[WARN] 当前连接正被占用,请稍候尝试!资源名称:socket1
- 2012-04-18 16:59:58 [com.socket.CheckThread]-[ERROR] 本次检测结束!
- 2012-04-18 16:59:58 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
- 2012-04-18 16:59:59 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
可以看到,socket2会一直被重新创建,而且socket1会发现被占用的情况。
你也可以中断服务端然后再重启服务端试试,看看效果。
如果你喜欢该博客,欢迎您访问我的博客:http://cuisuqiang.iteye.com/ !
源码下面可以下载,仅供参考!转载说明出处,谢谢合作!