springboot websocket 屏幕共享

实现springboot websocket同屏浏览功能

1,服务端:websocket screen share\jersey-server,推送给其他客户端。

2,运行websocketTest\client.bat,java websocket client截屏发送到服务端,客户端代码websocketTest\WebSocketClient.java。

3,通过浏览器拉取数据,地址为http://ip:8080/hello 运行顺序,先启动服务端,再启动推送客户端,最后通过浏览器浏览截屏,未实现客户端关闭连接处理,因此关闭客户端时会有异常,可以再重启服务端和推送客户端后重连。

4,可以调节客户端发送截屏频率,以及图片压缩质量。

5,注意,未做优化,本项目运行时占比较网络带宽(可以通过第四步调节发送频率和图片压缩质量调节运行时占用的网络资源)

6,记得修改设置websocket服务器的连接ip

代码下载地址

https://download.csdn.net/download/daqinzl/87976353

实现细节:

1,springboot websocket server enable

WebSocketConfig.java


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();
    }    
}

2, websocket server handler


import org.springframework.stereotype.Component;

import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;

/**
 * @ServerEndpoint
 * 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 */
@ServerEndpoint(value="/websocket")
@Component
public class WebSocketTest {
    
    private static ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<>();
    
    @OnOpen
    public void onOpen(Session session){ 
        System.out.println("加入连接");  
        sessions.put(session.getId(), session);
    }

    @OnClose
    public void onClose(){ 
        System.out.println("关闭连接");
        
    }

    @OnError
    public void onError(Session session, Throwable error){  
       System.out.println("发生错误");
       error.printStackTrace(); 
       //TODO
    }

    /**
     * 收到客户端消息后调用的方法
     * @param messages 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage(maxMessageSize = 5000000)
    public void onMessage(byte[] messages, Session session) {
        try {
          //System.out.println("接收到消息:"+new String(messages,"utf-8"));
            //返回信息
//            String resultStr="{name:\"张三\",age:18,addr:\"上海浦东\"}";
//            //发送字符串信息的 byte数组
//            ByteBuffer bf=ByteBuffer.wrap(resultStr.getBytes("utf-8"));
//            session.getBasicRemote().sendBinary(bf);
            //发送字符串
            //session.getBasicRemote().sendText("测试");


            //接收客户端发来的截屏数据,发送给其他客户端
            Iterator<Session> it = sessions.values().iterator();
            while(it.hasNext()) {
                Session tsession = it.next();
                if(tsession.getId()!=session.getId()) {
                    int len = messages.length;
                    int xx=0;
                    ByteBuffer bf=ByteBuffer.wrap(messages);
                    tsession.getBasicRemote().sendBinary(bf);
                }
            }
            
            
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
    

3, websocket客户端

3.1 WebSocketClient


import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;


//连接成功  
//tomcat-juli.jar, tomcat-util.jar, tomcat-websocket.jar, websocket-api.jar

public class WebSocketClient {
    
    public static void main(String[] args) {
        try {
            
//            String urlstr = "ws://localhost:8080/websocket";
            String urlstr = "ws://192.168.0.109:8080/websocket";
            
            final WebsocketClientEndpoint clientEndPoint = new WebsocketClientEndpoint(new URI(urlstr));

            // add listener
            clientEndPoint.addMessageHandler(new WebsocketClientEndpoint.MessageHandler() {
                public void handleMessage(String message) {
                    System.out.println(message);
                }
            });
            
            while(true) 
            {
                try {
                    Thread.sleep(30);
                    byte[] bytes = Util.captureScreen();
                    ByteBuffer bf=ByteBuffer.wrap(bytes);
                    
                    clientEndPoint.sendMessage(bf);                    
                }
                catch(Exception ex) {
                    ex.printStackTrace();
                }
                
            }
            
//            clientEndPoint.close();

        }
//        catch (InterruptedException ex) {
//            System.err.println("InterruptedException exception: " + ex.getMessage());
//        } 
        catch (URISyntaxException ex) {
            ex.printStackTrace();
            System.err.println("URISyntaxException exception: " + ex.getMessage());
        }
    }
   
}

3.2 WebsocketClientEndpoint


import java.net.URI;
import java.nio.ByteBuffer;

import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;

/**
 * ChatServer Client
 *
 * @author Jiji_Sasidharan
 */
@ClientEndpoint
public class WebsocketClientEndpoint {

    Session userSession = null;
    private MessageHandler messageHandler;

