引言:
websocket,webservice傻傻分不清楚,都觉得是很高深的东西,理解中的webservice是一种协议,通信协议,类似http协议的那种,比如使用webservice协议调后台接口,而websocket呢?与socket挂钩?长连接?对未知的东西总是恐惧的,所以默默不敢说话
启航:
学习过程中突然接触到了websocket的简单讲解,哦,websocket也是一种协议,它类似ajax,但连接不中断,接到消息就响应。叫什么双端通信。websocket请求头是ws://或者wss://开头,非安全与安全,后面就和http请求类似。后台写法当然与默认的http servlet有些不同,但变化不大,与springMVC的requestMapping有些相似,接受到请求可以进行拦截等处理,当然也可以限制接收请求的具体参数。
概念来一波:



原先实现模拟双端通信的手段:
在WebSocket规范提出之前,开发人员若要实现这些实时性较强的功能,经常会使用折衷的解决方法:轮询(polling)和Comet技术。其实后者本质上也是一种轮询,只不过有所改进。
轮询是最原始的实现实时Web应用的解决方案。轮询技术要求客户端以设定的时间间隔周期性地向服务端发送请求,频繁地查询是否有新的数据改动。明显地,这种方法会导致过多不必要的请求,浪费流量和服务器资源。
Comet技术又可以分为长轮询和流技术。长轮询改进了上述的轮询技术,减小了无用的请求。它会为某些数据设定过期时间,当数据过期后才会向服务端发送请求;这种机制适合数据的改动不是特别频繁的情况。流技术通常是指客户端使用一个隐藏的窗口与服务端建立一个HTTP长连接,服务端会不断更新连接状态以保持HTTP长连接存活;这样的话,服务端就可以通过这条长连接主动将数据发送给客户端;流技术在大并发环境下,可能会考验到服务端的性能。
这两种技术都是基于请求-应答模式,都不算是真正意义上的实时技术;它们的每一次请求、应答,都浪费了一定流量在相同的头部信息上,并且开发复杂度也较大。
特点:
1、双端通信
2、建立在TCP之上
3、协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
接入:
注意:JavaEE 7中出了JSR-356:Java API for WebSocket规范。不少Web容器,如Tomcat,Nginx,Jetty等都支持WebSocket。Tomcat从7.0.27开始支持 WebSocket,从7.0.47开始支持JSR-356
websocket客户端:
在客户端,没有必要为 WebSockets 使用 JavaScript 库。实现 WebSockets 的 Web 浏览器将通过 WebSockets 对象公开所有必需的客户端功能(主要指支持 Html5 的浏览器)。
客户端API:
以下 API 用于创建 WebSocket 对象。
var Socket = new WebSocket(url, [protocol] );
以上代码中的第一个参数 url, 指定连接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。
WebSocket 属性
以下是 WebSocket 对象的属性。假定我们使用了以上代码创建了 Socket 对象:
属性描述
Socket.readyState
只读属性 readyState 表示连接状态,可以是以下值:0 - 表示连接尚未建立。1 - 表示连接已建立,可以进行通信。2 - 表示连接正在进行关闭。3 - 表示连接已经关闭或者连接不能打开。
Socket.bufferedAmount
只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。
WebSocket 事件
以下是 WebSocket 对象的相关事件。假定我们使用了以上代码创建了 Socket 对象:
事件事件处理程序描述
open
Socket.onopen
连接建立时触发
message
Socket.onmessage
客户端接收服务端数据时触发
error
Socket.onerror
通信发生错误时触发
close
Socket.onclose
连接关闭时触发
WebSocket 方法
以下是 WebSocket 对象的相关方法。假定我们使用了以上代码创建了 Socket 对象:
方法描述
Socket.send()
使用连接发送数据
Socket.close()
关闭连接
客户端实例:


