web即时通信2--基于Spring websocket实现web聊天室

          本文使用Spring4和websocket搭建一个web聊天室,框架基于SpringMVC+Spring+Hibernate的Maven项目,后台使用spring websocket进行消息转发和聊天消息缓存。客户端使用socket.js和stomp.js来进行消息订阅和消息发送。详细实现见下面代码。

         首先在pom.xml中添加对spring websocket的相关依赖包。

         一、添加websocket依赖

             
			org.springframework
			spring-webmvc
			${springframework.version}
		
		
			org.springframework
			spring-websocket
			${springframework.version}
		
		
			org.springframework
			spring-messaging
			${springframework.version}
		
		
			org.apache.poi
			poi
			3.9
		
               
                com.fasterxml.jackson.core  
                jackson-core  
                2.3.0  
              
              
                com.fasterxml.jackson.core  
                jackson-databind  
                2.3.0  
              
              
                com.fasterxml.jackson.core  
                jackson-annotations  
                2.3.0  
               

       其中4.0.3.RELEASE。因为spring4以上才支持WebSocket。

        2、配置Spring WebSocket

        该配置可以在Spring MVC的配置文件配置,也可以使用注解方式配置,本文使用注解@Configuration方式进行配置。

package com.test.chat.controller;

import java.util.List;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer{
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //添加这个Endpoint,这样在网页中就可以通过websocket连接上服务了
        registry.addEndpoint("/webchat").withSockJS();
    }
     
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        System.out.println("服务器启动成功");
        //这里设置的simple broker是指可以订阅的地址,也就是服务器可以发送的地址
        config.enableSimpleBroker("/userChat","/initChat","/initFushionChart","/updateChart","/videoChat");  
        config.setApplicationDestinationPrefixes("/app");   
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration channelRegistration) {
    }

    @Override
    public void configureClientOutboundChannel(ChannelRegistration channelRegistration) {
    }

	@Override
	public void configureWebSocketTransport(
			WebSocketTransportRegistration registry) {
		// TODO Auto-generated method stub
		System.out.println("registry:"+registry);
	}

	@Override
	public boolean configureMessageConverters(
			List messageConverters) {
		// TODO Auto-generated method stub
		System.out.println("messageConverters:"+messageConverters);
		return true;
	}
}

    要使配置文件生效,需在Spring的文件中能够扫描到该文件所在的包。即配置

       3、聊天内容的实体对象和后台关键代码

package com.test.chat.model;

public class ChatMessage {
	//房间号
	private String roomid;
	//用户名
	private String userName;
	//机构名
	private String deptName;
	//当前系统时间
	private String curTime;
	//聊天内容
	private String chatContent;
	//是否是系统消息
	private String isSysMsg;
	
	public String getIsSysMsg() {
		return isSysMsg;  
	}
	public void setIsSysMsg(String isSysMsg) {
		this.isSysMsg = isSysMsg;
	}
	public String getRoomid() {
		return roomid;
	}
	public void setRoomid(String roomid) {
		this.roomid = roomid;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getDeptName() {
		return deptName;
	}
	public void setDeptName(String deptName) {
		this.deptName = deptName;
	}
	public String getCurTime() {
		return curTime;
	}
	public void setCurTime(String curTime) {
		this.curTime = curTime;
	}
	public String getChatContent() {
		return chatContent;
	}
	public void setChatContent(String chatContent) {
		this.chatContent = chatContent;
	}

}
 

    后台关键处理代码,用于转发消息并缓存聊天记录

package com.test.chat.controller;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.alibaba.fastjson.JSONObject;
import com.test.chat.model.ChatMessage;
import com.test.chat.model.LimitQueue;
import com.test.chat.model.VideoMessage;
import com.test.framework.common.SessionContainer;
import com.test.framework.service.GenericService;
import com.test.framework.utils.DateUtil;

@Controller
public class UserChatController {
        //每个聊天室缓存最大聊天信息条数,该值由SpringMVC的配置文件注入,超过该值将清理出缓存
	private int MAX_CHAT_HISTORY;

	public void setMAX_CHAT_HISTORY(int MAX_CHAT_HISTORY) {
		this.MAX_CHAT_HISTORY = MAX_CHAT_HISTORY;
	}

	@Resource
	private GenericService genericService;
	// 用于转发数据 sendTo
	private SimpMessagingTemplate template;
        //消息缓存列表
        private Map msgCache = new HashMap();

	@Autowired
	public UserChatController(SimpMessagingTemplate t) {
		template = t;
	}


