天天看點

即時通訊QQ,微信,好友聊天,群聊天 實作,關于websocket實作點對點好友聊天及群聊天的實作,基于springMVC架構,前端是安卓用戶端 源碼

基于springMVC

1.先修改pom檔案,添加websocket依賴包

即時通訊QQ,微信,好友聊天,群聊天 實作,關于websocket實作點對點好友聊天及群聊天的實作,基于springMVC架構,前端是安卓用戶端 源碼

依賴包

2.配置XML 攔截器

即時通訊QQ,微信,好友聊天,群聊天 實作,關于websocket實作點對點好友聊天及群聊天的實作,基于springMVC架構,前端是安卓用戶端 源碼

bean需要導入對應的包,否則下面的标簽會報錯

即時通訊QQ,微信,好友聊天,群聊天 實作,關于websocket實作點對點好友聊天及群聊天的實作,基于springMVC架構,前端是安卓用戶端 源碼

攔截器類,指向自己攔截器類所在的位置,準備工作做完,開始貼背景代碼了

3.JAVA背景實作部分代碼

先建立一個websocket’config類,指定自己需要攔截的websocket位址,根據自己的實際情況而定,包含了websocket請求的重定向功能,

/**
 * 配置類
 * @author k.li
 *
 */
@Configuration
@EnableWebMvc
@EnableWebSocket
@Service
public class SpringWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
	 @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		 
		 // "/mcloud/cloudclub/cloudService/websocket/*",
		 //允許連接配接的域,隻能以http或https開頭
	        String[] allowsOrigins = {"http://www.xxx.com"};
		//WebIM WebSocket通道 
	     String[] path = new String[]{
	    		 "/websocket",
	    		 "/meicloud/test/*"
	     };
        registry.addHandler(webSocketHandler(),path).addInterceptors(new SpringWebSocketHandlerInterceptor())
        .addInterceptors(interceptors()).setAllowedOrigins("*");
        
     
        
       
        /*registry.addHandler(chatWebSocketHandler(),"/webSocketIMServer").setAllowedOrigins(allowsOrigins).addInterceptors(myInterceptor());
        registry.addHandler(chatWebSocketHandler(), "/sockjs/webSocketIMServer").setAllowedOrigins(allowsOrigins).addInterceptors(myInterceptor()).withSockJS();*/
        
    }
    @Bean
    public TextWebSocketHandler webSocketHandler(){
        return new SpringWebSocketHandler();
    }
    
    @Bean
    
    public HttpSessionHandshakeInterceptor interceptors(){
    	return new SpringWebSocketHandlerInterceptor();
    
    }

           

定義攔截器類,使用websocket應該知道,首先他需要和HTTP一樣進行三次握手,不懂的先百度,然後在進行websocket通訊,發送點對點消息資料,這個根據自己的業務,比如你需要攔截使用者登入session,取使用者名密碼,也可以在這個類做,具體代碼如下

import java.util.Map;
import javax.servlet.http.HttpSession;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

/**
 * websocket 攔截器定義
 * @author k.li
 *
 */
public class SpringWebSocketHandlerInterceptor extends HttpSessionHandshakeInterceptor {
	
	@Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
            Map<String, Object> attributes) throws Exception {
       //根據具體業務進行攔截
       if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
            HttpSession session = servletRequest.getServletRequest().getSession(false);
            HttpHeaders headers = request.getHeaders();
            if(headers.get("username") != null){
            	 attributes.put("meiid",headers.get("useranme"));
            	
            }
       	if(!attributes.containsKey("username") ||!attributes.containsKey("meiid") ||!attributes.containsKey("meiid") ){
       		return false;
       	}else{
       		return super.beforeHandshake(request, response, wsHandler, attributes);
       	}
        
      	
        //return false;
    }

	// 握手後
	@Override
	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
			Exception ex) {			
		super.afterHandshake(request, response, wsHandler, ex);

	}
	

           

好了。下面到最關鍵的消息處理類,我是根據我自己的業務寫的,你們需要添加自己的業務進去,地方我會注釋說明,搬代碼要會靈活用用

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.socket.CloseStatus;

import org.springframework.web.socket.TextMessage;

import org.springframework.web.socket.WebSocketSession;

import org.springframework.web.socket.handler.TextWebSocketHandler;

import com.mbk.mcloud.model.GroupUserMsg;

