websocket连接类
package com.dnn.controller.inter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.apache.http.impl.entity.EntitySerializer;
import com.dnn.entity.WebSocketEntity;
import com.dnn.model.TbAdminMember;
import com.dnn.service.TbAdminMemberService;
import com.dnn.service.TbDataGrouprecordService;
import com.dnn.utils.jfinal.BaseController;
import com.jfinal.aop.Clear;
import net.sf.json.JSONObject;
@Clear
@ServerEndpoint("/webSocket/{userId}")
public class WebSocketController extends BaseController {
protected TbDataGrouprecordService tbDataGrouprecordService=new TbDataGrouprecordService();
protected TbAdminMemberService tbAdminMemberService=new TbAdminMemberService();
private static boolean isHeart=false;
private static final Set connections = new CopyOnWriteArraySet();
/**
*
* @Description: 连接方法
* @param @param userId
* @param @param session
* @return void
* @throws IOException
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
@OnOpen
public synchronized void onOpen(@PathParam("userId") String userId, Session session) throws IOException {
TbAdminMember member=tbAdminMemberService.findInfo(new TbAdminMember().setId(userId));
if(null==member){
logger.debug("发现未知生物");
return;
}
addUser(member, session);
if(connections.size()==1 && !isHeart){
isHeart=true;
startHeart();
}
}
/**
*
* @Description: 收到消息执行
* @param @param userId
* @param @param message
* @param @param session
* @param @throws IOException
* @return void
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
@OnMessage
public synchronized void onMessage(@PathParam("userId") String userId, String message, Session session) throws IOException {
logger.info(message);
JSONObject jsonObject = JSONObject.fromObject(message);
if(jsonObject.has("secret") && jsonObject.getString("secret").equals("ping")){//心跳
logger.info("收到"+userId+"的心跳"+message);
//如果收到了心跳 这里设置isHeart为true
WebSocketEntity entity=getUserEntity(userId);
if(null!=entity){
entity.setHeart(true);
}
}else{//普通对话
boolean res=tbDataGrouprecordService.addGroupRecord(jsonObject);
logger.warn("保存记录:"+res);
SimpleDateFormat sdfTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String datetime = sdfTime.format(date);//获取当前时间
jsonObject.put("intime", datetime);
message=tbAdminMemberService.addUserInfo(jsonObject);
sendMsg(message);
}
}
/**
*
* @Description: 链接错误执行
* @param @param userId
* @param @param session
* @param @param error
* @return void
* @throws IOException
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
@OnError
public synchronized void onError(@PathParam("userId") String userId, Session session, Throwable error) throws IOException {
logger.debug(userId+":发生了错误");
removeUser(userId,new CloseReason(CloseCodes.NO_EXTENSION, "客户端异常"));
error.printStackTrace();
}
@OnClose
public synchronized void onClose(@PathParam("userId") String userId,Session session,CloseReason reason){
logger.debug(userId+":退出了链接");
removeUser(userId,reason);
}
/**
*
* @Description: 获取在线人数
* @param @return
* @return int
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
private synchronized int getUserOnlineNum(){
return connections.size();
}
/**
*
* @Description: 获取在线人数列表
* @param @return
* @return Set
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
@SuppressWarnings("unused")
private synchronized Set getUserOnline(){
return connections;
}
/**
*
* @Description: 用户上线
* @param @param member
* @param @param session
* @return void
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
private synchronized void addUser(TbAdminMember member, Session session){
WebSocketEntity entity=getUserEntity(member.getId());
if(null==entity){
connections.add(new WebSocketEntity(member, session));
}else{
entity.setSession(session);
entity.setMemberHead(member.getHead());
entity.setMemberName(member.getName());
logger.debug("用户"+entity.getMemberName()+"上线了,当前人数为:"+getUserOnlineNum());
}
}
/**
*
* @Description: 根据userId获取实体类
* @param @param userId
* @param @return
* @return WebSocketEntity
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月22日
*/
private static WebSocketEntity getUserEntity(String userId){
WebSocketEntity entity=null;
if(connections.size()==0)
return entity;
for (WebSocketEntity webSocketEntity : connections) {
if(webSocketEntity.getUserId().contentEquals(userId)){
entity=webSocketEntity;
break;
}
}
return entity;
}
/**
*
* @Description: 用户下线
* @param @param userId
* @param @param reason
* @return void
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月23日
*/
private void removeUser(String userId, CloseReason reason) {
WebSocketEntity entity=getUserEntity(userId);
if(null!=entity){
try {
if(entity.getSession().isOpen()){
entity.getSession().close(reason);
}
connections.remove(entity);
} catch (IOException e) {
logger.info(e.toString());
e.printStackTrace();
}
}
logger.debug("当前人数:"+connections.size());
}
/**
*
* @param 发送心跳包
* @Description: 服务端群发消息
* @param @param message
* @param @throws IOException
* @return void
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
public synchronized void sendPing(String message) throws IOException{
if(connections.size()<=0)
return;
for (WebSocketEntity webSocketEntity : connections) {
synchronized (webSocketEntity) {
webSocketEntity.setTimeStr(getTimeInMillis());
webSocketEntity.setHeart(false);
((Session) webSocketEntity.getSession()).getBasicRemote().sendText(message);
}
}
}
/**
*
* @Description: 发消息
* @param @param message
* @param @throws IOException
* @return void
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月11日
*/
public synchronized void sendMsg(String message) throws IOException{
if(connections.size()<=0)
return;
for (WebSocketEntity entity : connections) {
synchronized (entity) {
((Session) entity.getSession()).getBasicRemote().sendText(message); // 回复用户
}
}
}
/**
*
* @Description: 启动心跳包
* @param
* @return void
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
private synchronized void startHeart(){
ExamineHeartThread examineHeart =new ExamineHeartThread();
Thread examineThread=new Thread(examineHeart);
KeepHeartThread keepHeart=new KeepHeartThread();
Thread keepThread=new Thread(keepHeart);
keepThread.start();
examineThread.start();
}
/**
*
* @Description: 获取时间戳
* @param @return
* @return long
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月22日
*/
private static long getTimeInMillis(){
Calendar c = Calendar.getInstance();
c.set(Calendar.SECOND,c.get(Calendar.SECOND)+8);
return c.getTimeInMillis();
}
/**
*
* @author 黑暗料理界扛把子
*
* @Description server发送心跳包 10秒一次
*/
private class KeepHeartThread implements Runnable {
@Override
public void run() {
JSONObject heartJson=new JSONObject();
heartJson.put("type", "0");
heartJson.put("secret", "heart_keep");
while (true) {
try {
logger.debug("发送心跳包当前人数为:"+getUserOnlineNum());
sendPing(heartJson.toString());
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
*
* @author 黑暗料理界扛把子
*
* @Description 检测是否收到client心跳 每秒一次
*/
private class ExamineHeartThread implements Runnable{
@Override
public void run() {
while (true) {
try {
long timeMillins=System.currentTimeMillis();
for (WebSocketEntity entity : connections) {
logger.debug(timeMillins);
logger.info(entity.getTimeStr());
logger.debug(timeMillins>entity.getTimeStr());
if(!entity.isHeart() && entity.getTimeStr()!=0 && timeMillins>entity.getTimeStr()){
logger.debug(entity.getMemberName()+"挂了");
onClose(entity.getUserId(),entity.getSession(),new CloseReason(CloseCodes.NORMAL_CLOSURE, "没有收到心跳"));
}
}
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
WebSocketEntity实体类
package com.dnn.entity;
import javax.websocket.Session;
import com.dnn.model.TbAdminMember;
public class WebSocketEntity {
private String userId;//用户id
private Session session;
private String memberName;//用户姓名
private String memberHead;//头像
private long timeStr;//记录下次发送时间的时间戳
private boolean isHeart=false;//是否收到了心跳
public boolean isHeart() {
return isHeart;
}
public void setHeart(boolean isHeart) {
this.isHeart = isHeart;
}
public String getMemberName() {
return memberName;
}
public void setMemberName(String memberName) {
this.memberName = memberName;
}
public String getMemberHead() {
return memberHead;
}
public void setMemberHead(String memberHead) {
this.memberHead = memberHead;
}
public long getTimeStr() {
return timeStr;
}
public void setTimeStr(long timeStr) {
this.timeStr = timeStr;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
public WebSocketEntity(String userId, Session session, String memberName, String memberHead) {
super();
this.userId = userId;
this.session = session;
this.memberName = memberName;
this.memberHead = memberHead;
}
public WebSocketEntity(TbAdminMember member, Session session) {
super();
this.userId = member.getId();
this.session = session;
this.memberName = member.getName();
this.memberHead = member.getHead();
}
@Override
public String toString() {
return "WebSocketEntity [userId=" + userId + ", session=" + session + ", memberName=" + memberName
+ ", memberHead=" + memberHead + ", timeStr=" + timeStr + ", isHeart=" + isHeart + "]";
}
@Override
public int hashCode() {
return this.userId.length();
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof WebSocketEntity)){
return false;
}
if(obj==this){
return true;
}
return this.userId.equals(((WebSocketEntity)obj).userId);
}
}