12.1.1.1. websocke-demo

后端的构建就不说了。直接开始说相关的。具体的项目我会放在git上。

在仓库中找到 spring-websocket

在依赖项中可以看到依赖的具体包。这里直接引入这一个即可

compile "org.springframework:spring-websocket:${springframeworkVersion}"

接下来就开始写了。

12.1.1.1.1. 创建并配置WebSocketHandler

WebSocketHandler的作用是,当有消息进入的时候,你要怎么处理?

spring提供给我们两种可用接口 TextWebSocketHandler或BinaryWebSocketHandler:

public class MyHandler extends TextWebSocketHandler {
    private Logger log = LoggerFactory.getLogger(getClass());

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        log.info("收到消息:sessionId={},msg={}", session.getId(), message);
    }
}

上面的处理器有专门的配置映射到一个url上。

@Configuration
@EnableWebSocket  // 开启websocket支持
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // 添加处理器
        registry.addHandler(myHandler(), "/myHandler");
    }

    @Bean
    public WebSocketHandler myHandler() {
        return new MyHandler();
    }
}

启动项目,访问http://localhost:8080/myHandler,如果没有问题的话,你会看到Can "Upgrade" only to "WebSocket".字样的输出。

这个是什么意思呢?前面说到过

HTTP仅用于初始握手,这取决于内置于HTTP中的机制来请求协议升级(或在这种情况下为协议交换机),服务器可以使用HTTP状态101对其进行响应(切换协议)如果它同意。假设握手成功,HTTP升级请求下面的TCP套接字保持打开,客户端和服务器都可以使用它来彼此发送消息。

那么这个就是说只能用于websocket协议来访问。

如果说你想问这个链接为404,那么请检查spring-mvc的配置,和是否把WebSocketConfig所在的路径扫描了,如

<context:component-scan base-package="cn.mrcode.javawebsocketdemo.websocket"/>

官方说到 :Spring的WebSocket支持不依赖于Spring MVC。WebSocketHandler 在WebSocketHttpRequestHandler的帮助下将其集成到其他HTTP服务环境中 相对比较简单。在这里我明白去研究其他的方式。

12.1.1.1.2. 前端链接服务

前端使用的vue-cli开发,不懂的同学可能要费点事,但是不要慌。这里只记录重要的代码和思路。同样也会放在git上。

<template>
  <div class="hello">
    状态:
  </div>
</template>

<script>
  export default {
    data () {
      return {
        msg: 'Welcome to Your Vue.js App'
      }
    },
    mounted () {
      let ws = new WebSocket('ws://localhost:8080/myHandler')
      ws.onopen = () => {
        this.msg = '链接成功.'
        // Web Socket 已连接上,使用 send() 方法发送数据
        ws.send('发送数据')
      }

      ws.onmessage = (evt) => {
        let receivedMsg = evt.data
        this.msg = '数据已接收...:' + receivedMsg
      }

      ws.onclose = () => {
        // 关闭 websocket
        this.msg = '连接已关闭...'
      }
    }
  }
</script>

上面的代码,在mounted函数中(看成一个钩子函数,进入该页面后,该函数会被执行),里面的代码是标准的 w3c-html5-websocket 内容。只不过被转成了es6的语法。

这里代码展示的效果是,进入该页面的时候,会去链接到我们后端暴露的websocket服务,(要记得找一个高版本的浏览器,否则可能不支持websocket)。分别在他的链接成功/关闭/收到消息事件回调函数中,在页面显示不同的字符串。

好了问题来了,按照现在的代码,肯定是链接不上的。页面一闪而过然后显示状态:连接已关闭...; 这个时候你按F12打开浏览器控制台就会发现报错了。

WebSocket connection to 'ws://localhost:8080/myHandler' failed: Error during WebSocket handshake: Unexpected response code: 403
mounted @ HelloWorld.vue?8664:16
...

403 错误,一般来说链接上了服务器,但是拒绝服务;这里的问题就是前后分离的方式开发的,两个项目的域名和端口不一样,导致跨域了。

12.1.1.1.3. 配置允许链接的源

从Spring Framework 4.1.5开始,WebSocket和SockJS的默认行为是仅接受相同的源请求。也可以允许所有或指定的起始列表。此检查主要是为浏览器客户端设计的。

3种可能的行为是:

  • 只允许相同的原始请求(默认):在此模式下,当启用SockJS时,将Iframe HTTP响应头X-Frame-Options设置为SAMEORIGIN,并禁用JSONP传输,因为它不允许检查请求的来源。因此,当启用此模式时,不支持IE6和IE7。
  • 允许指定的起始列表:每个提供的允许的起始必须以http://https://开始。在这种模式下,当启用SockJS时,基于IFrame和JSONP的传输都被禁用。因此,启用此模式时,不支持IE6至IE9。

  • 允许所有来源:启用此模式,您应该提供*作为允许的原始值。在这种模式下,所有的运输都可用。

所以这里我们在后端配置中配置允许的源 cn.mrcode.javawebsocketdemo.websocket.ws.WebSocketConfig

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // 添加处理器
        registry.addHandler(myHandler(), "/myHandler")
                .setAllowedOrigins("*")
        ;
    }

.setAllowedOrigins("*"):这里配置成允许所有的host访问。当然你也可以配置只允许http://mydomain.com的请求链接

配置完成后,重启后端服务重新刷新页面,就能看到已经链接成功了。

后台则会打印日志

cn.mrcode.javawebsocketdemo.websocket.ws.MyHandler handleTextMessage 19    - 收到消息:sessionId=0,msg=TextMessage payload=[发送数据], byteCount=12, last=true]

这个是由于在前端onopen链接成功后还发送了一个字符串到后端ws.send('发送数据').

那么问题来了,后端怎么发送消息给前端呢?

12.1.1.1.4. 后端发送消息

后端在接收到消息之后,还想给前端发送一个交互消息。这里就很简单了。

cn.mrcode.javawebsocketdemo.websocket.ws.MyHandler 中我们覆盖了一个接收消息的方法。

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        log.info("收到消息:sessionId={},msg={}", session.getId(), message);
    }

WebSocketSession session : 注意这个对象,顾名思义是一个session对象,这个session表示的意思是,一个链接对应一个session。前端中 new WebSocket 则是一个不同的session。

所以这里的session和mvc中的session不是同一个概念。那么信息的发送也是通过这个session。

 session.sendMessage(new TextMessage("Hello Word"));

再次刷新页面,则会看到状态:数据已接收...:Hello Word的输出。

12.1.1.1.5. 其他概念

  1. WebSocketSession 有几个?

在spring中通过测试,addHandler对应的地址相同的在一个session周期中都是同一个sessionId.

© All Rights Reserved            updated 2017-11-07 17:38:10

results matching ""

    No results matching ""