import com.mbk.mcloud.model.MyFriendMsg;

import com.mbk.mcloud.model.MyFriendVerify;

import com.mbk.mcloud.model.User;

import com.mbk.mcloud.service.GroupService;

import com.mbk.mcloud.service.MyFriendService;

import net.sf.json.JSONObject;

public class SpringWebSocketHandler extends TextWebSocketHandler {

@Autowired
private MyFriendService myFriendService;

@Autowired
private GroupService groupService;
private static Logger logger = LoggerFactory.getLogger(SpringWebSocketHandler.class);
public static int onlineNumber = 0;

private static final ConcurrentHashMap<String, WebSocketSession> users; // Map來存儲WebSocketSession,key用USER_ID
															// 即線上使用者清單
// 儲存使用者ID
private static ConcurrentHashMap<String, String> sessionMap = new ConcurrentHashMap<String, String>();

static {
	users = new ConcurrentHashMap<String, WebSocketSession>();
}
MyFriendMsg msg = new MyFriendMsg();

public SpringWebSocketHandler() {
}

/**
 * 連接配接成功時候,會觸發頁面上onopen方法
 */
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
	
	//連接配接
	logger.info("send msg!!!!!!!!!!!!!!!!!!!!!!!!!!!");
	// 驗證使用者名密碼,驗證通過則建立連接配接開始通訊,否則關閉連接配接
	if (session.getAttributes().containsKey("meiid") && session.getAttributes().containsKey("pwd")
			&& session.getAttributes().containsKey("device_id")) {
		String meiid = session.getAttributes().get("meiid").toString();
		String pwd = session.getAttributes().get("pwd").toString();
		String device_id = session.getAttributes().get("device_id").toString();
		boolean isAuth = isUserPwdAuth(meiid, pwd);
		if (isAuth) {
			logger.info("websocket connect success!");
		
			session.sendMessage(new TextMessage("webscoket connect success!"));
			Map<String, Object> dMap = new HashMap<>();
			meiid = meiid.substring(1, meiid.length() - 1);
			device_id = device_id.substring(1, device_id.length() - 1);
			String key = meiid + "_" + device_id;
			users.put(key, session);
			System.out.println(" 目前線上人數:" + users.size());
			// 儲存登入裝置資訊
			dMap.put("meiid", meiid);
			dMap.put("device_id", device_id);
			// 先判斷資料庫有有沒有該條記錄
			try {
				logger.info(" groupService ExistDevice mothed start.....");
				int isT = groupService.ExistDevice(dMap);
				logger.info(" groupService ExistDevice mothed end......");
				// 沒查到儲存 查到了不儲存
				if (isT == 0) {
					logger.info(" groupService addLoginDevice mothed start.....");
					groupService.addLoginDevice(meiid, device_id);
					logger.info(" groupService addLoginDevice mothed end.....");
				}
			} catch (Exception e) {
				e.getMessage();
				e.printStackTrace();
				logger.error("addLoginDevice mothed error! {}", e.getStackTrace().toString());
			}

			// 判斷自己有沒有離線好友消息(需要知道使用者的meiid)

			try {
				List<MyFriendMsg> offLineMsg = isOffLineMsg(meiid);
				// 判斷有沒有好友離線消息
				if (offLineMsg.size() > 0) {
					// 發送離線消息給自己
					for (MyFriendMsg msg : offLineMsg) {
						if (msg.getDeviceId().equals(device_id)) {
							Map<String, Object> map = new HashMap<>();
							msg.setMsgStatus(0);
							// 修改狀态
							logger.info("updateOffLineMsgByMsgRecMeiId mothed start.........");
							// 更新好友消息狀态
							myFriendService.updateOffLineMsgByMsgRecMeiId(msg);
							logger.info("updateOffLineMsgByMsgRecMeiId mothed end.........");
							map.put("id", msg.getId());
							map.put("targer_type", "0");
							map.put("meiid", meiid);
							map.put("targer_id", msg.getMsgSendMeiId());
							map.put("msg_type", msg.getMsgType());
							map.put("msg_content", msg.getMsgText());
							map.put("msg_time", msg.getMsgSendTime().getTime());
							map.put("function", "40001");
							session.sendMessage(new TextMessage(JSONObject.fromObject(map).toString()));
						}
					}
				}

			} catch (Exception e) {
				e.getMessage();
				e.printStackTrace();
				logger.error("updateOffLineMsgByMsgRecMeiId mothed error {}", e.getStackTrace().toString());
			}
			// 離線群消息
			try {
				// 判斷自己有沒有離線群消息
				List<GroupUserMsg> offLineGroupMsg = isOffLineGroupMsg(meiid);
				if (offLineGroupMsg.size() > 0) {
					for (GroupUserMsg msg : offLineGroupMsg) {
						if (msg.getDeviceId().equals(device_id)) {
							Map<String, Object> map = new HashMap<>();
							msg.setStatus(0);
							// 修改消息狀态
							logger.info("updateGroupUserMsg mothed start.........");
							groupService.updateGroupUserMsg(msg);
							logger.info("updateGroupUserMsg mothed end.........");
							map.put("id", msg.getId());
							map.put("targer_type", "1");
							map.put("meiid", meiid);
							map.put("targer_id", msg.getGroupId());
							map.put("msg_content", msg.getMsgContent());
							map.put("msg_time", msg.getSendTime().getTime());
							map.put("status", msg.getStatus());
							map.put("function", "40001");
							session.sendMessage(new TextMessage(JSONObject.fromObject(map).toString()));
							
						}

					}
				}
			} catch (Exception e) {
				e.getMessage();
				e.printStackTrace();
				logger.error("updateGroupUserMsg mothed error {}", e.getStackTrace().toString());
			}
			try {					
				// 判斷好友申請表有沒有我的離線消息
				List<MyFriendVerify> verifyList = isOffLineMyFriendVerify(meiid);
				if (verifyList.size() > 0) {
					for (MyFriendVerify mf : verifyList) {
						if (mf.getDeviceId().equals(device_id)) {
							mf.setIsSend(0);
							logger.info("updateMyFriendVerifyStatus mothed start.........");
							myFriendService.updateMyFriendVerifyStatus(mf);
							logger.info("updateMyFriendVerifyStatus mothed start.........");
							logger.info("selectToUserTabelById mothed start.........");
							User user = myFriendService.selectToUserTabelById(String.valueOf(mf.getFriendMeiId()));
							logger.info("selectToUserTabelById mothed start.........");
							Map<String, Object> msg = new HashMap<>();
							// 組裝資料
							if (user.getEmail() != null) {
								msg.put("userid", user.getEmail());
							}
							if (user.getMobile() != null) {
								msg.put("userid", user.getMobile());
							}
							if (user.getFaceUrl() != null) {
								msg.put("faceurl", user.getFaceUrl());
							} else {
								msg.put("faceurl", "");
							}
							if (user.getUserName() != null) {
								msg.put("username", user.getUserName());
							} else {
								msg.put("username", "");
							}
							msg.put("id", mf.getId());
							msg.put("meiid", mf.getFriendMeiId());
							msg.put("friend_meiid", mf.getMeiId());
							msg.put("create_time", mf.getCreateTime().getTime());
							msg.put("status", mf.getStatus());
							msg.put("function", "40002");
							session.sendMessage(new TextMessage(JSONObject.fromObject(msg).toString()));

						}
					}
				}

			} catch (Exception e) {
				e.getMessage();
				e.printStackTrace();
				logger.error("get friends verify mothed error {}", e.getStackTrace().toString());
			}

		} else {
			session.close();
			logger.error("websocket connect error! message {}!", "使用者名密碼參數驗證失敗!");
		}
	} else {
		session.close();
		logger.error("websocket connect error! message {}!", "使用者名密碼參數驗證失敗!");
	}

}

