众所周知,websocket是用于消息推送的,而最好的理解它的方法就是做网页实时聊天功能了,所以这里实现了一个简单的网页即时聊天实例:
服务器端:
这里用的maven依赖有:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-messaging -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
1 .实现WeSocketConfigurer接口的registerWeSocketHandlers方法:配置wesocket允许访问的域
@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
@Autowired
@Lazy
private SimpMessagingTemplate template;
/** {@inheritDoc} */
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(logWebSocketHandler(), "/log").addInterceptors(new WebSocketHandshakeInterceptor());
}
@Bean
public WebSocketHandler logWebSocketHandler() {
return new LogWebSocketHandler(template);
}
}
2.消息转发核心类,可以将消息转发给任何一个用户。这里的消息类型由TextWebSocketHandler决定,可其他的handler类型。
public class LogWebSocketHandler extends TextWebSocketHandler {
private static final Map<String, WebSocketSession> users;
private static final String String = null;
static {
users = new HashMap<>();
}
private SimpMessagingTemplate template;
public LogWebSocketHandler(SimpMessagingTemplate template) {
this.template = template;
System.out.println(" handler");
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String text = message.getPayload();
System.out.println("handMessage:" + text);
System.out.println("handMessage:" + message.toString());
// template.convertAndSend("/log", text);
sendMessageToAllUsers(message);
}
/**
* 发送给所有人
*
*
* @param message
* @return
*/
public boolean sendMessageToAllUsers(TextMessage message) {
boolean allSendSuccess = true;
Set<String> clientIds = users.keySet();
WebSocketSession session = null;
for (String clientId : clientIds) {
try {
session = users.get(clientId);
if (session.isOpen()) {
session.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
allSendSuccess = false;
}
}
return allSendSuccess;
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
// TODO Auto-generated method stub
System.out.println("connection closed...");
super.afterConnectionClosed(session, status);
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// TODO Auto-generated method stub
System.out.println("connection established...");
String userid = (String) session.getAttributes().get("userid");
System.out.println("add userId " + userid);
users.put(userid, session);
super.afterConnectionEstablished(session);
}
}
3.握手类,也可以说是拦截器,是在用户连接时的响应操作。这里将用户信息存进session,记录需要转发消息的用户传递给handler。
public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
// TODO Auto-generated method stub
HttpServletRequest ServletHttpRequest = ((ServletServerHttpRequest) request).getServletRequest();
String userid = ServletHttpRequest.getParameterMap().get("userid")[0];
System.out.println("intercepter userid : " + userid);
HttpSession session = ServletHttpRequest.getSession();
if (session != null) {
attributes.put("userid", userid);
}
return true;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception exception) {
// TODO Auto-generated method stub
}
}
4.访问聊天页面的controller
@Controller
public class TestController {
@RequestMapping("test")
public String test() {
return "/jsp/websoketTest.jsp";
}
}
客户端:
1.页面
<%@page import="com.common.SysInfo"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="<%=basePath %>javascript/jquery-min.js" ></script>
<script type="text/javascript" src="<%=basePath %>javascript/websoket1.js" ></script>
<script type="text/javascript" src="<%=basePath %>javascript/sockjs.min.js" ></script>
<link rel="stylesheet" href="<%=basePath %>css/websokect1.css" type="text/css"/>
</head>
<body>
<div id="skm_LockPane" class="LockOff"></div>
<form id="form1" runat="server" action="">
<label>服务器地址</label><input type="text" id="addr"/><br/>
<label>用户名</label><input type="text" id="username" value="太阳"/><br/>
<input type="button" id="conn" value="连接" onclick="ToggleConnectionClicked();"/><br/>
<div id="LogContainer" class="container"></div><br/>
<div id="SendDataContainer">
<input type="text" id="DataToSend" size="88"/>
<input type="button" id="SendData" value="发送" onclick="SendDataClicked();"/><br/>
</div>
</form>
</body>
</html>
2.websocket客户端的js代码:
var ws;
var SocketCreated = false;
var isUserloggedout = false;
function lockOn(str) {
var lock = document.getElementById("skm_LockPane");
if (lock) {
lock.className = "LockOn";
}
lock.innerHTML = str;
}
function lockOff() {
var lock = document.getElementById("skm_LockPane");
lock.className = "LockOff";
}
function ToggleConnectionClicked() {
if (SocketCreated && (ws.readyState == 0 || ws.readyState == 1)) {
lockOn("离开聊天室...");
sockedCreated = false;
isUserloggedout = true;
ws.close();
} else {
lockOn("准备进入聊天室...");
Log("准备连接到聊天服务器...");
try {
if ("WebSocket" in window) {
ws = new WebSocket("ws://"
+ document.getElementById("addr").value + "?userid="
+ document.getElementById("username").value);
} else if ("MozWebSocket" in window) {// firefox
ws = new MozWebSocket("ws://"
+ document.getElementById("addr").value + "?userid="
+ document.getElementById("username").value);
} else {
ws = new SockJS("http://"
+ document.getElementById("addr").value + "?userid="
+ document.getElementById("username").value);
}
SocketCreated = true;
isUserloggedout = false;
} catch (ex) {
Log(ex, "ERROR");
return;
}
document.getElementById("conn").value = "断开";
ws.onopen = WSonOpen;
ws.onmessage = WSonMessage;
ws.onclose = WSonClose;
ws.onerror = WSonError;
}
}
function WSonOpen() {
lockOff();
Log("连接已建立。", "OK");
$("#SendDataContainer").show();
ws.send("login:" + document.getElementById("username").value);
}
function WSonMessage(event) {
Log(event.data);
}
function WSonClose() {
lockOff();
if (isUserloggedout) {
Log("【" + document.getElementById("username").value + "】离开了聊天室!");
}
document.getElementById("conn").value = "连接";
// document.getElementById("conn").innerHTML = "连接";
$("#SendDataContainer").hide();
}
function WSonError() {
lockOff();
Log("远程连接中断。", "ERROR");
}
function SendDataClicked() {
if (document.getElementById("DataToSend").value.trim() != "") {
ws.send(document.getElementById("username").value + "说:\""
+ document.getElementById("DataToSend").value + "\"");
document.getElementById("DataToSend").value = "";
}
}
function Log(Text, MessageType) {
console.log(Text);
if (MessageType == "OK") {
Text = "<span style='color: green;'>" + Text + "</span>";
}
if (MessageType == "ERROR") {
Text = "<span style='color: red;'>" + Text + "</span>";
}
document.getElementById("LogContainer").innerHTML = document
.getElementById("LogContainer").innerHTML
+ Text + "<br/>";
var LogContainer = document.getElementById("LogContainer");
LogContainer.scrollTop = LogContainer.scrollHeight;
}
$(document)
.ready(
function() {
$("#SendDatacontainer").hide();
var WebSocketsExist = true;
try {
var dummy = new WebSocket(
"ws://<%=SysInfo.BASE_IP%>:<%=SysInfo.BASE_PORT%>/TProject/log");
} catch (ex) {
try {
webSocket = new MozWebSocket(
"ws://<%=SysInfo.BASE_IP%>:<%=SysInfo.BASE_PORT%>/TProject/log");
} catch (ex) {
try {
websocket = new SockJS(
"http://<%=SysInfo.BASE_IP%>:<%=SysInfo.BASE_PORT%>/TProject/log");
} catch (ex) {
WebSocketsExist = false;
}
}
}
if (WebSocketsExist) {
Log("您的浏览器支持ws,您可以尝试连接到聊天服务器!", "OK");
document.getElementById("addr").value = "20.16.1.22:8081/TProject/log";
} else {
Log("您的浏览器不支持ws。请选择其他的浏览器再尝试连接服务器。", "ERROR");
document.getElementById("addr").disabled = true;
}
$("#DataToSend").keypress(function(evt) {
if (evt.keyCode == 13) {
$("#SendData").click();
evt.preventDefault();
}
});
});