WebSocket jfinal 实现简单的聊天室功能

时间:2022-12-24 10:08:47
tomcat8才支持 websocket
 
 
 
用到了 websocket-api.jar 包
 
package com.appwx.test;

import java.io.IOException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import com.jfinal.kit.JsonKit;

/**
 * @class MyWebSocket.java
 * @author tuozq
 * @date 2017年3月3日 下午1:05:05
 * @descriptions
 */
//@ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端。注解的值将被用于监听用户连接的终端访问URL地址。
@ServerEndpoint("/mywebsocket")
public class MyWebSocket {

	 // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private  static final AtomicInteger onlineCount = new AtomicInteger(0);
    // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识

    private static Hashtable<String, Session> sessionMap = new Hashtable<String, Session>();

    private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();
    //定义一个记录客户端的聊天昵称
    private final String nickname;
    // 与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    public  MyWebSocket() {
        nickname = "访客"+onlineCount.getAndIncrement();
    }
	//onMessage 方法被@OnMessage所注解。这个注解定义了当服务器接收到客户端发送的消息时所调用的方法。
	//注意:这个方法可能包含一个javax.websocket.Session可选参数(在我们的例子里就是session参数)。如果有这个参数,容器将会把当前发送消息客户端的连接Session注入进去。

	@OnMessage
	public void onMessage(String message, Session session)
			throws IOException, InterruptedException {

		// Print the client message for testing purposes
		System.out.println(this.session==session);
		Map<String, Object> retMap = new HashMap<String, Object>();
	    retMap.put("msg", message);
	    retMap.put("nickname", nickname);
        broadcast(retMap);
	}
	//onOpen 和 onClose 方法分别被@OnOpen和@OnClose 所注解。这两个注解的作用不言自明:他们定义了当一个新用户连接和断开的时候所调用的方法。
	@OnOpen
	public void onOpen(Session session) {
		this.session = session;
		sessionMap.put(this.nickname, this.session);
        webSocketSet.add(this);
        String message = String.format("[%s,%s]",nickname,"加入聊天室");
        Map<String, Object> retMap = new HashMap<String, Object>();
	    retMap.put("msg", message);
	    retMap.put("nickname", nickname);
	    broadcast(retMap);
        System.out.println("onOpen");
	}

	@OnClose
	public void onClose(Session session) {
		 sessionMap.remove(this.nickname);
		 webSocketSet.remove(this);
	     String message = String.format("[%s,%s]",nickname,"离开了聊天室链接");
	     Map<String, Object> retMap = new HashMap<String, Object>();
	     retMap.put("msg", message);
	     broadcast(message);
	}

	 //完成群发
    private void broadcast( Map<String, Object> retMap ){
        for(MyWebSocket w:webSocketSet){
            try {
                synchronized (MyWebSocket.class) {
                	if(!w.session.getId().equals(this.session.getId())){
                		String jsonstr = JsonKit.toJson(retMap);
                		System.out.println(jsonstr);
                		w.session.getBasicRemote().sendText(jsonstr);
                	}
                }
            } catch (IOException e) {
                System.out.println("向客户端"+w.nickname+"发送消息失败");
                webSocketSet.remove(w);
                try {
                    w.session.close();
                } catch (IOException e1) {}
                String message = String.format("[%s,%s]",w.nickname,"已经断开链接");
                Map<String, Object> _retMap = new HashMap<String, Object>();
                _retMap.put("msg", message);
                broadcast(_retMap);
            }

        }
    }
    private void broadcast(String msg){
        for(MyWebSocket w:webSocketSet){
            try {
                synchronized (MyWebSocket.class) {
                	if(!w.session.getId().equals(this.session.getId())){
                		w.session.getBasicRemote().sendText(msg);
                	}
                }
            } catch (IOException e) {
                System.out.println("向客户端"+w.nickname+"发送消息失败");
                webSocketSet.remove(w);
                try {
                    w.session.close();
                } catch (IOException e1) {}
                String message = String.format("[%s,%s]",w.nickname,"已经断开链接");
                broadcast(message);
            }

        }
    }
    //对用户的消息可以做一些过滤请求,如屏蔽关键字等等。。。
    public static String filter(String message){
        if(message==null){
            return null;
        }
        return message;
    }
}
 

js:

<script type="text/javascript">
		var webSocket = null;
		//判断当前浏览器是否支持WebSocket
		if ('WebSocket' in window) {
			webSocket = new WebSocket('ws://localhost:8080/mywebsocket');
		} else {
			alert('当前浏览器 Not support websocket')
		}
		webSocket.onerror = function(event) {
			onError(event);
		};

		webSocket.onopen = function(event) {
			onOpen(event);
		};

		webSocket.onmessage = function(event) {
			onMessage(event, false);
		};

		function addMsgInfo(message, iscurr) {

			var jsonObj = null;
			if(!iscurr){
				jsonObj = JSON.parse(message.data);

			}else{
				jsonObj = message.data;
			}



			var html = "";
			html += '<div class = "msginfo">';
			if (iscurr) {
				html += '  <p class = "msgcoontent"><span class = "floatRight">'
						+ jsonObj.msg + '</span></p>';
			} else {
				html += '  <p class = "msgtitle">' + jsonObj.nickname + '</p>';
				html += '  <p class = "msgcoontent"><span>' + jsonObj.msg
						+ '</span></p>';
			}
			html += '</div>';
			$("#msgWindow").append(html);
		}

		function onMessage(event, iscurr) {
			addMsgInfo(event, iscurr);
		}

		function onOpen(event) {
			//alert("连接成功!");
		}

		function onError(event) {
			alert(event.data);
		}

		function sendmsg() {
			var sendmsg = $("#txtinputmsg").val();
			if (sendmsg) {
				var msgdata = {data : {msg:sendmsg}};
				addMsgInfo(msgdata, true);
				webSocket.send(sendmsg);
				$("#txtinputmsg").val("");
			}

		}

		function clearmsg() {
			$("#msgWindow").html("");
		}

		$(".friendsul li").click(function(){
			$(".friendsul li").removeClass("active");
			$(this).addClass("active");
		});
	</script>

jfinalConfig中要配置一下,对WebSocket的actionkey放行,否则会被jfinal的filter拦截

@Override
	public void configHandler(Handlers me) {
		// TODO Auto-generated method stub
		me.add(new WebSocketHandler("/mywebsocket"));

	}

package com.appwx.core.handler;

import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.jfinal.handler.Handler;
import com.jfinal.kit.StrKit;

/**
 * @class WebSocketHandler.java
 * @author tuozq
 * @date 2017年3月3日 下午2:12:21
 * @descriptions
 */
public class WebSocketHandler extends Handler {

	private Pattern filterUrlRegxPattern;

	public WebSocketHandler(String filterUrlRegx) {
		if (StrKit.isBlank(filterUrlRegx))
			throw new IllegalArgumentException("The para filterUrlRegx can not be blank.");
		filterUrlRegxPattern = Pattern.compile(filterUrlRegx);
	}

	@Override
	public void handle(String paramString,
			HttpServletRequest paramHttpServletRequest,
			HttpServletResponse paramHttpServletResponse,
			boolean[] paramArrayOfBoolean) {
		// TODO Auto-generated method stub
		if (filterUrlRegxPattern.matcher(paramString).find())
			return ;
		else
			next.handle(paramString, paramHttpServletRequest, paramHttpServletResponse, paramArrayOfBoolean);
	}

}