NIO server TCP connection management solution

A recent project needs to manage the connection end on the server side, so the plan is recorded here.

The results and background of the realization of the scheme

   Because the server and the client implement a long connection, it is necessary to monitor the connection status of the client to prevent invalid connections from occupying resources.

   Complete the reception and processing similar to the heartbeat

    which is:

      When the connection is too long event (keep-alive Time) and no new message is sent, the server will cut off the connection of its client.

details

    When handling the connection (Accpet event):

      Store SocketChannel in HashSet;

         Use the HashCode of the SocketChannel as the key to store the connection time (subject to the server time)

      (Open a HashMap or use Redis for caching)

   When handling the Readable event:

       Use the HashCode of the SocketChannel as the key to store the time when the read event occurs (subject to the server time);

       Handling read events


 

    Start a management thread that runs regularly and repeatedly, poll the SocketChannel in the HashSet each time it runs, and use the HashCode of the SocketChannel to get the corresponding time (LastSendTime)

    Get the current time (CurrentTime), do the calculation, if it is greater than the Keep-Alive Time, delete the key-value pair in HashMap (/Redis) and the SocketChannel object in HashSet, and close the SocketChannel.

 

     connection end

       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();
                        // Receive connection request 
                        if (selectionKey.isAcceptable()) {
                            ServerSocketChannel channel = (ServerSocketChannel) selectionKey
                                    .channel();
                            SocketChannel socketChannel = channel.accept();
                            logger.info( "Received a new connection request" + socketChannel.getRemoteAddress().toString());
                            socketChannel.configureBlocking( false );
                             // Every time a request is received, register in the same selector to process 
                            socketChannel.register(selector, SelectionKey.OP_READ); 
                 //Store the connection time in Redis, use the HashCode of SocketChannel as the Key connectionPool.getJedis ().set(
"LST_"+socketChannel.hashCode(), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format( new Date()));
                 //Put SocketChannel into HashSet for management connectedSokectList.add(socketChannel); }
else if (selectionKey.isReadable()) { //Execute the read event, in the read event handler, store the event again with the HashCode of the SocketChannel to refresh the time util.handleReadEvent(selectionKey,messageQueue,logger); } } } }

    connection processing thread

    

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 listening started... ..." );
         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( "Connection closed by TCP management thread, ip:" + ipLocation + ", last response time: " + lastSendTime);
                        }else {
                            logger.debug( "Current channel, ip:" + ipLocation + "Close..."+ ", last response time: " + lastSendTime);
                        }
                        return true;
                    } catch (IOException e) {
                        logger.error( "Channel, ip:" + ipLocation + "An exception occurred while closing" ,e);
                    }
                }else {
                    return false;
                }
            }
            if(channel.isConnected()){
                channel.close();
                logger.debug( "Connection closed by TCP management thread, ip:" + ipLocation + ": No login time detected..." );
            }else {
                logger.debug( "Current channel, ip:" + ipLocation + "closed..." );
            }

        }catch (Exception e){
            logger.error( "An exception occurred when the channel was closed" ,e);
        }
        return true;
    }

    @Override
    public void run() {
        logger.info( "Number of current connections"+ 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();
            }
        }
    }
}

 

 

 

    

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325003796&siteId=291194637