/**
 * 關閉連接配接時觸發
 */
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
	try {
		logger.info("closed websocket start .......");
		String meiid = session.getAttributes().get("meiid").toString();
		meiid = meiid.substring(1, meiid.length() - 1);
		String device_id = session.getAttributes().get("device_id").toString();
		device_id = device_id.substring(1, device_id.length() - 1);
		String key = meiid + "_" + device_id;
		users.remove(key);
		System.out.println("剩餘線上使用者" + users.size());
		logger.info("closed websocket end .......");
	} catch (Exception e) {
		e.getMessage();
		e.printStackTrace();
		logger.error("closed websocket error!{}",e.getStackTrace().toString());
	}
	
	super.afterConnectionClosed(session, closeStatus);
	// System.out.println(i);
}

/**
 * 關閉連接配接異常處理
 */
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
	try {
		logger.info("websocket error close start...........");
		if (session.isOpen()) {
			session.close();
		}
		String meiid = session.getAttributes().get("meiid").toString();
		meiid = meiid.substring(1, meiid.length() - 1);
		String device_id = session.getAttributes().get("device_id").toString();
		device_id = device_id.substring(1, device_id.length() - 1);
		String key = meiid + "_" + device_id;
		users.remove(key);
		logger.info("websocket error close end ...........");
	} catch (Exception e) {
		e.getMessage();
		e.printStackTrace();
		logger.error("websocket error close error{}" ,e.getStackTrace().toString());
	}
	
	
}

