java - 通过 WebSocket 连接的 Spring SseEmitter

标签 java spring websocket spring-boot stomp

我的目标是从前端向后端发送一个请求并接收多个响应。我使用 WebSocket 是因为响应非常频繁,并且 WebSocket 似乎是执行此操作的最佳协议(protocol),并且 SseEmitter 从后端发送多个响应。

这是我的请求 Controller :

@MessageMapping("/emitter")
@SendTo("/topic/response")
public SseEmitter output(RunData runData) throws Exception {
    SseEmitter emitter = new SseEmitter();
    new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                RemoteHostController rhc = new RemoteHostController(runData);
                rhc.execute();
                while (rhc.getActiveCount() > 0) {
                    emitter.send(rhc.getAllOutput());
                    Thread.sleep(2000);
                }

                 emitter.complete();
            } catch (Exception ee) {
                ee.printStackTrace();
                emitter.completeWithError(ee);
            }
        }
    }).start();

    return emitter;
}

RemoteHostController 正在管理连接,并且 getAllOutput 返回来自主机的输出。

前端应用程序正在运行非常简单的index.html,它使用Stomp和SockJS连接到websocket,将数据发送到服务器并使用响应中的数据生成

标签:

function connect() {
        var socket = new SockJS('http://localhost:8080/emitter');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function(frame) {
            setConnected(true);
            console.log('Connected: ' + frame);
            stompClient.subscribe('/topic/response', function(greeting){
                showOutput(greeting.body);
            });
        });
}

function sendData() {
        var hostname = document.getElementById('hostname').value;
        var username = document.getElementById('username').value;
        var password = document.getElementById('password').value;
        var command = document.getElementById('command').value;
        stompClient.send("/app/emitter", {}, JSON.stringify({ 'hostname': hostname,
                                                    'username': username,
                                                    'password': password,
                                                    'command': command}));
}

function showOutput(message) {
        var response = document.getElementById('response');
        var p = document.createElement('p');
        p.style.wordWrap = 'break-word';
        p.appendChild(document.createTextNode(message));
        response.appendChild(p);
}

当我将数据发送到后端时,我得到的唯一响应是:

{"timeout":null}

这是SseEmitter超时字段,当我更改超时时它将返回{"timeout":<timeout_value>} .

我可以在日志中看到 RemoteHostController 正在连接到主机并正确执行命令。

我做错了什么吗?或者WebSocket只支持一请求一响应通信?

最佳答案

这是 WebSocket 和 SSE 的示例。如上所述,IE 浏览器不支持 SSE。为了完整起见,我尽可能多地添加。确保在使用 SeeEmitter 时没有使用 RestController,因为这将返回对象,这是我从上面的描述中猜测的。

pom.xml

<dependencies>
    <!-- Spring boot framework -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
</dependencies>

网络套接字配置:

@Configuration
@EnableWebSocketMessageBroker
public class ApplicationWebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        super.configureMessageBroker(registry);
        registry.enableSimpleBroker("/topic");
    }

    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        stompEndpointRegistry.addEndpoint("/socketrequest").withSockJS();
    }
}

请求数据:

public class RequestData {
    private String string1;
    private String string2;
    // excluding getters and setters
}

网络套接字 Controller :

@Controller
public class WebSocketController {
   @Autowired
    SimpMessagingTemplate simpMessagingTemplate;

    @MessageMapping("/processrequest")
    void runWebSocket( RequestData requestData ) {
        new Thread(new RunProcess(requestData)).start();
    }

    private class RunProcess implements Runnable {
        private RequestData requestData;

        RunProcess(RequestData requestData) {
            this.requestData = requestData;
        }

        public void run() {
            simpMessagingTemplate.convertAndSend("/topic/response", requestData.getString1());
            simpMessagingTemplate.convertAndSend("/topic/response", requestData.getString2());
            simpMessagingTemplate.convertAndSend("/topic/response", "A third response via websocket");
        }
    }
}

Sse Controller :

@Controller
public class SseController {

    @RequestMapping("/emitter")
    public SseEmitter runEmitter(@RequestParam(value = "string1") String string1,
                                 @RequestParam(value = "string2") String string2)
    {
        SseEmitter sseEmitter = new SseEmitter();
        RequestData requestData = new RequestData();
        requestData.setString1(string1);
        requestData.setString2(string2);
        new Thread(new RunProcess(requestData,sseEmitter)).start();
        return sseEmitter;
    }

    private class RunProcess implements Runnable {
        private RequestData requestData;
        private SseEmitter sseEmitter;

        RunProcess(RequestData requestData, SseEmitter sseEmitter) {
            this.requestData = requestData;
            this.sseEmitter = sseEmitter;
        }

        public void run() {
            try {
                sseEmitter.send(requestData.getString1());
                sseEmitter.send(requestData.getString2());
                sseEmitter.send("A third response from SseEmitter");
                sseEmitter.complete();
            } catch (IOException e) {
                e.printStackTrace();
                sseEmitter.completeWithError(e);
            }
        }
    }

}

HTML 代码:

    <script src="/javascript/sockjs-0.3.4.js"></script>
    <script src="/javascript/stomp.js"></script>

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

    function connect() {
        var socket = new SockJS('http://localhost:8085/socketrequest');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function(frame) {
            console.log('Connected: ' + frame);
            stompClient.subscribe('/topic/response', function(message){
                showOutput(message.body);
            });
        });
    }

    function doWebsocket() {
        stompClient.send("/processrequest", {}, JSON.stringify({ 'string1': 'The first string', 'string2' : 'The second string' }));
    }


    function doSse() {
        console.log("doSse");
        var rtUrl= '/emitter?string1=first string sse&string2=second string sse';
        var source = new EventSource(rtUrl);
        source.onmessage=function(event){
            showOutput(event.data)
        };
    }

    function showOutput(message) {
        var response = document.getElementById('response');
        var p = document.createElement('p');
        p.style.wordWrap = 'break-word';
        p.appendChild(document.createTextNode(message));
        response.appendChild(p);
    }

    connect();

    </script>
    </head>

<div>
Starting page
</div>
<div>
    <button id="websocket" onclick="doWebsocket();">WebSocket</button>
    <button id="sse" onclick="doSse();">Server Side Events</button>
</div>
<div >
    Response:
    <p id="response"></p>
</div>

</html>

关于java - 通过 WebSocket 连接的 Spring SseEmitter,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35934930/

相关文章:

Java - 在 KeyStore 中存储 key 对

java - 使用 java 驱动程序更新 mongodb 需要很长时间?

java - 了解 Spring @Autowired 的用法

java - 如何在 JavaEE 应用程序中使用 Derby Embedded + JPA 2.0 + Hibernate 设置环境?

ios - 原生 iOS 客户端可以连接到 meteor.js 服务器吗?

python - 使用套接字(NodeJS,Socket.io)将数据从 Python 发送到 Node 服务器

c# - ASP.NET SignalR - 空闲时消耗的带宽

java - 如何在现有 Spring Hibernate 应用程序中包含 Spring JPA

java - 使用 Java Stream API 计算中位数

Java SE + Spring Data + Hibernate