java websocket学习

java

引言:

  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 对象:

事件事件处理程序描述
openSocket.onopen连接建立时触发
messageSocket.onmessage客户端接收服务端数据时触发
errorSocket.onerror通信发生错误时触发
closeSocket.onclose连接关闭时触发

  WebSocket 方法

  以下是 WebSocket 对象的相关方法。假定我们使用了以上代码创建了 Socket 对象:

方法描述
Socket.send()使用连接发送数据
Socket.close()关闭连接

    客户端实例:   

<%--

Created by IntelliJ IDEA.

User: zhen

Date: 2018/12/10

Time: 17:36

To change this template use File | Settings | File Templates.

--%>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>

<head>

<title>welcome page</title>

</head>

<body>

welcome to ssm all annotation project!

<input id="text" type="text"/>

<button onclick="send()">发送消息</button>

<hr/>

<button onclick="closeWebSocket()">关闭webSocket连接</button>

<hr/>

<div id="message"></div>

</body>

<script type="text/javascript">

var webSocket = null;

//判断当前浏览器是否支持webSocket

if (\'WebSocket\' in window) {

webSocket = new WebSocket("ws://localhost:8080/spring4webSocket/myHandler")

} else {

alert("当前浏览器 Not support webSocket");

}

webSocket.onerror = 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 + "<br/>" + message;

}

//监听串口关闭事件,当窗口关闭时,主动去关闭webSocket连接,防止还没断开就关闭窗口,srever端会抛异常

window.onbeforeunload = function (ev) {

closeWebSocket();

}

function closeWebSocket(){

webSocket.close();

}

//发送消息

function send() {

var message = document.getElementById("text").value;

webSocket.send(message);

}

</script>

</html>

客户端实例

 

服务端(java):

  基于servlet api:

    1、导入有关jar包     

        <dependency>

<groupId>javax</groupId>

<artifactId>javaee-api</artifactId>

<version>7.0</version>

<scope>provided</scope>

</dependency>

  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 class StoryWebSocket {

private static final List<Sticker> stickers = Collections.synchronizedList(new LinkedList<Sticker>());

private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());

@OnMessage

public void onMessage(Session session, Sticker sticker){

// 有消息从客户端发送过来,保存到列表中,然后通知所有的客户端

stickers.add(sticker);

for(Session openSession : sessions){

try {

openSession.getBasicRemote().sendObject(sticker);

} catch (IOException | EncodeException e) {

sessions.remove(openSession);

}

}

}

@OnOpen

public void onOpen(Session session) throws IOException, EncodeException{

// 有新的客户端连接时,保存此客户端的session,并且把当前所有的sticker发送给它

sessions.add(session);

for(Sticker sticker : stickers){

session.getBasicRemote().sendObject(sticker);

}

}

@OnClose

public void onClose(Session session){

// 有客户端断开连接时 ,从session列表中移除此客户端的session

sessions.remove(session);

}

}

服务端实例

看的有些混乱不能很好理解的时候就敲一些例子,功能出来就更容易理解了。

  

跟着下面两篇教程的案例

  • Using WebSocket for Real-Time Communication in Java Platform, Enterpise Edition 7
  • Java EE 7: Building Web Applications with WebSocket, JavaScript and HTML5

package com.zhen.websocket;

/**

* @author zhen

* @Date 2018/12/6 10:28

*/

