【分享】JFinal undertow 实现 websocket

        看了很多关于JFinal 关于 websocket 实现方法,不过很多都是在早前版本,或Tomcat下面实现,结合JFinal(3.4) + Jbolt(1.8.1) + undertow(1.8) 才是王道,先将代码贴出来。

pom.xml 添加

<dependency>
    <groupId>io.undertow</groupId>
    <artifactId>undertow-websockets-jsr</artifactId>
    <version>2.0.25.Final</version>
</dependency>


Session 管理类 WebSocketMapUtil

public class WebSocketMapUtil {
public static ConcurrentMap<String, MyWebSocket> webSocketMap = new ConcurrentHashMap<>();
public static void put(String key, MyWebSocket myWebSocket){
     webSocketMap.put(key, myWebSocket);
}
public static MyWebSocket get(String key){
        return webSocketMap.get(key);
    }
public static void remove(String key){
        webSocketMap.remove(key);
    }
public static Collection<MyWebSocket> getValues(){
        return webSocketMap.values();
    }
}


实现类 MyWebSocket

@ServerEndpoint("/quotes.ws")
public class MyWebSocket {
public Session session;
    @OnMessage
    public void message(String message, Session session) {
        for (Session s : session.getOpenSessions()) {
            s.getAsyncRemote().sendText(message);
        }
    }
    /**
     * 连接建立后触发的方法
     */
    @OnOpen
    public void onOpen(Session session){
        this.session = session;
        LogKit.info("====== onOpen:"+session.getId()+" ======");
        WebSocketMapUtil.put(session.getId(),this);
    }
    /**
     * 连接关闭后触发的方法
     */
    @OnClose
    public void onClose() {
        WebSocketMapUtil.remove(session.getId());
        LogKit.info("====== onClose:"+session.getId()+" ======");
    }
    /**
     * 发生错误时触发的方法
     */
    @OnError
    public void onError(Session session,Throwable error){
        LogKit.info(session.getId()+"连接发生错误"+error.getMessage());
        error.printStackTrace();
    }
}


MainConfig 启动配置

    public static void main(String[] args) {
        UndertowServer undertowServer = UndertowServer.create(MainConfig.class,"undertow.properties");
        undertowServer.configWeb(builder->{builder.addWebSocketEndpoint(MyWebSocket.class);});
        undertowServer.start();
    }


test.html

<html>
<head>
<title>Undertow Chat</title>
<script>
    var socket;
    if (window.WebSocket) {
        socket = new WebSocket("ws://localhost/quotes.ws");
        socket.onmessage = function (event) {
            var chat = document.getElementById('chat');
            chat.innerHTML = chat.innerHTML + event.data + "<br />";
        };
    } else {
        alert("Your browser does not support Websockets. (Use Chrome)");
    }
    function send(message) {
        if (!window.WebSocket) {
            return false;
        }
        if (socket.readyState == WebSocket.OPEN) {
            socket.send(message);
        } else {
            alert("The socket is not open.");
        }
        return false;
    }
