判断 Socket 的实时连接状态(心跳包)

一、问题引入

socket.isConnected()或者socket.isClosed()等方法都是访问socket在内存驻留的状态,当socket 和服务器端建立链接后,即使 socket 链接断掉了,调用上面的方法返回的仍然是链接时的状态,而不是socket的实时链接状态
例如如下实例:
客户端:

public class Client{
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1", 66666);
            socket.setKeepAlive(true);
            //设置最大连接超时时间,超过此时间,无响应则断开连接
            socket.setSoTimeout(10);
            while (true) {
                System.out.println(socket.isBound());//判断当前是否绑定本地的地址和端口
                System.out.println(socket.isClosed());
                System.out.println(socket.isConnected());
                System.out.println(socket.isInputShutdown());
                System.out.println(socket.isOutputShutdown());
                System.out.println("------------ ***** ------------");
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

服务端:

class DstService implements Runnable {
    Socket socket = null;
    public DstService(Socket s) {
        this.socket = s;
    }
    public void run() {
        try {
            int index = 1;
            while (true) {
                if (index > 2) {
                    socket.close();
                    System.out.println("服务端已经关闭链接!");
                    break;
                }
                index++;
                Thread.sleep(1000);//程序睡眠1秒钟
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class Server{
    public static void main(String[] args) {
        try {
            // 启动监听端口 66666
            ServerSocket server = new ServerSocket(66666);
            // 没有连接这个方法就一直堵塞
            Socket socket = server.accept();
            // 将请求指定一个线程去执行
            new Thread(new DstService(socket)).start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

客户端运行结果:
在这里插入图片描述
结果可以看出,尽管服务端建立连接 3 秒后就断开了,但是客户端并没有接收到服务端断开的信息,显示的依然是 Socket 驻留内存的状态

二、心跳包

心跳包就是在客户端和服务器间定时通知对方自己状态的一个自己定义的命令字,按照一定的时间间隔发送,类似于心跳,所以叫做心跳包

(1)用来判断对方(设备,进程或其它网元)是否正常运行,采用定时发送简单的通讯包,如果在指定时间段内未收到对方响应,则判断对方已经离线。

(2)用于检测TCP的异常断开。基本原因是服务器端不能有效的判断客户端是否在线,也就是说,服务器无法区分客户端是长时间在空闲,还是已经掉线的情况。

所谓的心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在而已,代码就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息如果服务端几分钟内没有收到客户端信息则视客户端断开
比如有些通信软件长时间不使用,要想知道它的状态是在线还是离线就需要心跳包,定时发包收包。

发包方:可以是客户也可以是服务端,看哪边实现方便合理,一般是客户端。服务器也可以定时发心跳下去。一般来说,出于效率的考虑,是由客户端主动向服务器端发包,而不是服务器向客户端发。
客户端每隔一段时间发一个包,使用TCP的,用send发,使用UDP的,用sendto发,服务器收到后,就知道当前客户端还处于“活着”的状态,否则,如果隔一定时间未收到这样的包,则服务器认为客户端已经断开,进行相应的客户端断开逻辑处理。

使用心跳包改进客户端:

import java.net.Socket;

public class Test{
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1", 6666);
            socket.setKeepAlive(true);
            //设置最大连接超时时间,超过此时间,无响应则断开连接
            socket.setSoTimeout(10);
            while (true) {
                socket.sendUrgentData(0xFF);//心跳包的值可以任意指定
                System.out.println(socket.isBound());//判断当前是否绑定本地的地址和端口
                System.out.println(socket.isClosed());
                System.out.println(socket.isConnected());
                System.out.println("------------ ***** ------------");
                Thread.sleep( 1000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
这里心跳包只是用来检测 Socket 的链接状态,并不会作为 Socket 链接的通信内容

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章