本次学习websocket主要是用来做实时推送。暂先学习,以便后面用到,这里提供两个简单的Demo,都是建立在Maven基础上的项目,来源于网上资料,经过自己的调式后都可以正常运行的,至于websocket的基本知识的讲解,可以先去w3c或者是IBM的网站上找一下相关的资料,这两个网站上讲的比较通俗易懂。
第一个Demo(基本的讲解都在这个Demo中):
本地文件Eclipse–>GasWebSocket
pom的依赖如下:
<dependencies>
<!-- socketJs start -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- socketJs end -->
<!-- spring start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- spring end -->
</dependencies>
<build>
<finalName>GasWebSocket</finalName>
<!-- 配置Maven对resource文件的过滤 -->
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/webapp</directory>
<includes>
<include>**/*.css</include>
<include>**/*.html</include>
<include>**/*.js</include>
<include>**/*.jpg</include>
<include>**/*.jsp</include>
<include>**/*.ttf</include>
<include>**/*.woff</include>
<include>**/*.woff2</include>
<include>**/*.svg</include>
<include>**/*.eot</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
后台的代码如下:
package com.hhu.websocket;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.RemoteEndpoint;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
/**
* 在tomcat7中存在WebSocketServlet类(但已经过时),在tomcat8中彻底删除
* 此处使用@ServerEndpoint注解,主要是将目前的类定义成一个websocket服务器端点
* 注解的值将被用于监听用户连接的终端访问URL地址,即前台请求后台的地址
*/
@ServerEndpoint("/getServer")
public class SocketServer{
/**
* 当一个新用户连接时所调用的方法
* 该方法可能包含一个javax.websocket.Session可选参数
* 如果有这个参数,容器将会把当前发送消息客户端的连接Session注入进去
* @throws UnsupportedEncodingException
*/
@OnOpen
public void onOpen(Session session) throws UnsupportedEncodingException {
//用于向客户端链接时向客户端发送信息载体
final RemoteEndpoint.Basic basic = session.getBasicRemote();
//这里建立连接的时候前台请求链接时候所带的参数
System.out.println("pathParams:"+session.getPathParameters());
System.out.println("requestParams"+session.getRequestParameterMap());
try {
basic.sendText("成功链接服务器");
} catch (IOException e) {
e.printStackTrace();
}
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.currentThread();
Thread.sleep(8000);//线程睡眠8s
basic.sendText("服务器:我是服务器");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
t1.start();
}
/**
* 客户端接收到服务端数据时触发
* @param relationId
* @param userCode
* @param message
* @return
*/
@OnMessage
public String onMessage(Session session,String message) {
System.out.println("pathParams:"+session.getPathParameters());
System.out.println("requestParams"+session.getRequestParameterMap());
return "服务器:客户端我收到你的信息了,你发了“"+message + "”";
}
/**
* 通信发生错误时触发
* @param relationId
* @param userCode
* @param session
*/
@OnError
public void onError(Throwable throwable,Session session) {
System.out.println("pathParams:"+session.getPathParameters());
System.out.println("requestParams"+session.getRequestParameterMap());
System.out.print("发生错误了:"+throwable.getMessage());
}
/**
* 关闭连接时触发
* @param relationId
* @param userCode
* @param session
*/
@OnClose
public void onClose(Session session) {
System.out.println("pathParams:"+session.getPathParameters());
System.out.println("requestParams"+session.getRequestParameterMap());
System.out.print("链接关闭 ");
}
}
前台的页面:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
//这里普通websocket建立连接的时候前缀用ws作为前缀,如果需要ssl加密,则需要用wss
String socPath="ws://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>websocketIndex</title>
<script type="text/javascript">
//这里的请求的url就是前面用@ServerEndPoint注释的映射地址
var wsuri = "<%=socPath%>getServer?'参数1'&'参数2'";
var ws = null;
function startWebSocket() {
if ('WebSocket' in window)
//创建WebSocket对象:var Socket = new WebSocket(url, [protocol]),第二个参数 protocol 是可选的,指定了可接受的子协议
alert("您当前使用的浏览器支持WebSocket!");
ws = new WebSocket(wsuri);
else if ('MozWebSocket' in window)
ws = new MozWebSocket(wsuri);
else
console.error("not support WebSocket!");
//获取消息事件
ws.onmessage = function(evt) {
alert(evt.data);
console.info(evt);
};
//关闭websocket
ws.onclose = function(evt) {
alert("链接关闭");
console.info(evt);
};
//打开链接
ws.onopen = function(evt) {
alert("正在尝试链接服务器");
console.info(evt);
};
};
init();
function init(){
startWebSocket();
};
//发送消息
function sendMsg(){
//Socket.send()使用链接发送数据;Socket.close()关闭链接
ws.send(document.getElementById('writeMsg').value);
}
</script>
</head>
<body>
<input type="text" id="writeMsg"/>
<input type="button" value="发送" onclick="sendMsg()"/>
<br>
<span>
等待8s,你将收到服务端的发送的消息
</span>
</body>
</html>
第二个Demo(将Websocket整合到SSM中)
Eclipse—>JavaWebSocket
pom的依赖文件如下:注意这里的Spring一定要是4.0以上才能支持websocket!
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
<!-- spring核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
web.xml中的配置要配置Spring的文件地址和Spring相关的监听器:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
</web-app>
Spring的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean name="monitor" class="Monitor" lazy-init="false" init-method="sendMsg"></bean>
</beans>
后台时间监视器的代码:
import com.uptop.websocket.WebSocketTest;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Monitor implements Runnable {
@Override
public void run() {
WebSocketTest webSocketTest = new WebSocketTest();
webSocketTest.sendMsg("当前时间:" + new Date());
}
public void sendMsg() {
ScheduledExecutorService newScheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
newScheduledThreadPool.scheduleWithFixedDelay(new Monitor(), 20, 5, TimeUnit.SECONDS);
}
}
websocket的服务类:
package com.uptop.websocket;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
* @author uptop
*/
@ServerEndpoint("/websocket")
public class WebSocketTest {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
public static CopyOnWriteArraySet<WebSocketTest> webSocketSet = new CopyOnWriteArraySet<WebSocketTest>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
/**
* 连接建立成功调用的方法
*
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
webSocketSet.add(this); //加入set中
addOnlineCount(); //在线数加1
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
webSocketSet.remove(this); //从set中删除
subOnlineCount(); //在线数减1
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
//群发消息
for (WebSocketTest item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
/**
* 发生错误时调用
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}
/**
* 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
*
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketTest.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketTest.onlineCount--;
}
public void sendMsg(String msg) {
for (WebSocketTest item : webSocketSet) {
try {
item.sendMessage(msg);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
}
最后是前台页面:
<%@ page language="java" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>index Page</title>
</head>
<body>
Welcome<br/><input id="text" type="text"/>
<button onclick="send()">发送消息</button>
<hr/>
<button onclick="closeWebSocket()">关闭WebSocket连接</button>
<hr/>
<div id="message"></div>
<table id="tb" class="altrowstable">
<th align="center" colspan="9">实时信息监控</th>
</table>
</body>
<script type="text/javascript">
var websocket = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8080/JavaWebSocket/websocket");
}
else {
alert('当前浏览器 Not support websocket')
}
//连接发生错误的回调方法
websocket.onerror = function () {
setMessageInnerHTML("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function () {
setMessageInnerHTML("WebSocket连接成功");
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
setMessageInnerHTML(event.data);
}
//连接关闭的回调方法
websocket.onclose = function () {
setMessageInnerHTML("WebSocket连接关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
var msg=innerHTML.split(" - ")
var table=document.getElementById("tb");
var row;
row=table.insertRow(1);
for(var i=0;i<msg.length;i++){
var cell = row.insertCell(i);
cell.appendChild(document.createTextNode(msg[i]));
}
if(table.rows.length>50){
table.deleteRow(table.rows.length-1);
}
// document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
}
//发送消息
function send() {
var message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</html>