// 判斷自己有沒有離線消息
public List<MyFriendMsg> isOffLineMsg(String meiid) {

	return myFriendService.OffLineMsgByMsgRecMeiId(meiid);

}

// 判斷自己有沒有離線群消息
public List<GroupUserMsg> isOffLineGroupMsg(String meiid) {
	// 查詢離線群消息
	return groupService.isOffLineGroupMsg(meiid);

}

// 判斷自己有沒有離線的好友申請消息
public List<MyFriendVerify> isOffLineMyFriendVerify(String meiid) {
	// 查詢離線群消息
	return groupService.isOffLineMyFriendVerify(meiid);

}

/*
 * 驗證使用者名密碼
 */
public boolean isUserPwdAuth(String meiid, String pwd) {
	boolean isAuth = false;
	try {
		logger.info("isUserPwdAuth mothed start.............");
		meiid = meiid.substring(1, meiid.length() - 1);
		pwd = pwd.substring(1, pwd.length() - 1);
		List<User> list = myFriendService.isUserPwdAuth(meiid, pwd);
		logger.info("isUserPwdAuth mothed end.............");
		if (list.size() > 0) {
			isAuth = true;
		}
	} catch (Exception e) {
		e.getMessage();
		e.printStackTrace();
		logger.error("isUserPwdAuth error {}",e.getStackTrace().toString());
	}
	
	return isAuth;
}

/**
 * js調用websocket.send時候,會調用該方法
 */
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
	super.handleTextMessage(session, message);
}

public boolean supportsPartialMessages() {
	return false;
}

/**
 * 給某個使用者發送消息
 *
 * @param userName
 * @param message
 */
public boolean sendMessageToUser(String meiid, TextMessage message) {
	
	//TODO 擷取隊列消息
		
	boolean isStatus = false;
	for (String s : users.keySet()) {
		if (s.equals(meiid)) {
			try {
				if (users.get(s).isOpen()) {
					users.get(s).sendMessage(message);
					isStatus = true;
				}
			} catch (Exception e) {
				e.getMessage();
				e.getStackTrace();
				logger.error("sendFriendMsg error {}{}",e.getMessage(),e.getStackTrace().toString());
			}
			break;
		}

	}
	return isStatus;
}

// 群發消息
public boolean sendMessageToGroup(String meiid, TextMessage message) {
	// 檢視成員在不線上
	boolean isStatus = false;
	for (String s : users.keySet()) {
		if (s.equals(meiid)) {
			try {
				if (users.get(s).isOpen()) {
					users.get(s).sendMessage(message);
					isStatus = true;
				}
			} catch (Exception e) {
				e.getMessage();
				e.getStackTrace();
				logger.error("sendGroupMsg error {}{}",e.getMessage(),e.getStackTrace().toString());
			}
			break;
		}

	}
	return isStatus;

}
           

}

這是我的消息處理類,然後我們到controller層實作消息發送,具體看代碼,這是單對單好友消息發送

需要注意的是發送的key是我自己組裝的,你們要根據具體的業務去自己組裝,

