编译环境:jdk1.8 , tomcat8.0+ , IDEA
这里主要讲一下websocket的信息传递,以如何实现多人实时在线聊天为例:
websocket主要的三个类
MyWebSocketConfig主要负责配置websocket的处理器和握手拦截器
MyHandShakeInterceptor 是websocket的拦截器
MyWebSocketHander是websocket的处理器
@Component
@EnableWebSocket
public class MyWebSocketConfig implements WebSocketConfigurer {
@Autowired
private MyWebSocketHander myWebSocketHander;
private static final String LINK_URI = "websocket.do";
//添加websocket处理器,添加握手拦截器 拦截器先执行 然后到处理器
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(myWebSocketHander,LINK_URI).addInterceptors(new MyHandShakeInterceptor());
}
}
/* private static Map<WebSocketSession,String> map = new HashMap<WebSocketSession, String>();
* websocket握手拦截器
* 拦截握手前,握手后的两个切面
*/
@Component
public class MyHandShakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
if(serverHttpRequest instanceof ServletServerHttpRequest){
HttpServletRequest servletRequest = ((ServletServerHttpRequest)serverHttpRequest).getServletRequest();
User user = (User)servletRequest.getSession().getAttribute("user");
//这里给map赋值 相当于websockethandler的afterConnectionEstablished方法里的WebSocketSession
//key是session,value是变量
map.put("ws_user", user);
}
return true;
}
@Override
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
}
}
实时通信的具体流程:
前端
<% String path = request.getContextPath();%>
<html>
link rel="stylesheet" href="<%=path%>/static/css/bootstrap.min.css"
type="text/css">
<script src="<%=path%>/static/js/jquery.min.js"></script>
<script src="<%=path%>/static/js/bootstrap.min.js"
type="text/javascript"></script>
<body>
<div id="container" style="width:500px">
<div id="header">
<h1 id="title">chat-room</h1></div>
<div class="middle">
<div id="menu">
<p id="tou">欢迎来到聊天室</p>
</div>
<div class="chatter" id="chatter">
<p id="msg"></p>
</div>
</div>
<div id="content">
<textarea class="form-control" rows="3" placeholder="我想说....."
id="msgContent">
</textarea>
</div>
<div style="background-color: #F8F8F8;">
<div id="buttons">
<button id="butSent" type="button" class="btn btn-default"
onclick="getConnection()">连接
</button>
<button type="button" class="btn btn-default"
onclick="sendMsgClose()">断开
</button>
<button type="button" class="btn btn-default" onclick="sendMsg()">
发送
</button>
</div>
</div>
<div id="footer">
Designed by Annie
</div>
</div>
</body>
<script>
//打开链接
function getConnection() {
if (websocket == null) {
websocket = new WebSocket(wsServer);
websocket.onopen = function (evnt) {
alert("链接服务器成功!");
};
//从后台接受数据的函数
websocket.onmessage = function (evnt) {
var onlineUser = $("#onlineUser");
//将收到的数据转换成对象
var message = eval("(" + evnt.data + ")");
//显示在线人数及在线用户
if (message.msgTyp === "notice") {
var htmlOnline;
$("#onlineNum").text(message.onlineNum);
htmlOnline = "<p> " + message.userName + " </p>";
//实时更新在线用户
onlineUser.html("");
$(onlineUser).append(htmlOnline);
} else if (message.msgTyp === "msg") {
showChat(evnt);
}
};
websocket.onerror = function (evnt) {
alert("发生错误,与服务器断开了链接!")
};
websocket.onclose = function (evnt) {
alert("与服务器断开了链接!")
};
} else {
alert("连接已存在!")
}
}
function showChat(evnt) {
var message = eval("(" + evnt.data + ")");
var msg = $("#msg");
//msg.html是之前的聊天内容,空一行
msg.html(msg.html() + "<br/>" + "用户: " + message.user + " 发送时间:" + message.sendDate + "<br/>" + message.sendContent);
}
function sendMsg() {
var msg = $("#msgContent");
if (websocket == null) {
alert("连接未开启!");
return;
}
var message = msg.val();
//输入完成后,清空输入区
msg.val("");
if (message == null || message === "") {
alert("输入不能为空的哦");
return;
}
//向后台MyWebSocketHandler中的handerMessage发送信息
//这里将信息转成JSON格式发送
websocket.send(JSON.stringify({
message: message,
type: "chatMsg"
}));
}
/**
* 关闭连接
*/
function closeConnection() {
if (websocket != null) {
websocket.close();
websocket = null;
alert("已经关闭连接")
} else {
alert("未开启连接")
}
}
</script>
</html>
后台MyWebSocketHander接受数据
@Component
public class MyWebSocketHander implements WebSocketHandler {
private final static List<WebSocketSession> USERS = new ArrayList<>();
private final static List<User> USER_ONLINE = new ArrayList<>();
/*
*在链接创建完后就在前端显示在线用户
*/
@Override
public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
USERS.add(webSocketSession);
//每次有新的连接,就加入到user集合中
User user = (User) webSocketSession.getAttributes().get("ws_user");
USER_ONLINE.add(user);
List<String> userNamelist = new ArrayList<>();
for (User u : USER_ONLINE) {
String userName = u.getUserName();
userNamelist.add(userName);
}
//String类的format()方法用于创建格式化的字符串以及连接多个字符串对象。
//这里传到前端的应该是JSON格式
String messageFormat = "{onlineNum:\"%d\",userName:\"%s\" , msgTyp " +
":\"%s\"}";
String msg = String.format(messageFormat, USERS.size(), userNamelist,
"notice");
TextMessage testMsg = new TextMessage(msg + "");
//确保每个用户信息都能同步到
for (WebSocketSession wss : USERS) {
wss.sendMessage(testMsg);
}
}
/**
* 客户端发送服务器的消息时的处理函数,在这里收到消息之后可以分发消息
*/
@Autowired
private ChatService chatService;
@Override
public void handleMessage(WebSocketSession webSocketSession,
WebSocketMessage<?> webSocketMessage) throws Exception {
String messageFormat = null;
//发送消息的时间
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String sentMsgDate = dateFormat.format(new Date());
User user = (User) webSocketSession.getAttributes().get("ws_user");
String msgContent = webSocketMessage.getPayload() + "";
JSONObject chat = JSON.parseObject(msgContent);
//消息的内容
String msgJSON = chat.get("message").toString();
//消息的样式
String msgJSONType = chat.get("type").toString();
String chatMsg = "chatMsg";
if (msgJSONType.equals(chatMsg)) {
//将消息保存到数据库
ChatMsg chatMessage = new ChatMsg(user.getId(), sentMsgDate,
msgJSON);
chatService.addMessage(chatMessage);
messageFormat = "{user:\"%s\",sendDate:\"%s\" ," +
"sendContent:\"%s\" , msgTyp :\"%s\"}";
String message = String.format(messageFormat, user.getUserName(),
sentMsgDate, msgJSON , "msg");
TextMessage toMsg = new TextMessage(message + "");
//遍历所有的用户,发信息,这个要注意哦,要不然不能做到多人同时聊天
for (WebSocketSession wss : USERS) {
wss.sendMessage(toMsg);
}
}
}
@Override
public void handleTransportError(WebSocketSession webSocketSession,
Throwable throwable) throws Exception {
USERS.remove(webSocketSession);
}
@Override
public void afterConnectionClosed(WebSocketSession webSocketSession,
CloseStatus closeStatus) throws Exception {
User userRemove = (User) webSocketSession.getAttributes().get(
"ws_user");
USER_ONLINE.remove(userRemove);
USERS.remove(webSocketSession);
}
@Override
public boolean supportsPartialMessages() {
return false;
}
}
所以websocket消息传送流程大致是
websocket.onopen打开链接 --> websocket.send()发送数据 -->后台的MyWebSocketHander接收数据-->WebSocketSession.sendMessage将消息发送给前端 --> 前端websocket.onmessage()接收数据并显示。
github地址:https://github.com/androidlearner2017/simple-chatroom.git
很简单的一个demon,里面有些功能还没有完全实现,继续修改中 ing