    public WebsocketClientEndpoint(URI endpointURI) {
        try {
            WebSocketContainer container = ContainerProvider.getWebSocketContainer();
            container.connectToServer(this, endpointURI);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Callback hook for Connection open events.
     *
     * @param userSession the userSession which is opened.
     */
    @OnOpen
    public void onOpen(Session userSession) {
        System.out.println("opening websocket");
        this.userSession = userSession;
    }

    /**
     * Callback hook for Connection close events.
     *
     * @param userSession the userSession which is getting closed.
     * @param reason the reason for connection close
     */
    @OnClose
    public void onClose(Session userSession, CloseReason reason) {
        System.out.println("closing websocket");
        this.userSession = null;
    }

    /**
     * Callback hook for Message Events. This method will be invoked when a client send a message.
     *
     * @param message The text message
     */
    @OnMessage
    public void onMessage(String message) {
        if (this.messageHandler != null) {
            this.messageHandler.handleMessage(message);
        }
        System.out.println("receive :" + message);
    }

    public void close(){
        onClose(userSession, new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "normal close"));
    }
    
    /**
     * register message handler
     *
     * @param msgHandler
     */
    public void addMessageHandler(MessageHandler msgHandler) {
        this.messageHandler = msgHandler;
    }

    /**
     * Send a message.
     *
     * @param message
     */
    public void sendMessage(String message) {
        this.userSession.getAsyncRemote().sendText(message);
    }

    public void sendMessage(ByteBuffer message) {
        this.userSession.getAsyncRemote().sendBinary(message);
    }
    
    /**
     * Message handler.
     *
     * @author Jiji_Sasidharan
     */
    public static interface MessageHandler {

        public void handleMessage(String message);
    }
}

3.3 截屏工具类

import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Iterator;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;

public class Util {

    private static Robot ro = null;
    private static BufferedImage bi = null;
    private static Dimension di = null;
    private static Rectangle rec = null;

    static {
        try {
            ro = new Robot(); // (通过本地操作)控制鼠标、键盘等实际输入源(java.awt)
            Toolkit tk = Toolkit.getDefaultToolkit(); // AWT组件的抽象父类(java.awt)
            di = tk.getScreenSize();
            rec = new Rectangle(0, 0, di.width, di.height);
        } catch (Exception e) {
//            logger.error("screensyncserver", e);
            e.printStackTrace();
        }        
    }
    
    public static byte[] captureScreen() {

        byte[] bytes = null;
        try {
            bi = ro.createScreenCapture(rec);
            BufferedImage get = bi.getSubimage(0, 0, di.width, di.height);

            // 从截屏中读取
            bytes = imageToBytes(get);
            return bytes;            
            
        } catch (Exception e) {
            //logger.error("getscreenandsend", e);
            e.printStackTrace();
        }
        return null;
    }
    

    private static byte[] imageToBytes(BufferedImage bImage) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            //ImageIO.write(bImage, "jpg", out);
            
            ImageOutputStream ios = ImageIO.createImageOutputStream(out); //var7
            Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("jpeg"); 
            ImageWriter writer = iter.next(); 
            ImageWriteParam iwp = writer.getDefaultWriteParam(); 
            iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); 
//            iwp.setCompressionQuality(1.0f);
            iwp.setCompressionQuality(0.3f);//TODO
            writer.setOutput(ios); 
            writer.write(null, new IIOImage(bImage,null,null),iwp); 
            writer.dispose();
            
        } catch (IOException e) {
            e.printStackTrace();
            //log.error(e.getMessage());
        }
        byte[] ret = out.toByteArray();
        try {
            out.close();
        } catch (IOException e) {
//            logger.error("image2bytes", e);
            e.printStackTrace();
        }
        return ret;
    }
        
}
 

4,浏览器拉取数据


<html>
<head> 
</head>
<body>
    <input id="text" type="text"/>
    <button οnclick="send()">send message</button> 
    <div id="message"></div>
    <!--收到的图片列表-->
    <div id="show"><h3>image:</h3>
    <img id="img" src=""/>
    </div>

</body>

