判断 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 链接的通信内容

  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
下面是一个基于 Java Socket 的长连接实现心跳检测的代码示例: 客户端代码: ```java public class Client { public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1", 8888); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); // 发送心跳包 ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); executorService.scheduleAtFixedRate(() -> { out.println("ping"); }, 0, 3, TimeUnit.SECONDS); // 接收消息 String message; while ((message = in.readLine()) != null) { System.out.println(message); } } } ``` 服务端代码: ```java public class Server { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8888); while (true) { Socket socket = serverSocket.accept(); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); // 接收心跳包 ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); executorService.scheduleAtFixedRate(() -> { try { String message = in.readLine(); if ("ping".equals(message)) { out.println("pong"); } } catch (IOException e) { e.printStackTrace(); } }, 0, 3, TimeUnit.SECONDS); } } } ``` 在客户端和服务端分别启动后,客户端会每隔 3 秒发送一个 "ping" 消息,服务端会每隔 3 秒接收客户端的消息并判断是否是心跳包,如果是则回复一个 "pong" 消息。这样就可以通过心跳检测来保持长连接的存活状态
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南淮北安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值