springboot配置使用websocket

摘要
Websocket 其实是一个新协议,跟 HTTP 协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是 HTTP 协议上的一种补充。
WebSocket 是一种网络通信协议,是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。RFC6455 定义了它的通信标准。
传统的HTTP通讯中,由于http协议是一种无状态的,无连接的单向通讯协议,只有客户端才能发起请求,服务端接收到请求后作出响应。以往的网站中,如果要做即时通信聊天窗口的话,就需要通过js频繁的刷新请求服务器获取最新的信息达到效果,轮询需要不停的建立和断开http连接,或者长期保持http连接。这样效率非常低,且浪费资源。故而需要像websocket这样一种双向通行的协议出现。websocket跟socket原理差不多,只需要建立一次连接,就可以一直保持连接状态,而且可以双向通讯。
在这里插入图片描述
WebSocket的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话
1、客户端,使用js,以下语法可以创建一个WebSocket对象

var Socket = new WebSocket(url, [protocol] );

2、服务端支持Node.js、Java、C++、Python 等多种语言,每个语言都有自己的解决方案。本文主要介绍springboot中配置WebSocket,即基于Java语言的

上干货,上干货,上干货,上干货:

1、客户端(html页面)

<!DOCTYPE HTML>
<html>
<head>
    <title>WebSocket test</title>
    <meta charset="UTF-8"/>
</head>

<body>
Welcome<br/>
当前连接状态:
<span id="showFirst">
    关闭
</span>
<br/>
<input id="text" type="text"/>
<button id="sendClick" onclick="send()">发送</button>
<button id="switchClick" onclick="closeWebSocket()">关闭连接</button>
<div id="message">
    打印信息:
</div>
</body>

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

    //判断当前浏览器是否支持WebSocket
    if ('WebSocket' in window) {
       //注意使用的是 ws://,而不是 http://
        websocket = new WebSocket("ws://127.0.0.1:8085/websocket/manager");
    }
    else {
        alert('浏览器不支持 websocket')
    }

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

    //连接成功建立的回调方法
    websocket.onopen = function (event) {
        //设置按钮显示为   关闭
        setStatusHTML("打开");
    }

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

    /**
     * 接收到服务端发过来的消息,
     * 回调方法
     * @param event
     */
    websocket.onmessage = function (event) {
        //将服务端的消息打印到屏幕
        setMessageInnerHTML(event.data);
    }

    /**
     * 给服务端发送消息
     *
     */
    function send() {
        var message = document.getElementById('text').value;
        websocket.send(message);
    }

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

    //修改开关按钮的显示
    function setStatusHTML(text) {
        document.getElementById('showFirst').innerHTML = text;
    }

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

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

</script>
</html>

2、服务端(springboot)
1)pom.xml依赖配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2)application.yml的配置(正常配置就行)

server:
  port: 8085

spring:
  profiles:
    active: dev #开发环境
  application:
      name: service-feign-push

3)新建一个websocket配置类

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

/**
 * @author zhaoxi
 * @date 2018/12/4 14:49
 * TODO:websocket配置类
 */
@Configuration
public class WebSocketConfig {

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

}

4)建一个服务端处理WebSocket的类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @author zhaoxi
 * @date 2018/12/4 16:28
 * TODO:后台管理系统的推送
 */
@ServerEndpoint(value = "/websocket/manager")
@Component
public class ManagerWebSocket {
    /**
     * log4j日志
     */
    private static final Logger log = LoggerFactory.getLogger(ManagerWebSocket.class);

    /**
     * 静态容器,记录每个客户端连接时,产生对应的ManagerWebSocket对象。
     * concurrent包的线程安全Set。
     */
    private static CopyOnWriteArraySet<ManagerWebSocket> webSocketSet = new CopyOnWriteArraySet<>();

    /**
     * 静态变量,记录当前在线连接数。
     * 如果需要精确的统计,需要把它设计成线程安全的。
     */
    private static int onlineCount = 0;

    /**
     * 成员变量;每个客户端连接时,产生连接会话。
     * 需要通过它来给对应的客户端发送数据
     * session作为ManagerWebSocket对象的一个成员属性存储起来
     */
    private Session session;

    /**
     * TODO: 连接建立成功调用的方法
     *
     * @return
     * @throws
     * @author zhaoxi
     * @time 2018/12/4 16:32
     * @params
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        log.info("有新连接加入!当前在线人数为" + getOnlineCount());
    }

    /**
     * TODO: 连接关闭调用的方法
     *
     * @return
     * @throws
     * @author zhaoxi
     * @time 2018/12/4 16:36
     * @params
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * TODO: 收到客户端消息后调用的方法
     *
     * @return
     * @throws
     * @author zhaoxi
     * @time 2018/12/4 16:36
     * @params
     */
    @OnMessage
    public void onMessage(Session session, String message) {
        log.info("来自客户端的消息:" + message);
        /**
         * 回复此客户端
         */
        try {
            this.sendMessage(session, message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * TODO: 发生错误时调用
     *
     * @return
     * @throws
     * @author zhaoxi
     * @time 2018/12/4 16:40
     * @params
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.info("发生错误");
        error.printStackTrace();
    }


    /**
     * TODO: 给客户端发消息
     *
     * @return
     * @throws
     * @author zhaoxi
     * @time 2018/12/4 16:39
     * @params
     */
    public void sendMessage(Session session, String message) throws IOException {
        session.getBasicRemote().sendText(message);
    }

    public void sendMessage(String message) throws IOException {
        session.getBasicRemote().sendText(message);
    }

    /**
     * TODO: 群发自定义消息
     *
     * @return
     * @throws
     * @author zhaoxi
     * @time 2018/12/4 16:44
     * @params
     */
    public static void sendInfo(String message) throws IOException {
        for (ManagerWebSocket item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        ManagerWebSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        ManagerWebSocket.onlineCount--;
    }

到此为止,我们的WebSocket客户端,服务端已经配置完毕。我们可以通过

/**
 * TODO: 给客户端发消息
 *
 * @return
 * @throws
 * @author zhaoxi
 * @time 2018/12/4 16:39
 * @params
 */
public void sendMessage(Session session, String message) throws IOException {
    session.getBasicRemote().sendText(message);
}

这个方法给客户端发消息了。

3、Nginx反向代理WebSocket
发布到服务器时候,我们一般不开放8085端口,这时候直接域名转发,然后用域名访问一般是访问不到。而且还有跨域问题。

# push 接口 服务器代理
upstream server_push{
   server 127.0.0.1:8085;
}
 # push 推送接口
    server {
        listen       80;
        listen       443;
        server_name  push.***.com;
       #ssl证书(没有的话可以不配置)
        ssl on;
        ssl_certificate   cert/21456751*****433.pem;
        ssl_certificate_key  cert/2145*****6480433.key;
        ssl_session_timeout 5m;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;

        # 允许跨域
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
        add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
        if ($request_method = 'OPTIONS') {
           return 204;
        }

        location / {
            #添加websocket代理
            proxy_pass http://server_push;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }

猜你喜欢

转载自blog.csdn.net/zhaoxichen_10/article/details/84860560
今日推荐