<script type="text/javascript">
    var websocket = null;
    //判断当前浏览器是否支持WebSocket
    if ('WebSocket' in window) {
        websocket = new WebSocket("ws://192.168.0.109:8080/websocket");
        //websocket默认是传输字符串的,需要改为arraybuffer二进制传输类型
        websocket.binaryType = "arraybuffer";
    }else {
        alert('当前浏览器 Not support websocket')
    }

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

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

    //接收到消息的回调方法
    websocket.onmessage = function (event) { 
        //将接收到的二进制数据转为字符串
        document.getElementById("img").src="";
        var uInt8Array = new Uint8Array(event.data)
        var i = uInt8Array.length;
        var binaryString = new Array(i);
        while (i--)
        {
          binaryString[i] = String.fromCharCode(uInt8Array[i]);
        }
        var data = binaryString.join('');
     
        var base64 = window.btoa(data);
        //if(src.endsWith("jpeg"))
        var url="data:image/jpeg;base64," + base64;
        //else if(src.endsWith("gif"))
        //var url="data:image/gif;base64," + base64;

        var img = document.getElementById("img");
        img.src=url;
        img.height=1080;
        img.width=1920;        
   
    }

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

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function () {
        closeWebSocket();
    }
 
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

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

    //发送消息
    function send() {
        var message = document.getElementById('text').value; 
        
        //将字符串转换为byte数组
        var bytesArr= stringToByte(message);   
        var bytes =new Uint8Array(bytesArr.length) ;
        for (var i = 0; i < bytes.length; i++) {
            bytes[i]=bytesArr[i];
        }  
        console.log(bytes)
       websocket.send(bytes);
    }
    
    
        //将字符串转为 Array byte数组
        function stringToByte(str) {  
                var bytes = new Array();  
                var len, c;  
                len = str.length;  
                for(var i = 0; i < len; i++) {  
                    c = str.charCodeAt(i);  
                    if(c >= 0x010000 && c <= 0x10FFFF) {  
                        bytes.push(((c >> 18) & 0x07) | 0xF0);  
                        bytes.push(((c >> 12) & 0x3F) | 0x80);  
                        bytes.push(((c >> 6) & 0x3F) | 0x80);  
                        bytes.push((c & 0x3F) | 0x80);  
                    } else if(c >= 0x000800 && c <= 0x00FFFF) {  
                        bytes.push(((c >> 12) & 0x0F) | 0xE0);  
                        bytes.push(((c >> 6) & 0x3F) | 0x80);  
                        bytes.push((c & 0x3F) | 0x80);  
                    } else if(c >= 0x000080 && c <= 0x0007FF) {  
                        bytes.push(((c >> 6) & 0x1F) | 0xC0);  
                        bytes.push((c & 0x3F) | 0x80);  
                    } else {  
                        bytes.push(c & 0xFF);  
                    }  
                }  
                return bytes;  
    
    
            }
            
            //byte数组转字符串
            function byteToString(arr) {
                if(typeof arr === 'string') {
                    return arr;
                }
                var str = '',
                    _arr = arr;
                for(var i = 0; i < _arr.length; i++) {
                    var one = _arr[i].toString(2),
                        v = one.match(/^1+?(?=0)/);
                    if(v && one.length == 8) {
                        var bytesLength = v[0].length;
                        var store = _arr[i].toString(2).slice(7 - bytesLength);
                        for(var st = 1; st < bytesLength; st++) {
                            store += _arr[st + i].toString(2).slice(2);
                        }
                        str += String.fromCharCode(parseInt(store, 2));
                        i += bytesLength - 1;
                    } else {
                        str += String.fromCharCode(_arr[i]);
                    }
                }
            return str;
        }

    
</script>
</html>

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot中,可以使用WebSocket实现数据共享。下面是一个简单的示例: 首先,确保在pom.xml文件中添加了WebSocket的依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 然后,创建一个WebSocket配置类,用于配置WebSocket的相关信息: ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new MyWebSocketHandler(), "/websocket") .setAllowedOrigins("*"); } } ``` 接下来,创建一个WebSocket处理器类,用于处理WebSocket的连接、消息发送和接收等操作: ```java public class MyWebSocketHandler extends TextWebSocketHandler { private List<WebSocketSession> sessions = new ArrayList<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { sessions.add(session); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { for (WebSocketSession s : sessions) { s.sendMessage(message); } } } ``` 在上述示例中,我们创建了一个`MyWebSocketHandler`类,用于处理WebSocket的连接和消息发送。在`afterConnectionEstablished`方法中,我们将新建立的WebSocket会话添加到`sessions`列表中。在`handleTextMessage`方法中,我们遍历`sessions`列表,将接收到的消息发送给所有连接的客户端。 最后,启动Spring Boot应用程序,并使用WebSocket客户端连接到`ws://localhost:8080/websocket`。当客户端发送消息时,服务器将将该消息发送给所有连接的客户端。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值