最近做的一个项目需要在服务端对连接端进行管理,故将方案记录于此。

方案实现的结果与背景

   因为服务端与客户端实现的是长连接,所以需要对客户端的连接情况进行监控,防止无效连接占用资源。

   完成类似于心跳的接收以及处理

    即:

      当连接过长事件(keep-alive Time)没有发送新的消息时,则在服务端切断其客户端的连接。

具体细节

    在处理连接(Accpet事件)时:

      将SocketChannel存入HashSet;

         以SocketChannel的HashCode作为Key来存储连接时间(以服务器时间为准)

      (开辟一个HashMap或者利用Redis进行缓存)

   在处理读取(Readable)事件时:

       以SocketChannel的HashCode作为Key来存储读取事件发生的时间(以服务器时间为准);

       处理读取事件


    开启一个定时反复运行的管理线程,每次运行对HashSet中的SocketChannel进行轮询,并以SocketChannel的HashCode去取对应的时间(LastSendTime)

    获取当前时间(CurrentTime),进行计算,如果大于Keep-Alive Time,则删除HashMap(/Redis)中的键值对,以及HashSet中的SocketChannel对象,并关闭SocketChannel。

     连接端

       ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress("127.0.0.2",1234));
serverChannel.configureBlocking(false);
AnalyisUtil util=new AnalyisUtil();
RedisConnectionPool connectionPool=new RedisConnectionPool();
Selector selector = Selector.open();
SelectionKey key = serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
int select = selector.select();
if (select > 0) {
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
// 接收连接请求
if (selectionKey.isAcceptable()) {
ServerSocketChannel channel = (ServerSocketChannel) selectionKey
.channel();
SocketChannel socketChannel = channel.accept();
logger.info("接收到一个新的连接请求"+ socketChannel.getRemoteAddress().toString());
socketChannel.configureBlocking(false);
//每接收请求,注册到同一个selector中处理
socketChannel.register(selector, SelectionKey.OP_READ);
                 //在Redis中存储连接的时间,以SocketChannel的HashCode作为Key
connectionPool.getJedis().set("LST_"+socketChannel.hashCode(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                 //将SocketChannel放入HashSet中管理
connectedSokectList.add(socketChannel);
} else if (selectionKey.isReadable()) {
//执行读事件,在读事件的处理函数中,重新以SocketChannel的HashCode再次存储事件,以刷新时间
util.handleReadEvent(selectionKey,messageQueue,logger);
}
}
}
}

    连接处理线程

    

package ConnectionSystem;

import Util.RedisConnectionPool;
import org.apache.log4j.Logger;
import redis.clients.jedis.Jedis;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.SocketChannel;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator; public class ConnectionManagerTask implements Runnable {
private HashSet<SocketChannel> connectedSokectList;
private long keepalive_Time=5000;
private Logger logger=Logger.getLogger(ConnectionManagerTask.class); ConnectionManagerTask(HashSet<SocketChannel> list){
logger.info("TCP监听已经启动... ...");
this.connectedSokectList=list;
} private long cucalateIsAlive(Date lastSendTime) throws ParseException {
Date currentTime=new Date();
return currentTime.getTime()-lastSendTime.getTime();
} private boolean handleSocket(SocketChannel channel){
int channel_code= channel.hashCode();
RedisConnectionPool connectionPool=new RedisConnectionPool();
Jedis jedisCilent;
SocketAddress ipLocation;
try{
ipLocation=channel.getRemoteAddress();
jedisCilent=connectionPool.getJedis();
String SendTime=jedisCilent.get("LST_"+channel_code);
if(SendTime!=null) {
SimpleDateFormat dfs = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date lastSendTime = dfs.parse(SendTime);
if (cucalateIsAlive(lastSendTime) > keepalive_Time) {
//超过时间
try {
if(channel.isConnected()){
channel.close();
jedisCilent.del("LST_"+channel_code);
logger.debug("连接被TCP管理线程关闭,ip:" + ipLocation + ",上次回应时间:" + lastSendTime);
}else {
logger.debug("当前通道,ip:" + ipLocation + "已经关闭... ..."+ ",上次回应时间:" + lastSendTime);
}
return true;
} catch (IOException e) {
logger.error("通道,ip:" + ipLocation + "关闭时发生了异常",e);
}
}else {
return false;
}
}
if(channel.isConnected()){
channel.close();
logger.debug("连接被TCP管理线程关闭,ip:" + ipLocation + ":未检测到登陆时间... ...");
}else {
logger.debug("当前通道,ip:" + ipLocation + "已经关闭... ...");
} }catch (Exception e){
logger.error("通道关闭时发生了异常",e);
}
return true;
} @Override
public void run() {
logger.info("当前连接数"+connectedSokectList.size());
if(connectedSokectList.isEmpty()){
return;
}
Iterator<SocketChannel> iterator = connectedSokectList.iterator();
while (iterator.hasNext()){
SocketChannel socketChannel=iterator.next();
Boolean removeFlag=handleSocket(socketChannel);
if(removeFlag){
iterator.remove();
}
}
}
}

   