@RequestMapping(value = "/sendFriend", produces = { "application/json;charset=UTF-8" })
	@ResponseBody
	public String sendFriend(HttpServletRequest request, HttpServletResponse response) {
		
		String meiid = StringUtil.trim(jsonObj.getString("meiid"));
		String friend_meiid = StringUtils.trim(jsonObj.getString("friend_meiid"));
		String msg_content = StringUtils.trim(jsonObj.getString("msg_content"));
		String msg_type = StringUtils.trim(jsonObj.getString("msg_type"));
		// 新增裝置id
	
	
		try {
				Map<String, Object> loginMap = new HashMap<>();
					// 儲存消息 次數(根據登入裝置決定)
					map.put("id", id);
					map.put("targer_type", "0");
					map.put("meiid", meiid);
					map.put("targer_id", friend_meiid);
					map.put("msg_content", msg.getMsgText());
					map.put("msg_time", msg.getMsgSendTime().getTime());
				//keyz指向的是自己websocket的ID,我這裡是自己組裝的
					String key = friend_meiid + "_" + ld.getDevice();
					boolean isTrue = infoHandler().sendMessageToUser(key,
							new TextMessage(JSONObject.fromObject(map).toString()));
					if (!isTrue) {
						msg.setMsgStatus(1);
						myFriendService.updateOffLineMsgByMsgRecMeiId(msg);
					}
				


			// 發送測試資料
			retMap.put("code", SysConstants.RET_CODE_SUCCESS);
			retMap.put("message", "成功");
			return jsonFmt(retMap, logger, commonparamVo);

		} catch (Exception e) {
			retMap.put("code", SysConstants.RET_CODE_999);
			retMap.put("message", "資料庫讀取異常");
			e.printStackTrace();
			return jsonFmt(retMap, logger, commonparamVo);
		}

	}

           

群消息發送

@RequestMapping(value = “/sendgroup”, produces = { “application/json;charset=UTF-8” })

@ResponseBody

public String sendgroup(HttpServletRequest request, HttpServletResponse response) throws Exception {

業務參數請求
	try {
		// 1選儲存一條群消息記錄
	
			
						// 組裝發送資料
						Map<String, Object> map = new HashMap<>();
						map.put("id", userMsgId);
						map.put("targer_type", "1");
						map.put("meiid", userMsg.getRecMeiid());
						map.put("targer_id", group_id);
						map.put("msg_content", msg_content);
						map.put("msg_type", msg.getGroupMsgType());
						map.put("msg_time", msg.getSendTime().getTime());
						String key = ids + "_" +    ld.getDevice();
						// 給群成員好友挨着發送消息
						if (!String.valueOf(userMsg.getSendMeiid()).equals(String.valueOf(userMsg.getRecMeiid()))) {
							boolean isTrue = infoHandler().sendMessageToGroup(key,
									new TextMessage(JSONObject.fromObject(map).toString()));
							if (!isTrue) {
								userMsg.setStatus(1);
								// 如果群成員不線上,就修改消息狀态
								groupService.updateGroupUserMsg(userMsg);
							}
						}

					}

				}

			}
		}
		retMap.put("code", SysConstants.RET_CODE_SUCCESS);
		retMap.put("message", "成功");
		return jsonFmt(retMap, logger, commonparamVo);

	} catch (Exception e) {
		retMap.put("code", SysConstants.RET_CODE_999);
		retMap.put("message", "資料庫讀取異常");
		e.printStackTrace();
		return jsonFmt(retMap, logger, commonparamVo);
	}

}
           

到這裡完整背景代碼就實作了,在附送一個前端測試連接配接的代碼

修改WS連接配接位址即可直接使用,我當時是測試wensocket連接配接數加的一個循環

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
	String ppp = request.getServerName() + ":" + request.getServerPort() + path + "/";	
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>" target="_blank" rel="external nofollow" >
<title>My JSP 'MyJsp.jsp' starting page</title>

<script src="baseui/js/plugins/jquery.js"></script>
</head>
<script>
	function test() {
		//每秒重新整理一次 
	
	var ws = new WebSocket("ws://XXXXXXXXXXXXXX/websocket");

		ws.onopen = function(evt) {
			console.log("Connection open ...");
			ws.send("Hello WebSockets!");
		};

		ws.onmessage = function(evt) {
			console.log("Received Message: " + evt.data);
			//ws.close();
		};

		 ws.onclose = function(evt) {
			console.log("Connection closed.");
		}; 
		
	}
	
	
	var c=0;
	function showLogin()
	{
		test();
	console.log(c++);
	}
	setInterval("showLogin()","1000");
</script>


<body>
	
		
	
	<button type="button" onclick="showLogin()">連接配接websocket</button>
  
	<div id="msgcount"></div>
</body>
</html>
           

好了,文檔分享到這,有問題或者疑問,可以随時留言!!!