	/**
	 * WebSocket聊天的相应接收方法和转发方法
	 * 客户端通过app/userChat调用该方法,并将处理的消息发送客户端订阅的地址
	 * @param userChat     关于用户聊天的各个信息
	 */
	@MessageMapping("/userChat")
	public void userChat(ChatMessage chatMessage) {
		// 找到需要发送的地址(客户端订阅地址)
		String dest = "/userChat/chat" + chatMessage.getRoomid();
		// 获取缓存,并将用户最新的聊天记录存储到缓存中
		Object cache = msgCache.get(chatMessage.getRoomid());
		try {
			chatMessage.setRoomid(URLDecoder.decode(chatMessage.getRoomid(),"utf-8"));
			chatMessage.setUserName(URLDecoder.decode(chatMessage.getUserName(), "utf-8"));
			chatMessage.setDeptName(URLDecoder.decode(chatMessage.getDeptName(), "utf-8"));
			chatMessage.setChatContent(URLDecoder.decode(chatMessage.getChatContent(), "utf-8"));
			chatMessage.setIsSysMsg(URLDecoder.decode(chatMessage.getIsSysMsg(),"utf-8"));
			chatMessage.setCurTime(DateUtil.format(new Date(),DateUtil.formatStr_yyyyMMddHHmmss));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace(); 
		}
		// 发送用户的聊天记录
		this.template.convertAndSend(dest, chatMessage);
                ((LimitQueue) cache).offer(chatMessage);
	}

	
	@SubscribeMapping("/initChat/{roomid}")
	public LimitQueue initChatRoom(@DestinationVariable String roomid) {
		System.out.print("-------新用户进入聊天室------");
		LimitQueue chatlist = new LimitQueue(MAX_CHAT_HISTORY);
		// 发送用户的聊天记录
		if (!msgCache.containsKey(roomid)) {
			// 从来没有人进入聊天空间
			msgCache.put(roomid, chatlist);
		} else {
			chatlist = (LimitQueue) msgCache.get(roomid);
		}
		return chatlist;
	}
	
}
在Spring的配置文件中注入MAX_CHAT_HISTRORY
    
        
           

其中缓存队列LimitQueue的实现为:
package com.test.chat.model;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;

public class LimitQueue implements Queue {
	private int limit;
	private Queue queue;

	public LimitQueue(int limit) {
		this.limit = limit;
		this.queue = new LinkedList();
	}

	@Override
	public int size() {
		return queue.size();
	}

	@Override
	public boolean isEmpty() {
		return queue.isEmpty();
	}

	@Override
	public boolean contains(Object o) {
		return queue.contains(o);
	}

	@Override
	public Iterator iterator() {
		return queue.iterator();
	}

	@Override
	public Object[] toArray() {
		return queue.toArray();
	}

	@Override
	public  T[] toArray(T[] a) {
		return queue.toArray(a);
	}

	@Override
	public boolean add(E e) {
		return queue.add(e);
	}

	@Override
	public boolean remove(Object o) {
		return queue.remove(0);
	}

	@Override
	public boolean containsAll(Collection c) {
		return queue.containsAll(c);
	}

	@Override
	public boolean addAll(Collection c) {
		return queue.addAll(c);
	}

	@Override
	public boolean removeAll(Collection c) {
		return queue.removeAll(c);
	}

	@Override
	public boolean retainAll(Collection c) {
		return queue.retainAll(c);
	}

	@Override
	public void clear() {
		queue.clear();
	}

	@Override
	public boolean offer(E e) {
		if (queue.size() >= limit) {
			queue.poll();
		}
		return queue.offer(e);
	}

	@Override
	public E remove() {
		return queue.remove();
	}

	@Override
	public E poll() {
		return queue.poll();
	}

	@Override
	public E element() {
		return queue.element();
	}

	@Override
	public E peek() {
		return queue.peek();
	}

	public int getLimit() {
		return this.limit;
	}
}

         四、前台聊天室的实现(前台界面使用dhtmlx控件)

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>


	
		聊天室管理
		 
		
		 
		
		
		
		
		
		
		  
	                     
	     
     	
       五、实现效果

     创建人tester4进入后,输入聊天内容后,退出。web即时通信2--基于Spring websocket实现web聊天室_第1张图片


     聊天室好友tester1进入并发言

web即时通信2--基于Spring websocket实现web聊天室_第2张图片
   文中代码部分参考了Spring WebSocket教程(一)Spring WebSocket教程(二)。

     下一篇文章将讲解在此网页聊天室上添加视频聊天功能。

你可能感兴趣的:(web开发)