User: zhen
Date:2018/12/10Time:17:36To changethis template use File | Settings |File Templates.--%>
welcome page发送消息
关闭webSocket连接
var webSocket = null;//判断当前浏览器是否支持webSocket
if ('WebSocket' inwindow) {
webSocket= new WebSocket("ws://localhost:8080/spring4webSocket/myHandler")
}else{
alert("当前浏览器 Not support webSocket");
}
webSocket.οnerrοr=onError;
webSocket.onopen=onOpen;
webSocket.onmessage=onMessage;
webSocket.onclose=onClose;
function onError() {
setMessageInnerHTML("WebSocket连接发生错误");
}
function onOpen() {
setMessageInnerHTML("WebSocket连接成功");
}
function onMessage(event){//将接受到的数据直接输出
setMessageInnerHTML(event.data);
}
function onClose() {
setMessageInnerHTML("webSocket连接关闭");
}
function setMessageInnerHTML(message) {var messageDiv = document.getElementById("message");
messageDiv.innerHTML= messageDiv.innerHTML + "
" +message;
}//监听串口关闭事件,当窗口关闭时,主动去关闭webSocket连接,防止还没断开就关闭窗口,srever端会抛异常
window.onbeforeunload =function (ev) {
closeWebSocket();
}
function closeWebSocket(){
webSocket.close();
}//发送消息
function send() {var message = document.getElementById("text").value;
webSocket.send(message);
}
客户端实例
服务端(java):
基于servlet api:
1、导入有关jar包
javax
javaee-api
7.0
provided
API:
@ServerEnpoint 声明webSocket服务端,指明映射url
@OnMessage 标注接收到消息执行监听方法
@OnOpen 标注打开连接时候执行监听方法
@OnClose 标注关闭连接时执行监听方法
@OnError 标注连接异常时执行监听方法
服务端实例:


package com.zhen.websocket;/**
* @author zhen
* @Date 2018/12/6 10:29*/import java.io.*;
import java.util.*;
import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;//@ServerEndpoint 注解允许你声明一个WeoSocket,定义url映射,定义编码和解码
@ServerEndpoint(
value="/story/notifications",
encoders={StickerEncoder.class},
decoders={StickerDecoder.class}
)public classStoryWebSocket {private static final List stickers = Collections.synchronizedList(new LinkedList());private static final Set sessions = Collections.synchronizedSet(new HashSet());
@OnMessagepublic voidonMessage(Session session, Sticker sticker){//有消息从客户端发送过来,保存到列表中,然后通知所有的客户端
stickers.add(sticker);for(Session openSession : sessions){try{
openSession.getBasicRemote().sendObject(sticker);
}catch (IOException |EncodeException e) {
sessions.remove(openSession);
}
}
}
@OnOpenpublic voidonOpen(Session session) throws IOException, EncodeException{//有新的客户端连接时,保存此客户端的session,并且把当前所有的sticker发送给它
sessions.add(session);for(Sticker sticker : stickers){
session.getBasicRemote().sendObject(sticker);
}
}
@OnClosepublic voidonClose(Session session){//有客户端断开连接时 ,从session列表中移除此客户端的session
sessions.remove(session);
}
}
服务端实例
看的有些混乱不能很好理解的时候就敲一些例子,功能出来就更容易理解了。


package com.zhen.websocket;/**
* @author zhen
* @Date 2018/12/6 10:28*/
public classSticker {private intx;private inty;privateString image;publicSticker() {
}public intgetX() {returnx;
}public void setX(intx) {this.x =x;
}public intgetY() {returny;
}public void setY(inty) {this.y =y;
}publicString getImage() {returnimage;
}public voidsetImage(String image) {this.image =image;
}
}
package com.zhen.websocket;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.spi.JsonProvider;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import java.io.IOException;
import java.io.Reader;/**
* @author zhen
* @Date 2018/12/6 11:08
* 用来读取webSocket流中的数据使用Decode.TextStream接口。 这个接口允许你读取数据从socket中通过JsonReader对象,构造传入Reader对象,并且转换客户端返回文本JSON数据*/
public class StickerDecoder implements Decoder.TextStream{//Do not create a JsonReader object. To create readers and writes, use the//JsonProvider class.
@Overridepublic voidinit(EndpointConfig config) {//TODO Auto-generated method stub
}
@Overridepublic voiddestroy() {//TODO Auto-generated method stub
}
@OverridepublicSticker decode(Reader reader) throws DecodeException, IOException {
JsonProvider provider=JsonProvider.provider();
JsonReader jsonReader=provider.createReader(reader);
JsonObject jsonSticker=jsonReader.readObject();
Sticker sticker= newSticker();
sticker.setX(jsonSticker.getInt("x"));
sticker.setY(jsonSticker.getInt("y"));
sticker.setImage(jsonSticker.getString("sticker"));returnsticker;
}
}
package com.zhen.websocket;
import javax.json.JsonObject;
import javax.json.JsonWriter;
import javax.json.spi.JsonProvider;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import java.io.IOException;
import java.io.Writer;/**
* @author zhen
* @Date 2018/12/6 10:59
* 这个类编码Sticker对象并且传递给WebSocket服务器通过写入流中*/
public class StickerEncoder implements Encoder.TextStream{
@Overridepublic voidinit(EndpointConfig config) {//TODO Auto-generated method stub
}
@Overridepublic voiddestroy() {//TODO Auto-generated method stub
}
@Overridepublic voidencode(Sticker sticker, Writer writer) throws EncodeException, IOException {
JsonProvider provider=JsonProvider.provider();
JsonObject jsonSticker=provider.createObjectBuilder()
.add("action", "add")
.add("x", sticker.getX())
.add("y", sticker.getY())
.add("sticker", sticker.getImage())
.build();
JsonWriter jsonWriter=provider.createWriter(writer);
jsonWriter.write(jsonSticker);
}
}
package com.zhen.websocket;/**
* @author zhen
* @Date 2018/12/6 10:29*/import java.io.*;
import java.util.*;
import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;//@ServerEndpoint 注解允许你声明一个WeoSocket,定义url映射,定义编码和解码
@ServerEndpoint(
value="/story/notifications",
encoders={StickerEncoder.class},
decoders={StickerDecoder.class}
)public classStoryWebSocket {private static final List stickers = Collections.synchronizedList(new LinkedList());private static final Set sessions = Collections.synchronizedSet(new HashSet());
@OnMessagepublic voidonMessage(Session session, Sticker sticker){//有消息从客户端发送过来,保存到列表中,然后通知所有的客户端
stickers.add(sticker);for(Session openSession : sessions){try{
openSession.getBasicRemote().sendObject(sticker);
}catch (IOException |EncodeException e) {
sessions.remove(openSession);
}
}
}
@OnOpenpublic voidonOpen(Session session) throws IOException, EncodeException{//有新的客户端连接时,保存此客户端的session,并且把当前所有的sticker发送给它
sessions.add(session);for(Sticker sticker : stickers){
session.getBasicRemote().sendObject(sticker);
}
}
@OnClosepublic voidonClose(Session session){//有客户端断开连接时 ,从session列表中移除此客户端的session
sessions.remove(session);
}
}var socket = null;
function initialize() {var canvas = document.getElementById("board");var ctx = canvas.getContext("2d");var img = document.getElementById("background_img");
ctx.drawImage(img,0, 0);
socket= new WebSocket("ws://localhost:8080/stickStory/story/notifications");
socket.onmessage=onSocketMessage;
}
function drag(ev) {var bounds =ev.target.getBoundingClientRect();var draggedSticker ={
sticker: ev.target.getAttribute("data-sticker"),
offsetX: ev.clientX-bounds.left,
offsetY: ev.clientY-bounds.top
};var draggedText =JSON.stringify(draggedSticker);
ev.dataTransfer.setData("text", draggedText);
}
function drop(ev) {
ev.preventDefault();var bounds = document.getElementById("board").getBoundingClientRect();var draggedText = ev.dataTransfer.getData("text");var draggedSticker =JSON.parse(draggedText);var stickerToSend ={
action:"add",
x: ev.clientX- draggedSticker.offsetX -bounds.left,
y: ev.clientY- draggedSticker.offsetY -bounds.top,
sticker: draggedSticker.sticker
};
socket.send(JSON.stringify(stickerToSend));
log("Sending Object" +JSON.stringify(stickerToSend));
}
function allowDrop(ev) {
ev.preventDefault();
}
function onSocketMessage(event) {if (event.data) {var receivedSticker = JSON.parse(event.data);
log("Received Object:" +JSON.stringify(receivedSticker));if (receivedSticker.action === "add") {var imageObj = newImage();
imageObj.οnlοad=function() {var canvas = document.getElementById("board");var context = canvas.getContext("2d");
context.drawImage(imageObj, receivedSticker.x, receivedSticker.y);
};
imageObj.src= "resources/stickers/" +receivedSticker.sticker;
}
}
}
function toggleLog() {var log = document.getElementById("logContainer");if (!log.getAttribute("style")) {
log.setAttribute("style", "display:block;");
}else{
log.setAttribute("style", "");
}
}var logCount = 0;
function log(logstr) {var logElement = document.getElementById("log");
logElement.innerHTML= "[" + logCount + "]:" + logstr + "
" +logElement.innerHTML;
logCount++;
}
window.οnlοad=initialize;<?xml version='1.0' encoding='UTF-8' ?>
Sticker StorySticker Story Book
Drag stickersfromthe left bar to the canvas.
Stickers






