在做IM通信时,都会遇到这样一个概念:心跳包。又是一个比较抽象的概念,那么心跳包到底是什么呢?
之前做的机房预定系统里,使用的Socket和服务器通信。
测试服务器:
Socket基本上都这么一个流程。
public class Test {
private static final int PORT = 1234;
private static final int BUFFER_SIZE = 1024;
public static void main(String[] args) {
ServerSocket ss;
try {
ss = new ServerSocket(PORT);
Socket s = ss.accept();
byte[] recData = null;
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
while(true) {
recData = new byte[BUFFER_SIZE];
int r = in.read(recData);
//int r = in.read(recData);
if(r>-1) {
String data = new String(recData);
if(data.trim().equals("over")) {
s.close();
}
System.out.println("读取到客户端发送的来数据:"+data);
out.write("这是服务端发给客户端的数据:".getBytes());
out.write(recData);
}else {
System.out.println("数据读取完毕!");
s.close();
System.exit(0);
//ss.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
这个就是读客户端传过来的数据。
客户端我写在了android上,一个click事件:
public void test(View v) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Socket s = new Socket(serv,port);
PrintWriter out = new PrintWriter(s.getOutputStream());
while (true) {
Thread.sleep(5000);
String msg = "me";
out.println(msg);
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
启动Server后,效果如下,每5秒钟会收到客户端发的消息:
这个就是长连接,如果不关掉服务器,会一直接收某个客户端的消息。
服务器代码:
public class Test {
private static final int PORT = 1234;
private static final int BUFFER_SIZE = 1024;
public static void main(String[] args) {
ServerSocket ss;
try {
ss = new ServerSocket(PORT);
Socket s = ss.accept();
//主要是这一句
s.setSoTimeout(2000);
byte[] recData = null;
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
while(true) {
recData = new byte[BUFFER_SIZE];
int r = in.read(recData);
//int r = in.read(recData);
if(r>-1) {
String data = new String(recData);
if(data.trim().equals("over")) {
s.close();
}
System.out.println("读取到客户端发送的来数据:"+data);
out.write("这是服务端发给客户端的数据:".getBytes());
out.write(recData);
}else {
System.out.println("数据读取完毕!");
s.close();
System.exit(0);
//ss.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端代码:
public void test(View v) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Socket s = new Socket(serv,port);
PrintWriter out = new PrintWriter(s.getOutputStream());
while(true){
String msg = "me";
out.println(msg);
out.flush();
Thread.sleep(3000);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
可以看到,客户端是3秒发一次消息,而服务器设置的是2秒超时。所以只能接收到一次消息,然后就关闭了socket。
如果不想报错,就把服务器的while(true)循环放到Socket s = ss.accept();外面。意思是,为每个客户端分配的连接时长是2秒,超过以后就中断这个socket,除非重新建立连接。
长连接占用系统资源,但是短连接会造成连接中断。所以综合这两种情况,提出一个心跳包的概念。就是给服务器设置超时,比如上面的2秒,然后客户端发空的消息,或约定好的字符串格式,当然间隔时长需要在超时以内(比如这里就需要在2秒以内)来给服务器保证这个连接(即这个socket)不被销毁。
服务器代码:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Test {
private static final int PORT = 1234;
private static final int BUFFER_SIZE = 1024;
public static void main(String[] args) {
ServerSocket ss = null;
try {
ss = new ServerSocket(PORT);
Socket s = ss.accept();
s.setSoTimeout(2000);
byte[] recData = null;
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
while(true) {
recData = new byte[BUFFER_SIZE];
int r = in.read(recData);
//int r = in.read(recData);
if(r>-1) {
String data = new String(recData);
if(data.trim().equals("over")) {
s.close();
}
System.out.println("读取到客户端发送的来数据:"+data);
out.write("这是服务端发给客户端的数据:".getBytes());
out.write(recData);
}else {
System.out.println("数据读取完毕!");
s.close();
System.exit(0);
//ss.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端代码:
public void test(View v) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Socket s = new Socket(serv,port);
PrintWriter out = new PrintWriter(s.getOutputStream());
while(true){
String msg = "me";
out.println(msg);
out.flush();
//这里改成1秒了
Thread.sleep(1000);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
这样就保持了一个长连接。这个就可以看做心跳包。
实际使用时比这个要复杂,但大概就是这个原理。