</script>
<style type="text/css">
    html,body {width:100%;height:100%;}
    html,body,ul,ol,dl,li,dt,dd,p,blockquote,fieldset,legend,img,form,h1,h2,h3,h4,h5,h6 {margin:0;padding:0;}
    body {
        font:normal 12px/1.5 Arial,Helvetica,'Bitstream Vera Sans',sans-serif;
        background: #c5deea; /* Old browsers */
        background: -moz-linear-gradient(top, #c5deea 0%, #066dab 100%); /* FF3.6+ */
        background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #c5deea), color-stop(100%, #066dab)); /* Chrome,Safari4+ */
        background: -webkit-linear-gradient(top, #c5deea 0%, #066dab 100%); /* Chrome10+,Safari5.1+ */
        background: -o-linear-gradient(top, #c5deea 0%, #066dab 100%); /* Opera 11.10+ */
        background: -ms-linear-gradient(top, #c5deea 0%, #066dab 100%); /* IE10+ */
        background: linear-gradient(to bottom, #c5deea 0%, #066dab 100%); /* W3C */
        height: 90%;
    }
    .center {
        margin-left: auto;
        margin-right: auto;
        width: 70%;
        background: white;
    }
    .chatform {
        margin-left: auto;
        margin-right: auto;
        margin-bottom: 0;
        width: 70%;
        height: 200px;
    }
    form{
        width: 100%;
    }
    label{
        display: inline;
        width: 100px;
    }
    #msg{
        display: inline;
        width: 100%;
    }
</style>
</head>
<body>
<div class="page">
    <div class="center" >
        <h1>Socket Chat</h1>
        <div id="chat" style="height:80%;width: 100%; overflow: scroll;">
        </div>
        <form onsubmit="return false;" class="chatform" action="">
            <label for="msg">Message</label>
            <input type="text" name="message" id="msg"  onkeypress="if(event.keyCode==13) { send(this.form.message.value); this.value='' } " />
        </form>
    </div>
</div>
</body>
</html>


默认IndexController 里添加方法

    public void test() {    
        render("test.html");
    }


以上都配置好后,运行MainConfig 打开 http://127.0.0.1/test

底下输入信息回车,后台会将收到的信息推送到页面,另外再打开一个新的浏览器输入上面地址,其中一个输入信息,后台也会将信息推送给另外的浏览器。

另外同时问一下JFinal的大神们,如果我要服务端定时主动给客户端推送信息,比如我结合了任务quartz,一分钟推送一次信息,我要怎么调用,网上找了没有太好的办法


评论区

JFinal

2019-08-23 17:21

代码很详细,收藏加点击,下次有人问题起 websocket 问题,可以直接发你的分享了

此外,jfinal 建议升级到 4.4, jfinal undertow 建议升级到 1.9

playsex

2019-08-23 17:30

@JFinal 好的,如果我要服务端定时主动给客户端推送信息,比如我结合了任务quartz,一分钟推送一次信息,我要怎么调用,网上找了没有太好的办法?

JFinal

2019-08-23 17:41

@playsex 你的 webSocketMap 中已经存放了相关的类,大致这样从服务端发起消息推送:
for (MyWebSocket mws : webSocketMap.values()) {
msw.session.getAsyncRemote().sendText(message);
}

上面的代码是向所有客户端发消息,你也可以通过 id 获取某一个客户端,单独向它来发信息,例如:
webSocketMap.get(sessionId).session.getAsyncRemote().sendText(message);

将上面的代码放在定时器中即可,注意,在定时器中你要有一个对象持有 webSocketMap 这个对象

playsex

2019-08-23 17:43

SpringBoot 使用 SimpMessagingTemplate.convertAndSend("/xxx", new OutMessage(message.getContent()));来实现,JFinal 没找到类似的办法。还是我必须将信息传到上面/quotes.ws,才会广播出去。

Dreamlu

2019-08-23 18:54

@playsex 你把 Session 保留起来就可以了,遍历 Session 列表去广播,话说 ws 和 JFInal-event 更搭配哦。

要输就输给追求

2019-08-23 21:57

你怕是没看到这个分享 https://www.jfinal.com/share/1344

playsex

2019-08-25 13:45

@JFinal 谢谢,正解。

zhangchuang

2019-08-26 11:53

@Dreamlu 请教下, 假如 消息 需要存库,一条消息需要推送 给 N 个人(百十人,消息存库分已读未读),使用 event 或 自定义线程池,有办法整体控制事务 吗? 其实也就是 N 个任务,某个子线程异常,有办法简单地控制整体事务吗?

Dreamlu

2019-08-28 19:05

@zhangchuang 为啥要整理控制事物呢,感觉这个方向就偏了。统一做子线程的异常处理这样岂不是更加简单。

热门分享

扫码入社