一般是用来判断对方(设备,进程或其它网元)是否正常动行,一般采用定时发送简单的通讯包,如果在指定时间段内未收到对方响应,则判断对方已经当掉。用于检测TCP的异常断开。一般是用来判断对方(设备,进程或其它网元)是否正常动行,一般采用定时发送简单的通讯包,如果在指定时间段内未收到对方响应,则判断对方已经当掉。用于检测TCP的异常断开。基本原因是服务器端不能有效的判断客户端是否在线也就是说,服务器无法区分客户端是长时间在空闲,还是已经掉线的情况.所谓的心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在而已。
...
客户端每隔一段时间发一个包,使用TCP的,用send发,使用UDP的,用sendto发,服务器收到后,就知道当前客户端还处于“活着”的状态,否则,如果隔一定时间未收到这样的包,则服务器认为客户端已经断开,进行相应的客户端断开逻辑处理。
1.心跳包实际就是一个自定义的协议包,主要用于判断对方机器是否还在连接。你可以自己定义协议的格式。
2.既然是协议,当然需要c/s双方都支持
3.如果你仅仅是为了判断服务器是否在线,用tcp协议自带的心跳包就可以了,用setsockopt设置socket的SO_KEEPALIVE属性,可以设置发包时间间隔
合理的解决方案就是做心跳处理,只是在客户端新开一个线程,然后while,里面sleep(5000)+向网络发送一个数据,也就是每五秒发送一个数据,这个数据最好跟正常的数据分开,简单些,然后在客户端设置socket.setSoTimeout(10000);,也就是说如果10秒钟没接收到数据的话就抛异常,这样就可以了。
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.net.InetSocketAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Map;
- import org.dom4j.Document;
- import org.dom4j.DocumentException;
- import org.dom4j.DocumentHelper;
- import cn.edu.zju.cst.mina.im.server.entity.User;
- import cn.edu.zju.cst.mina.im.server.handler.ServerControler;
- public class UserStateManage extends Thread {
- //在线用户状态列表
- static HashMap<Integer, UserState> userStateList = new HashMap<Integer, UserState>();
- Object hashLock = new Object();
- //当前的连接数和工作线程数
- static int workThreadNum = 0;
- static int socketConnect = 0;
- private ServerSocket serverSocket;
- //服务器IP
- private String host = "10.82.81.79";
- //服务器端口
- private int stateReportPort = 60001;
- //设置心跳包的结束标记
- String endFlag = "</protocol>";
- CharSequence csEndFlag = endFlag.subSequence(0, 10);
- //扫描间隔
- private int scanTime = 1800;
- @Override
- public void run() {
- //绑定端口,并开始侦听用户的心跳包
- serverSocket = startListenUserReport(stateReportPort);
- if(serverSocket == null){
- System.out.println("【创建ServerSocket失败!】");
- return;
- }
- //启动扫描线程
- Thread scanThread = new Thread(new scan());
- scanThread.start();
- //等待用户心跳包请求
- while(true){
- Socket socket = null;
- try {
- socketConnect = socketConnect + 1;
- //接收客户端的连接
- socket = serverSocket.accept();
- //为该连接创建一个工作线程
- Thread workThread = new Thread(new Handler(socket));
- //启动工作线程
- workThread.start();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- /**
- * 创建一个ServerSocket来侦听用户心跳包请求
- * @param port 指定的服务器端的端口
- * @return 返回ServerSocket
- * @author dream
- */
- public ServerSocket startListenUserReport(int port){
- try {
- ServerSocket serverSocket = new ServerSocket();
- if(!serverSocket.getReuseAddress()){
- serverSocket.setReuseAddress(true);
- }
- serverSocket.bind(new InetSocketAddress(host,port));
- System.out.println("【开始在"+serverSocket.getLocalSocketAddress()+"上侦听用户的心跳包请求!】");
- return serverSocket;
- } catch (IOException e) {
- System.out.println("【端口"+port+"已经被占用!】");
- if (serverSocket != null) {
- if (!serverSocket.isClosed()) {
- try {
- serverSocket.close();
- } catch (IOException e1) {
- e1.printStackTrace();
- }
- }
- }
- }
- return serverSocket;
- }
- //工作线程类
- class Handler implements Runnable{
- private Socket socket;
- UserState us = null;
- User newUser = null;
- private int userId;
- private int userState;
- /**
- * 构造函数,从调用者那里取得socket
- * @param socket 指定的socket
- * @author dream
- */
- public Handler(Socket socket){
- this.socket = socket;
- }
- /**
- * 从指定的socket中得到输入流
- * @param socket 指定的socket
- * @return 返回BufferedReader
- * @author dream
- */
- private BufferedReader getReader(Socket socket){
- InputStream is = null;
- BufferedReader br = null;
- try {
- is = socket.getInputStream();
- br = new BufferedReader(new InputStreamReader(is));
- } catch (IOException e) {
- e.printStackTrace();
- }
- return br;
- }
- public void run() {
- try{
- workThreadNum = workThreadNum +1;
- System.out.println("【第"+workThreadNum+"个的连接:"+socket.getInetAddress()+":"+socket.getPort()+"】");
- BufferedReader br = getReader(socket);
- String meg = null;
- StringBuffer report = new StringBuffer();
- while ((meg = br.readLine()) != null) {
- report.append(meg);
- if (meg.contains(csEndFlag)) {
- us = getReporterUserState(meg, socket);
- synchronized (hashLock) {
- userStateList.put(userId, us);
- }
- }
- }
- }catch(IOException e){
- System.out.println("【客户:"+newUser.getUser_id()+"已经断开连接!】");
- userStateList.remove( userId );
- announceStateChange( userId , -1);
- }finally{
- if(socket != null){
- try {
- //断开连接
- socket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- private UserState getReporterUserState(String meg , Socket socket){
- UserState us = new UserState();
- try {
- Document requestDoc = DocumentHelper.parseText(meg);
- newUser = ServerControler.parseXmlToUserState(requestDoc,socket);
- userId = newUser.getUser_id();
- userState = newUser.getUser_state();
- us.setFlag(2);
- us.setUser_state( userState );
- us.setUser_id( userId );
- us.setUser_ip(newUser.getUser_ip());
- us.setUser_port(newUser.getUser_port());
- } catch (DocumentException e) {
- System.out.println("【来自客户端的信息不是一个合法的心跳包协议】");
- }
- return us;
- }
- }
- //扫描线程
- class scan implements Runnable{
- public void run() {
- while (true) {
- System.out.println("*******"+new Date()+":扫描线程开始扫描"+"*******");
- synchronized (hashLock) {
- if(!userStateList.isEmpty()){
- //遍历在线用户列表
- for (Map.Entry<Integer, UserState> entry : userStateList.entrySet()) {
- int flag = entry.getValue().getFlag();
- if ( (flag - 1) < 0) {
- //在这里通知该用户的好友其状态发生改变
- // announceStateChange(entry.getKey() , 0);
- }else{
- entry.getValue().setFlag(flag - 1);
- userStateList.put(entry.getKey(), entry.getValue());
- }
- System.out.println(entry.getKey() + "-->" + entry.getValue().toString());
- }
- }else{
- System.out.println("现在还没有在线用户!");
- }
- }
- //实现定时扫描
- try {
- sleep(scanTime);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- private void announceStateChange(int userId , int state){
- System.out.println("通知其好友!");
- }
- /**
- * 查询一个用户是否在线
- * @param userId 指定要查询状态的用户的ID
- * @return true 在线; false 不在线;
- * @author dream
- */
- public boolean isAlive(int userId){
- synchronized (hashLock) {
- return userStateList.containsKey(userId);
- }
- }
- /**
- * 返回指定用户ID的状态
- * @param userId 指定要查询状态的用户的ID
- * @return >0 该用户在线; -1 该用户离线
- * @author dream
- */
- public int getUserState(int userId){
- synchronized (hashLock) {
- if(userStateList.containsKey(userId)){
- return userStateList.get(userId).getUser_state();
- }else{
- return -1;
- }
- }
- }
- public Object getHashLock() {
- return hashLock;
- }
- public void setHashLock(Object hashLock) {
- this.hashLock = hashLock;
- }
- public String getHost() {
- return host;
- }
- public void setHost(String host) {
- this.host = host;
- }
- public int getStateReportPort() {
- return stateReportPort;
- }
- public void setStateReportPort(int stateReportPort) {
- this.stateReportPort = stateReportPort;
- }
- public String getEndFlag() {
- return endFlag;
- }
- public void setEndFlag(String endFlag) {
- this.endFlag = endFlag;
- }
- public int getScanTime() {
- return scanTime;
- }
- public void setScanTime(int scanTime) {
- this.scanTime = scanTime;
- }
- public static HashMap<Integer, UserState> getUserStateList() {
- return userStateList;
- }
- public static int getWorkThreadNum() {
- return workThreadNum;
- }
- public static int getSocketConnect() {
- return socketConnect;
- }
- //测试本函数的main函数
- public static void main(String arg[]){
- UserStateManage usm = new UserStateManage();
- usm.start();
- }
- }