日志系统websocket服务

websocket介绍

WebSocket协议是一种双向通信协议,它建立在TCP之上,同http一样通过TCP来传输数据,但是它和http最大的不同有两点:1.WebSocket是一种双向通信协议,在建立连接后,WebSocket服务器和客户端都能主动的向对方发送或接收数据,就像Socket一样,不同的是WebSocket是一种建立在Web基础上的一种简单模拟Socket的协议;2.WebSocket需要通过握手连接,类似于TCP它也需要客户端和服务器端进行握手连接,连接成功后才能相互通信。

websocket的springboot实现

创建一个maven工程,pom中加入依赖

如果是运行在tomcat下,主要引入javaee-api,不过使用springboot的内置tomcat就不需要了

<dependencies>    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
        <version>1.3.5.RELEASE</version>
    </dependency>
</dependencies>

配置端口和静态资源

创建application.properties文件,添加server端口和静态资源路径

server.port=8080
spring.resources.static-locations=classpath:/webapp/html/

websocket服务端的实现

首先是springboot的入口

package com.web;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;

@EnableAutoConfiguration
@ComponentScan
public class Application
{


  public static void main(String[] args)
  {
    SpringApplication.run(Application.class, args);
  }
}

注入ServerEndpointExporter的bean

package com.web;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}

websocket的具体实现

package com.web;

import java.io.IOException;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.springframework.stereotype.Component;

@ServerEndpoint("/websocket")
@Component
public class MyWebSocket {

    @OnMessage
    public void onMessage(String message, Session session) 
        throws IOException, InterruptedException {

        // Print the client message for testing purposes
        System.out.println("Received: " + message);

        // Send the first message to the client
        session.getBasicRemote().sendText("This is the first server message");

        // Send 3 messages to the client every 5 seconds
        int sentMessages = 0;
        while(sentMessages < 3){
            Thread.sleep(5000);
            session.getBasicRemote().
                sendText("This is an intermediate server message. Count: " 
                    + sentMessages);
            sentMessages++;
        }

        // Send a final message to the client
        session.getBasicRemote().sendText("This is the last server message");
    }

    @OnOpen
    public void onOpen () {
        System.out.println("Client connected");
    }

    @OnClose
    public void onClose () {
        System.out.println("Connection closed");
    }
}

一个简单的demo,websocket一共有四种事件

  • onopen websocket建立连接完成
  • onclose websocket连接关闭
  • onmessage websocket接收到数据,发送数据调用socket.send方法
  • onerror websocket发生错误

websocket的客户端的实现

websocket两种实现方法,一种是sockjs,一种是HTML5,这里使用HTML5实现,创建一个index.html

<!DOCTYPE HTML>
<html>
<head>
    <title>My WebSocket</title>
</head>

<body>
Welcome<br/>
<input id="text" type="text" /><button onclick="send()">Send</button>    <button onclick="closeWebSocket()">Close</button>
<div id="message">
</div>
</body>

<script type="text/javascript">
    var websocket = null;

    //判断当前浏览器是否支持WebSocket
    if('WebSocket' in window){
        websocket = new WebSocket("ws://localhost:8080/websocket");
    }
    else{
        alert('Not support websocket')
    }

    //连接发生错误的回调方法
    websocket.onerror = function(){
        setMessageInnerHTML("error");
    };

    //连接成功建立的回调方法
    websocket.onopen = function(event){
        setMessageInnerHTML("open");
    }

    //接收到消息的回调方法
    websocket.onmessage = function(event){
        setMessageInnerHTML(event.data);
    }

    //连接关闭的回调方法
    websocket.onclose = function(){
        setMessageInnerHTML("close");
    }

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function(){
        websocket.close();
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML){
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

    //关闭连接
    function closeWebSocket(){
        websocket.close();
    }

    //发送消息
    function send(){
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
</script>
</html>

运行Application.java,页面访问http://127.0.0.1:8080/index.html,可以通过客户端发送消息,也可以接受服务端返回的消息

这样基本实现了简单的websocket通讯,接下来websocket的客户端接入echars的接口,实现日志系统的前端展示

websokcet安全模式

websocket的安全模式需要创建wss实例,wss是基于https握手协议的,我们的cf平台统一配置了https协议,所以不用再配置ssl,直接创建wss实例即可

if(window.location.protocol == 'http:'){
         websocket = new WebSocket("ws://localhost:8080/websocket");
}else{
         websocket = new WebSocket("wss://localhost:8080/websocket");
}

websocket的token验证

websocket的数据传输安全模式通过wss,客户端和服务端握手的时候采用https协议,这时的安全需要token验证

websocket = new WebSocket(“wss://localhost:8080/websocket?token=*“)
用户登入平台的时候获取token,服务端拿到token去平台验证

 private static void tokenAuth(String token) {
 //发送 GET 请求
 String s=HttpPost.sendGet("http://&&&&&", "token");
 }


 public static String sendGet(String url, String param) {
    String result = "";
    BufferedReader in = null;
    try {
        String urlNameString = url + "?" + param;
        URL realUrl = new URL(urlNameString);
        // 打开和URL之间的连接
        URLConnection connection = realUrl.openConnection();
        // 设置通用的请求属性
        connection.setRequestProperty("accept", "*/*");
        connection.setRequestProperty("connection", "Keep-Alive");
        connection.setRequestProperty("user-agent",
                "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
        // 建立实际的连接
        connection.connect();
        // 获取所有响应头字段
        Map<String, List<String>> map = connection.getHeaderFields();
        // 遍历所有的响应头字段
        for (String key : map.keySet()) {
            System.out.println(key + "--->" + map.get(key));
        }
        // 定义 BufferedReader输入流来读取URL的响应
        in = new BufferedReader(new InputStreamReader(
                connection.getInputStream()));
        String line;
        while ((line = in.readLine()) != null) {
            result += line;
        }
    } catch (Exception e) {
        System.out.println("发送GET请求出现异常!" + e);
        e.printStackTrace();
    }
    // 使用finally块来关闭输入流
    finally {
        try {
            if (in != null) {
                in.close();
            }
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }
    return result;
}