springboot的websocket服务端及客户端的实现,可在Linux+nginx上部署运行

springboot的WebSocket实现

  • 需要引入的maven pom.xml文件
    • 添加springboot的websocket支持
    • 实现WebSocket服务端,创建一个类,我这里起名叫做MyWebSocketServer
    • 采用java代码实现websocket的客户端
    • 采用html页面的形式实现客户端连接
    • 将websocket服务放在Linux上,且通过nginx转发
    • 总结

需要引入的maven pom.xml文件


    
        org.springframework.boot
        spring-boot-starter-websocket
    

    
    
        org.java-websocket
        Java-WebSocket
        1.4.0
    

添加springboot的websocket支持

这里采用的是springboot的内置容器,所以需要注入ServerEndpointExporter这个类,来开启websocket的支持

/**
 * Author: songxiaoyan
 * Description:开启WebSocket支持
 * Date: Created in 14:53 2019/5/7
 */
@Configuration
public class WebSocketConfig {

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

实现WebSocket服务端,创建一个类,我这里起名叫做MyWebSocketServer

这里的@ServerEndpoint类似于spring的controller层,ws协议可以从这个入口进入,另还可以携带参数

com.dxjtai.aicappk.service;

import com.dxjtai.aicappk.postcall.RecogEngine;
import org.json.JSONObject;
import org.json.XML;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * Author: songxiaoyan
 * Description:websocket服务端
 * Date: Created in 14:57 2019/5/7
 */
@ServerEndpoint("/websocket/{sid}")
@Component
public class MyWebSocketServer {
    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<MyWebSocketServer> webSocketSet = new CopyOnWriteArraySet<MyWebSocketServer>();

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    //接收sid
    private String sid="";
    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session,@PathParam("sid") String sid) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        System.out.println("有新窗口开始监听:"+sid+",当前在线人数为" + getOnlineCount());
        this.sid=sid;
        try {
            sendMessage("连接成功");
            //设置传输最大字节数
            session.setMaxBinaryMessageBufferSize(524280000);
            session.setMaxTextMessageBufferSize(524280000);
        } catch (IOException e) {
            System.err.println("websocket IO异常");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("收到来自窗口"+sid+"的信息:"+message);
        //群发消息
        for (MyWebSocketServer item : webSocketSet) {
            try {
                item.sendMessage("服务器推出的消息"+message);
//                sendInfo("服务器推出的消息"+message+sid,sid);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @OnMessage
    public void onMessage(byte[] message,Session session) throws IOException {
//        saveFileFromBytes(message, "src/" + fileName);
        System.out.println("进入消息接收");
//     	根据自己的业务进行相应处理,我这里是要接受一个数据流,处理完返回json串。
        String url = "";//根据自己业务自定义url
        RecogEngine.HttpRequest request = new RecogEngine().new HttpRequest();
        String response = null;
        response = request.post(url, message);
        JSONObject xmlJSONObj = XML.toJSONObject(response);
        response = xmlJSONObj.toString();
        System.out.println(response);
       	//调用服务端返回消息
        sendMessage( response );
    }

    /**
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.err.println("发生错误");
        error.printStackTrace();
    }
    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }


    /**
     * 群发自定义消息
     * */
    public static void sendInfo(String message,@PathParam("sid") String sid) throws IOException {
        System.out.println("推送消息到窗口"+sid+",推送内容:"+message);
        for (MyWebSocketServer item : webSocketSet) {
            try {
                //这里可以设定只推送给这个sid的,为null则全部推送
                if(sid==null) {
                    item.sendMessage(message);
                }else if(item.sid.equals(sid)){
                    item.sendMessage(message);
                }
            } catch (IOException e) {
                continue;
            }
        }
    }


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

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

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

采用java代码实现websocket的客户端

这里使用的是java-websocket实现的客户端,感兴趣的话可以试着使用springboot集成的客户端

package com.dxjtai.aicappk.util;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.enums.ReadyState;
import org.java_websocket.handshake.ServerHandshake;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;

/**
 * Author: songxiaoyan
 * Description:websocket客户端测试
 * Date: Created in 17:00 2019/5/8
 * Modified By:
 */
public class WebsocketClient {

    public static WebSocketClient client;

	private static String url = "ws://localhost:8011/websocket/33";
    public static void main(String[] args) throws InterruptedException {
        try {
            client = new WebSocketClient(new URI(url),new Draft_6455()) {
                @Override
                public void onOpen(ServerHandshake serverHandshake) {
                    System.out.println("打开链接");
                }

                @Override
                public void onMessage(String s) {
                    System.out.println("收到消息"+s);
                }

                @Override
                public void onClose(int i, String s, boolean b) {
                    System.out.println("链接已关闭");
                }

                @Override
                public void onError(Exception e) {
                    e.printStackTrace();
                    System.out.println("发生错误已关闭");
                }
            };
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }

        client.connect();
        System.out.println(client.getDraft());
        while(!client.getReadyState().equals(ReadyState.OPEN)){
            System.out.println("正在连接");
        }
        System.out.println("打开了");
        try {
            String filename = "C:\\Users\\knowologe\\Desktop\\8k16bit.wav";
            byte[] data=InputStream2ByteArray(filename);
//            for(int i=0;i<5;i++){
                client.send(data);
//                Thread.sleep(1000);
//            }
        } catch (Exception e) {
            e.printStackTrace();
        }
//        client.close();
    }


    public static void send(byte[] bytes){
        client.send(bytes);
    }

    private static byte[] InputStream2ByteArray(String filePath) throws IOException {

        InputStream in = new FileInputStream(filePath);
        byte[] data = toByteArray(in);
        in.close();

        return data;
    }

    private static byte[] toByteArray(InputStream in) throws IOException {

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 4];
        int n = 0;
        while ((n = in.read(buffer)) != -1) {
            out.write(buffer, 0, n);
        }
        return out.toByteArray();
    }

}

采用html页面的形式实现客户端连接

<!DOCTYPE html>
<html>
<head>
<title>WebSocket Chat Client</title>
<meta charset="utf-8" />
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script type="text/javascript" >
    //判读浏览器是否支持websocket
    $().ready(function() {
        if ( !window.WebSocket ) {
             alert("童鞋, 你的浏览器不支持该功能啊");
        }
         
    });
    
    //在消息框中打印内容
function log(text) {
        $("#log").append(text+"\n");
    }
    
    //全局的websocket变量
var ws;
    
    //创建连接
    $(function() {
    $("#uriForm").submit(function() {
        log("准备连接到" + $("#uri").val());
        
        ws = new WebSocket($("#uri").val());
        //连接成功建立后响应
        ws.onopen = function() {
            log("成功连接到" + $("#uri").val());
        }
        //收到服务器消息后响应
        ws.onmessage = function(e) {
            log("收到服务器消息:" + e.data + "'\n");
        }
        //连接关闭后响应
        ws.onclose = function() {
            log("关闭连接");
            $("#disconnect").attr({"disabled":"disabled"});
            $("#uri").removeAttr("disabled");
            $("#connect").removeAttr("disabled");
            ws = null;
        }
        $("#uri").attr({"disabled":"disabled"});
        $("#connect").attr({"disabled":"disabled"});
        $("#disconnect").removeAttr("disabled");
        return false;
    });
    });
    
    //发送字符串消息
    $(function() {
    $("#sendForm").submit(function() {
         if (ws) {
             var textField = $("#textField");
             ws.send(textField.val());
             log("我说:" + textField.val());
             textField.val("");
             textField.focus();
         }
         return false;
    });
    });
    
    //发送arraybuffer(二进制文件)
    $(function() {
    $("#sendFileForm").submit(function() {
        var inputElement = document.getElementById("file");
        var fileList = inputElement.files;
        
        for ( var i = 0; i < fileList.length; i++) {
            console.log(fileList[i]);
            log(fileList[i].name);
            //发送文件名
            ws.send(fileList[i].name);
//            reader.readAsBinaryString(fileList[i]);
//读取文件  
       var reader = new FileReader();
            reader.readAsArrayBuffer(fileList[i]);
//            reader.readAsText(fileList[i]);
//文件读取完毕后该函数响应
            reader.onload = function loaded(evt) {
                var binaryString = evt.target.result;
                // Handle UTF-16 file dump
                log("\n开始发送文件");
                ws.send(binaryString);
            }
        }
        return false;
    });
    });
    
    $(function() {
    $("#disconnect").click(function() {
         if (ws) {
             $("#log").empty();
             ws.close();
             ws = null;
         }
         return false;
    });
    });
    
    $(function() {
    $("#reset").click(function() {
        $("#log").empty();
         return false;
    });
    });
    
    
</script>
</head>
<body>
    <form id="uriForm">
        <input type="text" id="uri" value="ws://localhost:8011/websocket/33"
            style="width: 200px;"> <input type="submit" id="connect"
            value="Connect"><input type="button" id="disconnect"
            value="Disconnect" disabled="disabled">
    </form>
    <br>
    
    <form id="sendFileForm">
        <input id="file" type="file" multiple />
        <input type="submit" value="Send" />
        <input type="button" id="reset" value="清空消息框"/>
    </form>
    <br>
    <form id="sendForm">
        <input type="text" id="textField" value="" style="width: 200px;">
        <input type="submit" value="Send">
    </form>
    <br>
    <form>
        <textarea id="log" rows="30" cols="100"
            style="font-family: monospace; color: red;"></textarea>
    </form>
    <br>
</body>
</html>

将websocket服务放在Linux上,且通过nginx转发

需要在nginx.config中配置
开始未加入协议转换头文件的设置,导致不能识别ws协议,一直认为是个http协议,报错GET /websocket/33 HTTP/1.1" 404 144 “-” “-”

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “upgrade”;

server {
      listen   8080;
      server_name 域名;
      location / {
        proxy_pass   http://127.0.0.1:8011/; // 代理转发地址
      proxy_http_version 1.1;//http的版本号
        proxy_read_timeout   3600s; // 超时设置,默认为60s
        // 启用支持websocket连接,必须设置,主要讲http协议转化为ws协议
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
      }
}

总结

最近公司需要用到websocket全双工通信处理消息,经过各种跳坑,总算成功完成。非常感谢网上各位大牛分享的博客,帮助了我很多,欢迎各位小伙伴一起探讨研究哦!
另,如有侵权,请联系本人删除!

参考博客资料:
https://blog.csdn.net/moshowgame/article/details/80275084
https://blog.csdn.net/weixin_37264997/article/details/80341911

你可能感兴趣的:(Java,随笔,WebSocket,springboot,nginx)