public class Sticker {

private int x;

private int y;

private String image;

public Sticker() {

}

public int getX() {

return x;

}

public void setX(int x) {

this.x = x;

}

public int getY() {

return y;

}

public void setY(int y) {

this.y = y;

}

public String getImage() {

return image;

}

public void setImage(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<Sticker>{

// Do not create a JsonReader object. To create readers and writes, use the

// JsonProvider class.

@Override

public void init(EndpointConfig config) {

// TODO Auto-generated method stub

}

@Override

public void destroy() {

// TODO Auto-generated method stub

}

@Override

public Sticker decode(Reader reader) throws DecodeException, IOException {

JsonProvider provider = JsonProvider.provider();

JsonReader jsonReader = provider.createReader(reader);

JsonObject jsonSticker = jsonReader.readObject();

Sticker sticker = new Sticker();

sticker.setX(jsonSticker.getInt("x"));

sticker.setY(jsonSticker.getInt("y"));

sticker.setImage(jsonSticker.getString("sticker"));

return sticker;

}

}

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<Sticker> {

@Override

public void init(EndpointConfig config) {

// TODO Auto-generated method stub

}

@Override

public void destroy() {

// TODO Auto-generated method stub

}

@Override

public void encode(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 class StoryWebSocket {

private static final List<Sticker> stickers = Collections.synchronizedList(new LinkedList<Sticker>());

private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());

@OnMessage

public void onMessage(Session session, Sticker sticker){

// 有消息从客户端发送过来,保存到列表中,然后通知所有的客户端

stickers.add(sticker);

for(Session openSession : sessions){

try {

openSession.getBasicRemote().sendObject(sticker);

} catch (IOException | EncodeException e) {

sessions.remove(openSession);

}

}

}

@OnOpen

public void onOpen(Session session) throws IOException, EncodeException{

// 有新的客户端连接时,保存此客户端的session,并且把当前所有的sticker发送给它

sessions.add(session);

for(Sticker sticker : stickers){

session.getBasicRemote().sendObject(sticker);

}

}

@OnClose

public void onClose(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 = new Image();

imageObj.onload = 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 = "<b>[" + logCount + "]: </b>" + logstr + "<br>" + logElement.innerHTML;

logCount++;

}

window.onload = initialize;

<?xml version=\'1.0\' encoding=\'UTF-8\' ?>

<!DOCTYPE html>

<html>

<head>

<title>Sticker Story</title>

<link href="resources/styles.css" rel="stylesheet" type="text/css" >

<script src="resources/story-page.js" type="text/javascript"></script>

</head>

<body>

<header>

<h1>Sticker Story Book</h1>

</header>

<nav>

Drag stickers from the left bar to the canvas.

</nav>

<aside>

<h2>Stickers</h2>

<div id="stickerContainer">

<img src="resources/stickers/bear.png"

data-sticker="bear.png"

style="float:left"

draggable="true" ondragstart="drag(event);" >

<img src="resources/stickers/chicken.png"

data-sticker="chicken.png"

style="float:left"

draggable="true" ondragstart="drag(event);" >

<img src="resources/stickers/leopard.png"

data-sticker="leopard.png"

style="float:left"

draggable="true" ondragstart="drag(event);" >

<img src="resources/stickers/monkey.png"

data-sticker="monkey.png"

style="float:left"

draggable="true" ondragstart="drag(event);" >

<img src="resources/stickers/horse.png"

data-sticker="horse.png"

style="float:left"

draggable="true" ondragstart="drag(event);" >

<img src="resources/stickers/tiger.png"

data-sticker="tiger.png"

style="float:left"

draggable="true" ondragstart="drag(event);" >

</div>

</aside>

<div id="content">

<canvas id="board" width="1000" height="580" ondrop="drop(event);"

ondragover="allowDrop(event);">

Canvas Not Supported.

</canvas>

<img src="resources/canvas2.png" id="background_img"

width="1000" height="580" style="display:none;"/>

</div>

<footer>

<small>Made with HTML5 + WebSockets and JSON</small>

<ol>

<li onclick="toggleLog();">Log</li>

</ol>

</footer>

<div id="logContainer">

<h2>log</h2>

<div id="log"></div>

</div>

</body>

</html>

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.zhen</groupId>

<artifactId>StickStory</artifactId>

<version>1.0-SNAPSHOT</version>

<packaging>war</packaging>

<dependencies>

<!-- Servlet -->

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>javax.servlet-api</artifactId>

<version>3.0.1</version>

</dependency>

<dependency>

<groupId>javax</groupId>

<artifactId>javaee-api</artifactId>

<version>7.0</version>

<scope>provided</scope>

</dependency>

<!-- JSON工具包 -->

<dependency>

<groupId>org.glassfish</groupId>

<artifactId>javax.json</artifactId>

<version>1.0.4</version>

</dependency>

</dependencies>

<build>

<plugins>

<!-- 编译插件 -->

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>3.5.1</version>

<configuration>

<source>1.7</source>

<target>1.7</target>

<encoding>UTF-8</encoding>

</configuration>

</plugin>

<!-- tomcat 插件 -->

<plugin>

<groupId>org.apache.tomcat.maven</groupId>

<artifactId>tomcat7-maven-plugin</artifactId>

<version>2.2</version>

<configuration>

<port>8080</port>

<path>/stickStory</path>

</configuration>

</plugin>

</plugins>

</build>

</project>

第一篇敲的项目代码

这里接受返回消息用到了转换器,接受对象类型json返回对象类型json

实现发布订阅模式

项目目录如下:

第二篇敲的项目代码:

package com.zhen.model;

/**

* @author zhen

* @Date 2018/12/6 15:30

*/

public class Device {

private int id;

private String name;

private String status;

private String type;

private String description;

public Device() {

}

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getStatus() {

return status;

}

public void setStatus(String status) {

this.status = status;

}

public String getType() {

return type;

}

public void setType(String type) {

this.type = type;

}

public String getDescription() {

return description;

}

public void setDescription(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

*/

@ApplicationScoped

public class DeviceSessionHandler {

private final Set<Session> sessions = new HashSet<>();

private final Set<Device> devices = new HashSet<>();

private static AtomicInteger deviceId = new AtomicInteger(0);

public void addSession(Session session) {

sessions.add(session);

for (Device device : devices) {

JsonObject addMessage = createAndMessage(device);

sendToSession(session, addMessage);

}

}

public void removeSession(Session session) {

sessions.remove(session);

}

public void addDevice(Device device) {

device.setId(deviceId.incrementAndGet());

devices.add(device);

JsonObject addMessage = createAndMessage(device);

sendToAllConnectedSessions(addMessage);

}

public void removeDevice(int id) {

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(int id) {

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 List<Device> getDevices(){

return new ArrayList<>(devices);

}

public Device getDeviceById(int id) {

for (Device device : devices) {

if (device.getId() == id) {

return device;

}

}

return null;

}

public JsonObject 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();

return addMessage;

}

private void sendToAllConnectedSessions(JsonObject message) {

for (Session session : sessions) {

sendToSession(session, message);

}

}

private void sendToSession(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 class DeviceWebSocketServer {

@Inject

private DeviceSessionHandler sessionHandler = new DeviceSessionHandler();

@OnOpen

public void open(Session session) {

sessionHandler.addSession(session);

}

@OnClose

public void close(Session session) {

sessionHandler.removeSession(session);

}

@OnError

public void onError(Throwable error) {

Logger.getLogger(DeviceWebSocketServer.class.getName()).log(Level.SEVERE, null, error);

}

@OnMessage

public void handleMessage(Session session, String message) {

try(JsonReader reader = Json.createReader(new StringReader(message))){

JsonObject jsonMessage = reader.readObject();

if ("add".equals(jsonMessage.getString("action"))) {

Device device = new Device();

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\' ?>

<!DOCTYPE html>

<html>

<head>

<title>Index</title>

<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">

<link rel="stylesheet" href="style.css">

<script src="websocket.js"></script>

</head>

<body>

<div id="wrapper">

<h1>Java WebSocket Home</h1>

<p>Welcome to the Java WebSocket Home. Click the Add a device button to start adding devices.</p>

<br/>

<div id="addDevice">

<div class="button"><a href="#" onclick="showForm()">Add a device</a> </div>

<form id="addDeviceForm">

<h3>Add a new device</h3>

<span>Name: <input type="text" name="device_name" id="device_name"></span>

<span>

Type:

<select id="device_type">

<option name="type" value="Appliance">Appliance</option>

<option name="type" value="Electronics">Electronics</option>

<option name="type" value="Lights">Lights</option>

<option name="type" value="Other">Other</option>

</select>

</span>

<span>

Description:<br/>

<textarea name="description" id="device_description" rows="2" cols="50"></textarea>

</span>

<input type="button" class="button" value="Add" onclick="formSubmit();">

<input type="reset" class="button" value="Cancel" onclick="hideForm();">

</form>

</div>

<br/>

<h3>Currently connected devices:</h3>

<div id="content"></div>

</div>

</body>

</html>

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.onload = 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 + " (<a href=\"#\" OnClick=toggleDevice(" + device.id + ")>Turn off</a>)";

} else if (device.status === "Off") {

statusText.innerHTML = "Status: " + device.status + " (<a href=\"#\" OnClick=toggleDevice(" + device.id + ")>Turn on</a>)";

}

}

}

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 = "<b>Type:</b> " + device.type;

deviceDiv.appendChild(deviceType);

var deviceStatus = document.createElement("span");

if (device.status === "On") {

deviceStatus.innerHTML = "<b>Status:</b> " + device.status + " (<a href=\"#\" OnClick=toggleDevice(" + device.id + ")>Turn off</a>)";

} else if (device.status === "Off") {

deviceStatus.innerHTML = "<b>Status:</b> " + device.status + " (<a href=\"#\" OnClick=toggleDevice(" + device.id + ")>Turn on</a>)";

//deviceDiv.setAttribute("class", "device off");

}

deviceDiv.appendChild(deviceStatus);

var deviceDescription = document.createElement("span");

deviceDescription.innerHTML = "<b>Comments:</b> " + device.description;

deviceDiv.appendChild(deviceDescription);

var removeDevice = document.createElement("span");

removeDevice.setAttribute("class", "removeDevice");

removeDevice.innerHTML = "<a href=\"#\" OnClick=removeDevice(" + device.id + ")>Remove device</a>";

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"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.zhen</groupId>

<artifactId>WebSocketHome</artifactId>

<version>1.0-SNAPSHOT</version>

<packaging>war</packaging>

<dependencies>

<!-- Servlet -->

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>javax.servlet-api</artifactId>

<version>3.0.1</version>

</dependency>

<dependency>

<groupId>javax</groupId>

<artifactId>javaee-api</artifactId>

<version>7.0</version>

<scope>provided</scope>

</dependency>

<!-- JSON工具包 -->

<dependency>

<groupId>org.glassfish</groupId>

<artifactId>javax.json</artifactId>

<version>1.0.4</version>

</dependency>

</dependencies>

<build>

<plugins>

<!-- 编译插件 -->

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>3.5.1</version>

<configuration>

<source>1.7</source>

<target>1.7</target>

<encoding>UTF-8</encoding>

</configuration>

</plugin>

<!-- tomcat 插件 -->

<plugin>

<groupId>org.apache.tomcat.maven</groupId>

<artifactId>tomcat7-maven-plugin</artifactId>

<version>2.2</version>

<configuration>

<port>8080</port>

<path>/webSocketHome</path>

</configuration>

</plugin>

</plugins>

</build>

</project>

第二篇敲的代码

此案例利用websocket实现了一套增删改查

项目中使用了CDI注解,如@ApplicationScope,@Inject进行注入功能

项目结构:

 

 spring的websocket支持:

  spring4提供了对websocket的支持

<!-- spring-webSocket,不使用javaee7的api里面了,使用spring的封装 -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-websocket</artifactId>

<version>${spring.version}</version>

</dependency>

服务端配置:

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

@EnableWebSocket

public class WebSocketConfig implements WebSocketConfigurer {

@Override

public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {

registry.addHandler(myHandler(),"/myHandler");

registry.addHandler(myHandler1(),"/myHandler1").withSockJS();

}

public WebSocketHandler myHandler() {

return new MyHandler();

}

public WebSocketHandler myHandler1() {

return new MyHandler1();

}

}

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 class MyHandler1 extends AbstractWebSocketHandler {

@Override

protected void handleTextMessage(WebSocketSession session, TextMessage message) {

try{

session.sendMessage(message);

}catch (IOException e){

e.printStackTrace();

}

}

@Override

public void afterConnectionEstablished(WebSocketSession session) throws Exception {

super.afterConnectionEstablished(session);

}

@Override

public void afterConnectionClosed(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对象发送请求,它的语法和原生几乎一致

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%@ page session="false" %>

<html>

<head>

<title>WebSocket with SockJS</title>

</head>

<body>

<h1>Welcome!</h1>

<ul id="ul">

</ul>

<script src="${pageContext.request.contextPath}/static/js/sockjs.min.js"></script>

<script>

// SockJS与原生的WebSocket的方法基本是一致的,

// 所以只需要将 new WebSocket(url); 换成 new SockJS(url); 就可以了

var url = "/spring4webSocket/myHandler1";

var sock = new SockJS(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!");

};

</script>

</body>

</html>

 

spring的封装还有基于stomp的部分:

  这个没理解好,以后再做研究。与spring配置请求的也涉及到再做补充。虽然写过demo,但是不太理解,用的springbot,使用了spring security的例子,暂过。

 

参考链接:https://www.jianshu.com/p/942a2b16e26c

参考链接:https://baike.baidu.com/item/WebSocket/1953845?fr=aladdin

参考链接:https://www.cnblogs.com/jingmoxukong/p/7755643.html

参考链接:http://www.ruanyifeng.com/blog/2017/05/websocket.html

参考链接:http://www.cnblogs.com/xdp-gacl/p/5193279.html

参考链接:https://blog.csdn.net/john_62/article/details/78208177

参考链接:https://blog.csdn.net/dadiyang/article/details/83715569

 

此学习得出的经验之谈:如果暂时理解不了,就先使用他,然后再理解  

 

以上是 java websocket学习 的全部内容, 来源链接: utcz.com/z/390683.html

回到顶部