前言
上篇文章我们用STOMP子协议实现了在线群聊和一对一聊天室等功能,本篇我们继续WebSocket这个话题,这次我们换个实现维度:用原生的WebSocket来实现,看看这两者在实现上的差别有多大。
实战WebSocket的要点
一、WebSocket重要属性属性备注
Socket.readyState只读属性 readyState 表示连接状态,可以是以下值:
0 -表示连接尚未建立。
1 -表示连接已建立,可以进行通信。
2 -表示连接正在进行关闭。
3 -表示连接已经关闭或者连接不能打开。
Socket.bufferedAmount只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。
二、WebSocket核心事件事件事件处理程序备注
openSocket.onopen连接建立时触发
messageSocket.onmessage客户端接收服务端数据时触发
errorSocket.onerror通信发生错误时触发
closeSocket.onclose连接关闭时触发
三、WebSocket核心方法方法备注
Socket.send()使用连接发送数据
Socket.close()连接关闭
代码设计实现
一、服务端部分/**
*@authorandychen https://blog.51cto.com/14815984
*@description:WebSocket配置*/@Configurationpublic classWebSocketConfig {
/**
* 注册并开启WebSocket
*@return*/@BeanpublicServerEndpointExporter serverEndpointExporter(){
return newServerEndpointExporter();}
}/**
*@authorandychen https://blog.51cto.com/14815984
*@description:WebSocket通信业务类*/@ServerEndpoint("/ws/server")
@Componentpublic classWebSocketController {
private static finalLogger log= LoggerFactory.getLogger(WebSocketController.class);/**
* 服务端连接计数器
*/private static finalAtomicInteger counter= newAtomicInteger(0);/**
* 定义客户端会话安全容器
* 缓存客户端会话对象(正式环境,这里可以直接做分布式缓存)
*/private static finalCopyOnWriteArraySet sessionContainer= newCopyOnWriteArraySet<>();/**
* 定义客户端会话和用户身份映射安全容器
*/private static finalMap sessionMap= newConcurrentHashMap<>();/**
* 消息分隔字符窜
*/private static finalString MSG_SPLIT_STR= "@#@";/**
* 消息角色
*/private static finalString[] MSG_ROLES= {"sender","recevier"};/**
* WebSocket连接打开事件
*@paramsession客户端连接会话
*/@OnOpenpublic voidopen(Session session){
//缓存会话sessionContainer.add(session);//会话IdString sessionId = session.getId();
if(!sessionMap.containsKey(sessionId)){
String receiver = this.getRecevier(session);
booleanisMass = (null== receiver);//消息用户:群聊为发送者,单聊时为发送者和接收者String usrInfo = parseMsgParameter(session,MSG_ROLES[0]);
if(isMass){
sessionMap.put(sessionId,usrInfo);}else{
usrInfo += MSG_SPLIT_STR+receiver;sessionMap.put(usrInfo,sessionId);}
//发送新用户加入消息if(isMass){
sendMass("系统消息"+MSG_SPLIT_STR+"用户["+usrInfo+"]加入群聊");}
log.info("会话[{}]加入,当前连接数为:{}",sessionId,counter.incrementAndGet());}
}
/**
* 接收客户端消息事件
*@parammessage文本消息(也支持对象、二进制Buffer)
*@paramsession客户端连接会话
*/@OnMessagepublic voidaccept(String message,Session session){
String sender = null;String sessionId = session.getId();String sessionId2 = null;String msg =null;String recevier = getRecevier(session);
if(null== recevier){
msg = sessionMap.get(sessionId)+MSG_SPLIT_STR+message;sendMass(msg);}else{
sender = parseMsgParameter(session,MSG_ROLES[0]);msg = sender+MSG_SPLIT_STR+message;//发送者sessionIdsessionId = sender+MSG_SPLIT_STR+recevier;sessionId = sessionMap.get(sessionId);//接收者sessionIdsessionId2 = recevier+MSG_SPLIT_STR+sender;sessionId2 = sessionMap.get(sessionId2);sendSingle(sessionId,sessionId2,msg);}
log.info("已接收客户端[{}]消息:{},请求地址:{}",sessionId,message,session.getRequestURI().toString());}
/**
* 连接关闭事件
*@paramsession客户端连接会话
*/@OnClosepublic voidclose(Session session){
String sessionId = session.getId();sessionContainer.remove(session);String recevier =getRecevier(session);
if(null== recevier){
//群聊发送退群消息String sender = sessionMap.get(sessionId);sessionMap.remove(sessionId);sendMass("系统消息"+MSG_SPLIT_STR+"用户["+sender+"]退出群聊");}else{
sessionId = parseMsgParameter(session,MSG_ROLES[0])+MSG_SPLIT_STR+recevier;sessionId = sessionMap.get(sessionId);sessionMap.remove(sessionId);}
log.info("会话[{}]关闭连接,当前连接数为:{}",sessionId,counter.decrementAndGet());}
/**
* 连接发生错误事件
*@paramsession客户端连接会话
*@paramerror错误对象
*/@OnErrorpublic voiderror(Session session,Throwable error){
log.error("连接发生错误:{},\n\n客户端会话ID[{}],请求地址:{}",error.getMessage(),session.getId(),session.getRequestURI().toString());error.printStackTrace();}
/**
* 是否单聊
*@paramsession客户会话id
*@return*/privateString getRecevier(Session session){
returnparseMsgParameter(session,MSG_ROLES[1]);}
/**
* 解析消息参数
*@paramsession客户端会话
*@paramname参数名称
*@return*/private staticString parseMsgParameter(Session session,String name){
//获取会话中包含的参数信息Map> params = session.getRequestParameterMap();
if(params.containsKey(name)){
returnparams.get(name).get(0);}
return null;}
/**
* 发送消息
*@paramsession客户端会话
*@parammsg消息内容
*/private static booleansend(Session session,String msg){
try{
//异步转发文本消息(也可发送消息对象,二进制流等)session.getAsyncRemote().sendText(msg);
return true;} catch(Exception e) {
log.error("消息发送失败:{}",e.getMessage());e.printStackTrace();}
return false;}
/**
* 群发消息
*@parammsg消息内容
*/private static voidsendMass(String msg){
for(Session session : sessionContainer){
if(session.isOpen()){
//发送send(session,msg);}
}
}
/**
* 发送聊消息
*@paramsenderSid发送者会话id
*@paramrecevSid接收者会话id
*@parammsg消息内容
*/private static voidsendSingle(String senderSid,String recevSid,String msg){
String id = null;
intcount = 0;
for(Session s : sessionContainer) {
id = s.getId();
if(senderSid.equals(id)) {
count++;send(s,msg);}
if(recevSid.equals(id)) {
count++;send(s,msg);}
if(2== count){break;}
}
if(2> count){
log.warn("未找到指定会话[ID: {}或{}]",senderSid,recevSid);}
}
}
二、客户端部分
WebSocket在线聊天室选择你的网名:
请选择..
zhangsan
lisi
wangwu
zhaoliu
chenqi
qianba
群聊: