如何使用Spring通过WebSocket向客户端发送消息

33

我尝试使用Spring与websocket。我从这篇教程开始了我的调查。

在我的客户端中,我有类似以下的代码来初始化与服务器的连接:

function connect() {
    var socket = new SockJS('/myphotos/form');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function(frame) {
        setConnected(true);
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/greetings', function(greeting){
            showGreeting(JSON.parse(greeting.body).content);
        });
    });
}

它的效果非常好,在我的控制器中,我能够在以下类中执行我的流程:

@Controller
@RequestMapping("/")
public class PhotoController {

    @MessageMapping("/form")
    @SendTo("/topic/greetings")
    public Greeting validate(AddPhotosForm addPhotosForm) {
        return new Greeting("Hello world !");
    }
}

现在我想要做的是让一个线程向监听“/topic/greeting”的客户端发送消息。我编写了这样的可运行类:

public class FireGreeting implements Runnable {

    private PhotoController listener;

    public FireGreeting(PhotoController listener) {
        this.listener = listener;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep( 2000 );
                listener.fireGreeting();
            } catch ( InterruptedException e ) {
                e.printStackTrace();
            }
        }   
    }
}

然后我完成了我的控制器:

@Controller
@RequestMapping("/")
public class PhotoController {

    @MessageMapping("/form")
    @SendTo("/topic/greetings")
    public Greeting validate(AddPhotosForm addPhotosForm) {

        // added this part
        FireGreeting r = new FireGreeting( this );
        new Thread(r).start();

        return new Greeting("Hello world !");
    }

    // added this method
    @SendTo("/topic/greetings")
    public Greeting fireGreeting() {
        System.out.println("Fire");
        return new Greeting("Fire");
    }
}

我调用了PhotoController.fireGreeting方法,但客户端上没有任何反应。有什么建议吗?谢谢。


14
请阅读Spring Websocket教程中的21.4.5 发送消息。这肯定不是你应该做的方式。还要考虑阅读关于Spring 调度机制,而不是使用原始线程(不正确地)。 - Boris the Spider
谢谢您提供的链接,这正是我想要做的。关于线程,显然我不会在应用程序的最终状态中使用它。我在服务器端有一个长时间运行的进程,将向客户端发送信息,让他知道已经完成了哪些步骤。 - cheb1k4
更新@BoristheSpider的链接:4.4.7. 发送消息 - Denis Abakumov
2个回答

59

得益于@Boris the Spider,我成功解决了我的问题。正确的解决方案是这样的:

@Controller
@RequestMapping("/")
public class PhotoController {

    @Autowired
    private SimpMessagingTemplate template;

    @MessageMapping("/form")
    @SendTo("/topic/greetings")
    public Greeting validate(AddPhotosForm addPhotosForm) {

        FireGreeting r = new FireGreeting( this );
        new Thread(r).start();

        return new Greeting("Hello world !");
    }

    public void fireGreeting() {
        System.out.println("Fire");
        this.template.convertAndSend("/topic/greetings", new Greeting("Fire"));
    }
}

注入的 SimpMessagingTemplate 使用了哪个 MessageChannel 类? - clD

7
更好的定期任务调度方式是使用Spring调度机制(请参见this guide),正如@Boris the Spider所建议的。
出于关注点分离的考虑,我还会将与计划相关的代码与控制器代码分开。
在您的情况下,您可以使用像这样的类:
@Component
public class ScheduledTasks {

    @Autowired
    private SimpMessagingTemplate template;

    @Scheduled(fixedRate = 2000)
    public void fireGreeting() {
        this.template.convertAndSend("/topic/greetings", new Greeting("Fire"));
    }
}

并将@EnableScheduling标签添加到您的Application类中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接