NIO 服务端TCP连接管理的方案的更多相关文章

  1. 经典!服务端 TCP 连接的 TIME_WAIT 过多问题的分析与解决

    开源Linux 专注分享开源技术知识 本文给出一个 TIME_WAIT 状态的 TCP 连接过多的问题的解决思路,非常典型,大家可以好好看看,以后遇到这个问题就不会束手无策了. 问题描述 模拟高并发的 ...

  2. 服务端 TCP 连接的 TIME_WAIT 过多问题的分析与解决

    https://mp.weixin.qq.com/s/VRQ_12tzy3gRYD091cI7Ew

  3. Kafka技术内幕 读书笔记之(二) 生产者——服务端网络连接

    KafkaServer是Kafka服务端的主类, KafkaServer中和网络层有关的服务组件包括 SocketServer.KafkaApis 和 KafkaRequestHandlerPool后 ...

  4. NIO服务端主要创建过程

    NIO服务端主要创建过程:   步骤一:打开ServerSocketChannel,用于监听客户端的连接,它是所有客户端连接的副管道,示例代码如下:      ServerSocketChannel ...

  5. TCP系列07—连接管理—6、TCP连接管理的状态机

            经过前面对TCP连接管理的介绍,我们本小节通过TCP连接管理的状态机来总结一下看看TCP连接的状态变化 一.TCP状态机整体状态转换图(截取自第二版TCPIP详解) 二.TCP连接建立 ...

  6. 6-1 建立客户端与zk服务端的连接

    6-1 建立客户端与zk服务端的连接 zookeeper原生java api使用 会话连接与恢复; 节点的增删改查; watch与acl的相关操作; 导入jar包;

  7. TCP连接管理(TCP Connection Management)

    在最近的求职面试过程中,关于"建立TCP连接的三次握手"不止一次被问到了,虽然我以前用同样的问题面试过别人,但感觉还是不能给面试官一个很清晰的回答.本文算是对整个TCP连接管理做一 ...

  8. 客户端 new socket时候 就像服务端发起连接了

    客户端 new socket时候  就像服务端发起连接了

  9. 客户端(springmvc)调用netty构建的nio服务端,获得响应后返回页面(同步响应)

    后面考虑通过netty做一个真正意义的简约版RPC框架,今天先尝试通过正常调用逻辑调用netty构建的nio服务端并同步获得返回信息.为后面做铺垫 服务端实现 我们先完成服务端的逻辑,逻辑很简单,把客 ...

随机推荐

  1. Java 8 – Map排序

    前提 Map是Java中最常用的集合类之一,这里整理了关于HashMap的排序 (关于List的排序,请查看Collections.sort()的doc或源码). 将无序的HashMap借助Strea ...

  2. 第6章 HDFS HA配置

    目录 6.1 hdfs-site.xml文件配置 6.2 core-site.xml文件配置 6.3 启动与测试 6.4 结合ZooKeeper进行自动故障转移 在Hadoop 2.0.0之前,一个H ...

  3. Linux3.5—IIC学习分析

    I2C控制器的设备对象内核已经实现并关联到platform总线. I2C控制器的驱动对象内核已经实现. 看mach-tiny4412.h /plat-samsung/目录下 /drivers/i2c/ ...

  4. java 递归打印20个斐波那契数

    class Test { public static void main(String[] args) { // feibo j=new feibo(); for (int n = 1; n < ...

  5. Fedora 下面安装FTP服务

    1. yum install vsftpd 2. systemctl disable vsftpd.service 3. systemctl stop vsftpd.service 4. system ...

  6. MAC下SecureCRT 8.0.4破解版远程连接工具的安装与配置

    一 SCRT的软件的安装:首先访问 http://xclient.info/s/securecrt.html?_=4365bb5cf097dc2c135a57c2ce30b264 选择8.0.4的版本 ...

  7. ov5640介绍

    1 摄像头 在各类信息中,图像含有最丰富的信息,作为机器视觉领域的核心部件,摄像头被广泛地应用在安防.探险以及车牌检测等场合.摄像头按输出信号的类型来看可以分为数字摄像头和模拟摄像头,按照摄像头图像传 ...

  8. Java设计模式(23)——行为模式之访问者模式(Visitor)

    一.概述 概念 作用于某个对象群中各个对象的操作.它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作. 引入 试想这样一个场景,在一个Collection中放入了一大堆的各种对象的引用 ...

  9. 抓猫_KEY

    抓猫 题面如下: [ 题目描述] 流浪猫布满城市的每一个角落, 非常影响市容市貌, 作为城市聘请的抓猫者, 你有一 种捕捉器, 一定可以捕捉到所有走到里面的猫, 更加幸运的是你有一个非常厉害的动物心理 ...

  10. 【BZOJ5299】【CQOI2018】解锁屏幕(动态规划,状态压缩)

    [BZOJ5299][CQOI2018]解锁屏幕(动态规划,状态压缩) 题面 BZOJ 洛谷 Description 使用过Android手机的同学一定对手势解锁屏幕不陌生.Android的解锁屏幕由 ...