Canvas Not Supported.

Made with HTML5 + WebSockets and JSON
- Log
log
4.0.0
com.zhen
StickStory
1.0-SNAPSHOT
war
javax.servlet
javax.servlet-api
3.0.1
javax
javaee-api
7.0
provided
org.glassfish
javax.json
1.0.4
org.apache.maven.plugins
maven-compiler-plugin
3.5.1
1.7
1.7
UTF-8
org.apache.tomcat.maven
tomcat7-maven-plugin
2.2
8080
/stickStory
第一篇敲的项目代码
这里接受返回消息用到了转换器,接受对象类型json返回对象类型json
实现发布订阅模式
项目目录如下:

第二篇敲的项目代码:


package com.zhen.model;/**
* @author zhen
* @Date 2018/12/6 15:30*/
public classDevice {private intid;privateString name;privateString status;privateString type;privateString description;publicDevice() {
}public intgetId() {returnid;
}public void setId(intid) {this.id =id;
}publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}publicString getStatus() {returnstatus;
}public voidsetStatus(String status) {this.status =status;
}publicString getType() {returntype;
}public voidsetType(String type) {this.type =type;
}publicString getDescription() {returndescription;
}public voidsetDescription(String description) {this.description =description;
}
}
package com.zhen.websocket;
import com.zhen.model.Device;
import javax.enterprise.context.ApplicationScoped;
import javax.json.JsonObject;
import javax.json.spi.JsonProvider;
import javax.websocket.Session;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;/**
* @author zhen
* @Date 2018/12/6 15:50*/@ApplicationScopedpublic classDeviceSessionHandler {private final Set sessions = new HashSet<>();private final Set devices = new HashSet<>();private static AtomicInteger deviceId = new AtomicInteger(0);public voidaddSession(Session session) {
sessions.add(session);for(Device device : devices) {
JsonObject addMessage=createAndMessage(device);
sendToSession(session, addMessage);
}
}public voidremoveSession(Session session) {
sessions.remove(session);
}public voidaddDevice(Device device) {
device.setId(deviceId.incrementAndGet());
devices.add(device);
JsonObject addMessage=createAndMessage(device);
sendToAllConnectedSessions(addMessage);
}public void removeDevice(intid) {
Device device=getDeviceById(id);if (device != null) {
devices.remove(device);
JsonProvider provider=JsonProvider.provider();
JsonObject removeMessage=provider.createObjectBuilder()
.add("action", "remove")
.add("id", id)
.build();
sendToAllConnectedSessions(removeMessage);
}
}public void toggleDevice(intid) {
JsonProvider provider=JsonProvider.provider();
Device device=getDeviceById(id);if (device != null) {if ("On".equals(device.getStatus())) {
device.setStatus("Off");
}else{
device.setStatus("On");
}
JsonObject updateDevMessage=provider.createObjectBuilder()
.add("action", "toggle")
.add("id", device.getId())
.add("status", device.getStatus())
.build();
sendToAllConnectedSessions(updateDevMessage);
}
}public ListgetDevices(){return new ArrayList<>(devices);
}public Device getDeviceById(intid) {for(Device device : devices) {if (device.getId() ==id) {returndevice;
}
}return null;
}publicJsonObject createAndMessage(Device device) {
JsonProvider provider=JsonProvider.provider();
JsonObject addMessage=provider.createObjectBuilder()
.add("action", "add")
.add("name", device.getName())
.add("type", device.getType())
.add("status", device.getStatus())
.add("description", device.getDescription())
.build();returnaddMessage;
}private voidsendToAllConnectedSessions(JsonObject message) {for(Session session : sessions) {
sendToSession(session, message);
}
}private voidsendToSession(Session session, JsonObject message) {try{
session.getBasicRemote().sendText(message.toString());
}catch(IOException ex) {
sessions.remove(session);
Logger.getLogger(DeviceSessionHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
package com.zhen.websocket;
import com.zhen.model.Device;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.StringReader;
import java.util.logging.Level;
import java.util.logging.Logger;/**
* @author zhen
* @Date 2018/12/6 15:32*/@ApplicationScoped
@ServerEndpoint("/actions")public classDeviceWebSocketServer {
@Injectprivate DeviceSessionHandler sessionHandler = newDeviceSessionHandler();
@OnOpenpublic voidopen(Session session) {
sessionHandler.addSession(session);
}
@OnClosepublic voidclose(Session session) {
sessionHandler.removeSession(session);
}
@OnErrorpublic voidonError(Throwable error) {
Logger.getLogger(DeviceWebSocketServer.class.getName()).log(Level.SEVERE, null, error);
}
@OnMessagepublic voidhandleMessage(Session session, String message) {try(JsonReader reader = Json.createReader(newStringReader(message))){
JsonObject jsonMessage=reader.readObject();if ("add".equals(jsonMessage.getString("action"))) {
Device device= newDevice();
device.setName(jsonMessage.getString("name"));
device.setDescription(jsonMessage.getString("description"));
device.setType(jsonMessage.getString("type"));
device.setStatus("Off");
sessionHandler.addDevice(device);
}if ("remove".equals(jsonMessage.getString("action"))) {int id = (int) jsonMessage.getInt("id");
sessionHandler.removeDevice(id);
}if ("toggle".equals(jsonMessage.getString("action"))) {int id = (int) jsonMessage.getInt("id");
sessionHandler.toggleDevice(id);
}
}
}
}<?xml version='1.0' encoding='UTF-8' ?>
IndexJava WebSocket Home
Welcome to the Java WebSocket Home. Click the Add a device button to start adding devices.
Add a new device
Name:
Type:
Appliance
Electronics
Lights
Other
Description:
Currently connected devices:
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 80%;
background-color: #1f1f1f;
}
#wrapper {
width: 960px;
margin: auto;
text-align: left;
color: #d9d9d9;
}
p {
text-align: left;
}
.button {
display: inline;
color: #fff;
background-color: #f2791d;
padding: 8px;
margin: auto;
border-radius: 8px;-moz-border-radius: 8px;-webkit-border-radius: 8px;
box-shadow: none;
border: none;
}
.button:hover {
background-color: #ffb15e;
}
.button a, a:visited, a:hover, a:active {
color: #fff;
text-decoration: none;
}
#addDevice {
text-align: center;
width: 960px;
margin: auto;
margin-bottom: 10px;
}
#addDeviceForm {
text-align: left;
width: 400px;
margin: auto;
padding: 10px;
}
#addDeviceForm span {
display: block;
}
#content {
margin: auto;
width: 960px;
}
.device {
width: 180px;
height: 110px;
margin: 10px;
padding: 16px;
color: #fff;
vertical-align: top;
border-radius: 8px;-moz-border-radius: 8px;-webkit-border-radius: 8px;
display: inline-block;
}
.device.off {
background-color: #c8cccf;
}
.device span {
display: block;
}
.deviceName {
text-align: center;
font-weight: bold;
margin-bottom: 12px;
}
.removeDevice {
margin-top: 12px;
text-align: center;
}
.device.Appliance {
background-color: #5eb85e;
}
.device.Appliance a:hover {
color: #a1ed82;
}
.device.Electronics {
background-color: #0f90d1;
}
.device.Electronics a:hover {
color: #4badd1;
}
.device.Lights {
background-color: #c2a00c;
}
.device.Lights a:hover {
color: #fad232;
}
.device.Other {
background-color: #db524d;
}
.device.Other a:hover {
color: #ff907d;
}
.device a {
text-decoration: none;
}
.device a:visited, a:active, a:hover {
color: #fff;
}
.device a:hover {
text-decoration: underline;
}
window.οnlοad=init;var socket = new WebSocket("ws://localhost:8080/webSocketHome/actions");
socket.onmessage=onMessage;
function onMessage(event) {var device = JSON.parse(event.data);if (device.action === "add") {
printDeviceElement(device);
}if (device.action === "remove") {
document.getElementById(device.id).remove();//device.parentNode.removeChild(device);
}if (device.action === "toggle") {var node =document.getElementById(device.id);var statusText = node.children[2];if (device.status === "On") {
statusText.innerHTML= "Status:" + device.status + "(Turn off)";
}else if (device.status === "Off") {
statusText.innerHTML= "Status:" + device.status + "(Turn on)";
}
}
}
function addDevice(name, type, description) {var DeviceAction ={
action:"add",
name: name,
type: type,
description: description
};
socket.send(JSON.stringify(DeviceAction));
}
function removeDevice(element) {var id =element;var DeviceAction ={
action:"remove",
id: id
};
socket.send(JSON.stringify(DeviceAction));
}
function toggleDevice(element) {var id =element;var DeviceAction ={
action:"toggle",
id: id
};
socket.send(JSON.stringify(DeviceAction));
}
function printDeviceElement(device) {var content = document.getElementById("content");var deviceDiv = document.createElement("div");
deviceDiv.setAttribute("id", device.id);
deviceDiv.setAttribute("class", "device" +device.type);
content.appendChild(deviceDiv);var deviceName = document.createElement("span");
deviceName.setAttribute("class", "deviceName");
deviceName.innerHTML=device.name;
deviceDiv.appendChild(deviceName);var deviceType = document.createElement("span");
deviceType.innerHTML= "Type:" +device.type;
deviceDiv.appendChild(deviceType);var deviceStatus = document.createElement("span");if (device.status === "On") {
deviceStatus.innerHTML= "Status:" + device.status + "(Turn off)";
}else if (device.status === "Off") {
deviceStatus.innerHTML= "Status:" + device.status + "(Turn on)";//deviceDiv.setAttribute("class", "device off");
}
deviceDiv.appendChild(deviceStatus);var deviceDescription = document.createElement("span");
deviceDescription.innerHTML= "Comments:" +device.description;
deviceDiv.appendChild(deviceDescription);var removeDevice = document.createElement("span");
removeDevice.setAttribute("class", "removeDevice");
removeDevice.innerHTML= "Remove device";
deviceDiv.appendChild(removeDevice);
}
function showForm() {
document.getElementById("addDeviceForm").style.display = '';
}
function hideForm() {
document.getElementById("addDeviceForm").style.display = "none";
}
function formSubmit() {var form = document.getElementById("addDeviceForm");var name = form.elements["device_name"].value;var type = form.elements["device_type"].value;var description = form.elements["device_description"].value;
hideForm();
document.getElementById("addDeviceForm").reset();
addDevice(name, type, description);
}
function init() {
hideForm();
}<?xml version="1.0" encoding="UTF-8"?>
4.0.0
com.zhen
WebSocketHome
1.0-SNAPSHOT
war
javax.servlet
javax.servlet-api
3.0.1
javax
javaee-api
7.0
provided
org.glassfish
javax.json
1.0.4
org.apache.maven.plugins
maven-compiler-plugin
3.5.1
1.7
1.7
UTF-8
org.apache.tomcat.maven
tomcat7-maven-plugin
2.2
8080
/webSocketHome
第二篇敲的代码
此案例利用websocket实现了一套增删改查
项目中使用了CDI注解,如@ApplicationScope,@Inject进行注入功能
项目结构:

spring的websocket支持:
spring4提供了对websocket的支持
org.springframework
spring-websocket
${spring.version}
服务端配置:
package com.zhen.spring_websocket.config;
import com.zhen.spring_websocket.service.MyHandler;
import com.zhen.spring_websocket.service.MyHandler1;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;/**
* @author zhen
* @Date 2018/12/10 18:34*/@Configuration
@EnableWebSocketpublic classWebSocketConfig implements WebSocketConfigurer {
@Overridepublic voidregisterWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(),"/myHandler");
registry.addHandler(myHandler1(),"/myHandler1").withSockJS();
}publicWebSocketHandler myHandler() {return newMyHandler();
}publicWebSocketHandler myHandler1() {return newMyHandler1();
}
}
package com.zhen.spring_websocket.service;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;/**
* @author zhen
* @Date 2018/12/10 18:32
* 此类实现WebSocketHandler,执行处理请求等操作,这里只是接受请求然后再将请求转发回去的功能*/
public classMyHandler1 extends AbstractWebSocketHandler {
@Overrideprotected voidhandleTextMessage(WebSocketSession session, TextMessage message) {try{
session.sendMessage(message);
}catch(IOException e){
e.printStackTrace();
}
}
@Overridepublic voidafterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
}
@Overridepublic voidafterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
}
}
Handler并不一定是继承abstractWebSocketHandler,只要是WebSocketHandler的子类即可。
和servlet的websocket api差不多吧
这句代码:
registry.addHandler(myHandler1(),"/myHandler1").withSockJS();
是表示接受的是前端sockJs对象发送的请求。是spring-websocket模块的一个封装功能。
sockJs是什么呢?
在不支持WebSocket的情况下,也可以很简单地实现WebSocket的功能的,方法就是使用 SockJS。
它会优先选择WebSocket进行连接,但是当服务器或客户端不支持WebSocket时,会自动在 XHR流、XDR流、iFrame事件源、iFrame HTML文件、XHR轮询、XDR轮询、iFrame XHR轮询、JSONP轮询 这几个方案中择优进行连接。
它是websocket客户端的拓展与补充。
使用sockJs之后,spring注册websocket链接就上面代码这样既可。
客户端:
引入sockjs.min.js
使用sockJs对象替代WebSocket对象发送请求,它的语法和原生几乎一致
WebSocket with SockJSWelcome!
//SockJS与原生的WebSocket的方法基本是一致的,//所以只需要将 new WebSocket(url); 换成 new SockJS(url); 就可以了
var url = "/spring4webSocket/myHandler1";var sock = newSockJS(url);
sock.onopen=function (ev) {
console.log("opening");
sayHey();
};
sock.onmessage=function (ev) {
console.log(ev.data);var li = document.createElement("li");
li.innerText=ev.data;
document.getElementById("ul").appendChild(li);
setTimeout(sayHey,2000);
};
sock.onclose=function (ev) {
console.log("closed");
};
function sayHey() {
console.log("sending 'Hey guy!'");
sock.send("Hey guy!");
};
spring的封装还有基于stomp的部分:
这个没理解好,以后再做研究。与spring配置请求的也涉及到再做补充。虽然写过demo,但是不太理解,用的springbot,使用了spring security的例子,暂过。
此学习得出的经验之谈:如果暂时理解不了,就先使用他,然后再理解