目前要在后端中实现websocket基本就是依靠javaee-api或者spring4.0以后的spring版本,本片文章记录下使用javaee-api后端的websocket的过程。
项目依赖
本项目使用maven构建,相关的几个依赖如下:(其他依赖不再列出)
<!-- 添加servlet3.0核心包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.2-b01</version>
</dependency>
<!-- WebSocket配置开始-->
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
<!-- WebSocket配置结束-->复制代码
服务端具体实现
创建HttpSession的配置器类HttpSessionConfiguratorpublic class HttpSessionConfigurator extends ServerEndpointConfig.Configurator{
//用来保存session的信息,后面的websocket就可以获取到了
@Override
public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
HttpSession session = (HttpSession) request.getHttpSession();
config.getUserProperties().put(HttpSession.class.getName(), session);
}
}
复制代码
创建ChatServer,最主要的websocket实现类,通过注解实现连接、关闭、接收消息等等方法。
//@ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
//configurator使用上一步的HttpSessionConfigurator类,这样就可以通过EndpointConfig的实例获取session
@ServerEndpoint(value="/chatServer/{userid}", configurator = HttpSessionConfigurator.class)
public class ChatServer {
private static Logger logger = Logger.getLogger(ChatServer.class);
private static int onlineCount = 0; // 记录连接数目
// Map<用户id,用户信息>
private static Map<String, OnlineUser> onlineUserMap = new ConcurrentHashMap<String, OnlineUser>(); // 在线用户
/**
* 连接成功调用的方法
*/
@OnOpen
public void onOpen(@PathParam("userid") String userid , Session session, EndpointConfig config){
logger.info("[ChatServer] connection : userid = " + userid + " , sessionId = " + session.getId());
// 增加用户数量
addOnlineCount();
// 获取当前用户的session
HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
User user = (User) httpSession.getAttribute("user"); // 获得当前用户信息
// 将当前用户存到在线用户列表中
OnlineUser onlineUser = new OnlineUser(user.getUserid(),user.getNickname(),session);
onlineUserMap.put(user.getUserid(),onlineUser);
// 通知所有在线用户,当前用户上线
String content = "[" + CommonDate.getTime24() + " : " + user.getNickname() + "加入聊天室,当前在线人数为 " + getOnlineCount() + "位" + "]";
JSONObject msg = new JSONObject();
msg.put("content",content);
String message = Message.getMessage(msg.toString(),Message.NOTICE,onlineUserMap.values());
Message.broadcast(message,onlineUserMap.values());
}
/**
* 连接关闭方法
*/
@OnClose
public void onClose(@PathParam("userid") String userid,Session session,CloseReason closeReason){
logger.info("[ChatServer] close : userid = " + userid + " , sessionId = " + session.getId() +
" , closeCode = " + closeReason.getCloseCode().getCode() + " , closeReason = " +closeReason.getReasonPhrase());
// 减少当前用户
subOnlienCount();
// 移除的用户信息
OnlineUser removeUser = onlineUserMap.remove(userid);
onlineUserMap.remove(userid);
// 通知所有在线用户,当前用户下线
String content = "["+ CommonDate.getTime24() + " : " + removeUser.getNickname() + " 离开聊天室,当前在线人数为 " + getOnlineCount() + "位" + "]";
JSONObject msg = new JSONObject();
msg.put("content",content);
if(onlineUserMap.size() > 0){
String message = Message.getMessage(msg.toString(), Message.NOTICE, onlineUserMap.values());
Message.broadcast(message,onlineUserMap.values());
}else{
logger.info("content : ["+ CommonDate.getTime24() + " : " + removeUser.getNickname() + " 离开聊天室,当前在线人数为 " + getOnlineCount() + "位" + "]");
}
}
/**
* 接收客户端的message,判断是否有接收人而选择进行广播还是指定发送
* @param data 客户端发来的消息
*/
@OnMessage
public void onMessage(@PathParam("userid") String userid,String data){
logger.info("[ChatServer] onMessage : userid = " + userid + " , data = " + data);
JSONObject messageJson = JSONObject.fromObject(data);
JSONObject message = messageJson.optJSONObject("message");
String to = message.optString("to");
String from = message.optString("from");
// 将用户id转换为名称
to = this.userIdCastNickName(to);
OnlineUser fromUser = onlineUserMap.get(from);
String sendMessage = Message.getContent(fromUser,to,message.optString("content"),message.optString("time"));
String returnData = Message.getMessage(sendMessage, messageJson.optString("type"),null);
if(to == null || to.equals("")){ // 进行广播
Message.broadcast(returnData.toString(),onlineUserMap.values());
}else{
Message.singleSend(returnData.toString(), onlineUserMap.get(from)); // 发送给自己
String[] useridList = message.optString("to").split(",");
for(String id : useridList){
if(!id.equals(from)){
Message.singleSend(returnData.toString(), onlineUserMap.get(id)); // 分别发送给指定的用户
}
}
}
}
/**
* 发生错误
* @param throwable
*/
@OnError
public void onError(@PathParam("userid") String userid,Session session,Throwable throwable){
logger.info("[ChatServer] close : userid = " + userid + " , sessionId = " + session.getId() +" , throwable = " + throwable.getMessage() );
}
public static int getOnlineCount() {
return onlineCount;
}
public synchronized void addOnlineCount(){
onlineCount++;
}
public synchronized void subOnlienCount(){
onlineCount--;
}
/**
* 将用户id转换为名称
* @param userIds
* @return
*/
private String userIdCastNickName(String userIds){
String niceNames = "";
if(userIds != null && !userIds.equals("")){
String[] useridList = userIds.split(",");
String toName = "";
for (String id : useridList){
toName = toName + onlineUserMap.get(id).getNickname() + ",";
}
niceNames = toName.substring(0,toName.length() - 1);
}
return niceNames;
}
}复制代码
Message类,组装信息,发送或广播到具体客户端。
public class Message {
private static Logger logger = Logger.getLogger(Message.class);
/**
* 消息类型
*/
public static String NOTICE = "notice"; //通知
public static String MESSAGE = "message"; //消息
/**
* 组装信息返回给前台
* @param message 交互信息
* @param type 信息类型
* @param userList 在线列表
* @return
*
* "massage" : {
* "from" : "xxx",
* "to" : "xxx",
* "content" : "xxx",
* "time" : "xxxx.xx.xx"
* },
* "type" : {notice|message},
* "list" : {[xx],[xx],[xx]}
*/
public static String getMessage(String message,String type,Collection<OnlineUser> userList){
JSONObject msg = new JSONObject();
msg.put("message",message);
msg.put("type", type);
if(CollectionUtils.isNotEmpty(userList)){
List<String> propertys = new ArrayList<String>();
propertys.add("session");
JSONArray userListArray = JSONArray.fromObject(userList,JsonConfigUtils.getJsonConfig(propertys));
msg.put("list", userListArray);
}
return msg.toString();
}
/**
* 消息内容
* @param fromUser
* @param to
* @param content
* @param time
* @return
* {
* "from" : "xxx",
* "to" : "xxx",
* "content" : "xxx",
* "time" : "xxxx.xx.xx"
* }
*/
public static String getContent(OnlineUser fromUser,String to,String content,String time){
JSONObject contentJson = new JSONObject();
// 转化为json串时去掉session,用户session不能被序列化
List<String> propertys = new ArrayList<String>();
propertys.add("session");
contentJson.put("from",JSONObject.fromObject(fromUser,JsonConfigUtils.getJsonConfig(propertys)));
contentJson.put("to",to);
contentJson.put("content",content);
contentJson.put("time",time);
return contentJson.toString();
}
/**
* 广播消息
* @param message 消息
* @param onlineUsers 在线用户
*/
public static void broadcast(String message,Collection<OnlineUser> onlineUsers){
StringBuilder userStr = new StringBuilder();
for(OnlineUser user : onlineUsers){
userStr.append(user.getNickname()).append(",");
}
userStr.deleteCharAt(userStr.length()-1);
logger.info("[broadcast] message = " + message + ", onlineUsers = " + userStr.toString());
for(OnlineUser user : onlineUsers){
try {
for (int i = 0; i < 20; i++) {
user.getSession().getBasicRemote().sendText(message);
}
} catch (IOException e) {
e.printStackTrace();
logger.info("消息发送失败!" + e.getMessage());
}
}
}
/**
* 对特定用户发送消息
* @param message
* @param onlineUser
*/
public static void singleSend(String message, OnlineUser onlineUser){
logger.info("[singleSend] message = " + message + ", toUser = " + onlineUser.getNickname());
try {
onlineUser.getSession().getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
logger.info("消息发送失败!" + e.getMessage());
}
}
}复制代码
客户端实现
var wsServer = null;
var ws = null;
wsServer = "ws://" + location.host+"${pageContext.request.contextPath}" + "/chatServer/${userid}";
ws = new WebSocket(wsServer); //创建WebSocket对象
ws.onopen = function (evt) {
layer.msg("已经建立连接", { offset: 0});
};
ws.onmessage = function (evt) {
analysisMessage(evt.data); //解析后台传回的消息,并予以展示
};
ws.onerror = function (evt) {
layer.msg("产生异常", { offset: 0});
};
ws.onclose = function (evt) {
layer.msg("已经关闭连接," + evt.reason, { offset: 0});
};复制代码