基于WebSocket 私聊、ws_session、httpsession
【解码器跟编码器】为了可以直接sendObject
解码 => 解成计算机需要的码 => 将用户输入的文本或者二进制 序列化成消息对象。 (dll 给机器吃的)
编码 => 编成用户需要的码 => 将消息对象 反序列化成 文本或者二进制。(txt 给用户吃的)
public class ChatMessageCodec
implements Encoder.BinaryStream<ChatMessage>,
Decoder.BinaryStream<ChatMessage>
{
//Jackson readValue=>解码 writeValue=>编码
private static final ObjectMapper MAPPER = new ObjectMapper(); //JSON生成器默认配置是自动关闭的,也就是true
static {
MAPPER.findAndRegisterModules();
MAPPER.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
} //对象转换成JSON 写入OutputStream
@Override
public void encode(ChatMessage chatMessage, OutputStream outputStream)
throws EncodeException, IOException
{
try
{
ChatMessageCodec.MAPPER.writeValue(outputStream, chatMessage);
}
catch(JsonGenerationException | JsonMappingException e)
{
throw new EncodeException(chatMessage, e.getMessage(), e);
}
} //从InputStream JSON 反序列化成消息
@Override
public ChatMessage decode(InputStream inputStream)
throws DecodeException, IOException
{
try
{
return ChatMessageCodec.MAPPER.readValue(
inputStream, ChatMessage.class
);
}
catch(JsonParseException | JsonMappingException e)
{
throw new DecodeException((ByteBuffer)null, e.getMessage(), e);
}
}
}
【ServerEndpoint】Session(WS_SESSION) HttpSession(用户请求) ChatSession(聊天窗口) 都放在一处 协调。
//通过配置编码器和解码器,jackson自动 序列化、反序列化。
@ServerEndpoint(value = "/chat/{sessionId}",
encoders = ChatMessageCodec.class,
decoders = ChatMessageCodec.class,
configurator = ChatEndpoint.EndpointConfigurator.class)
@WebListener
public class ChatEndpoint implements HttpSessionListener
{
private static final String HTTP_SESSION_PROPERTY = "com.wrox.ws.HTTP_SESSION";
private static final String WS_SESSION_PROPERTY = "com.wrox.http.WS_SESSION";
private static long sessionIdSequence = 1L;
//对象锁 给序列号 自增使用
private static final Object sessionIdSequenceLock = new Object();
private static final Map<Long, ChatSession> chatSessions = new Hashtable<>();
private static final Map<Session, ChatSession> sessions = new Hashtable<>();
//HttpSession 倾向于请求 关联Session跟HttpSession
private static final Map<Session, HttpSession> httpSessions =
new Hashtable<>();
//等待加入的聊天会话 列表
public static final List<ChatSession> pendingSessions = new ArrayList<>(); //onOpen 客户端连接进来
@OnOpen
public void onOpen(Session session, @PathParam("sessionId") long sessionId)
{
//modifyHandshake方法负责put 到onOpen这里首先要检查
//HttpSession(http)是否和Session(tcp)关联
HttpSession httpSession = (HttpSession)session.getUserProperties()
.get(ChatEndpoint.HTTP_SESSION_PROPERTY);
try
{
if(httpSession == null || httpSession.getAttribute("username") == null)
{
session.close(new CloseReason(
CloseReason.CloseCodes.VIOLATED_POLICY,
"You are not logged in!"
));
return;
}
String username = (String)httpSession.getAttribute("username");
session.getUserProperties().put("username", username); ChatMessage message = new ChatMessage();
message.setTimestamp(OffsetDateTime.now());
message.setUser(username);
ChatSession chatSession;
if(sessionId < 1)//会话列表从1开始,小于1就是没有
{
message.setType(ChatMessage.Type.STARTED);
message.setContent(username + " started the chat session.");
//构造聊天窗口消息列表
chatSession = new ChatSession();
synchronized(ChatEndpoint.sessionIdSequenceLock)
{
chatSession.setSessionId(ChatEndpoint.sessionIdSequence++);
}
//首次加入的会话 是客户
chatSession.setCustomer(session);
chatSession.setCustomerUsername(username);
chatSession.setCreationMessage(message);
//因为首先加入的 所以要放到等待列表
ChatEndpoint.pendingSessions.add(chatSession);
//关联 聊天窗口消息列表 保存在ServerEndpoint。
ChatEndpoint.chatSessions.put(chatSession.getSessionId(),
chatSession);
}
else
{
//客户支持代表加入
message.setType(ChatMessage.Type.JOINED);
message.setContent(username + " joined the chat session.");
//路径参数 sessionId 获得聊天窗口会话
chatSession = ChatEndpoint.chatSessions.get(sessionId);
chatSession.setRepresentative(session);
chatSession.setRepresentativeUsername(username);
ChatEndpoint.pendingSessions.remove(chatSession);//从等待会话列表移除
//向客户发送消息
session.getBasicRemote()
.sendObject(chatSession.getCreationMessage());
session.getBasicRemote().sendObject(message);
}
//关联websocket session 和 chatSession
ChatEndpoint.sessions.put(session, chatSession);
//关联
ChatEndpoint.httpSessions.put(session, httpSession);
//httpSession 也放了一份 ws_session 发给表现层
this.getSessionsFor(httpSession).add(session);
//聊天记录
chatSession.log(message);
//向客户代表发送消息
chatSession.getCustomer().getBasicRemote().sendObject(message);
}
catch(IOException | EncodeException e)
{
this.onError(session, e);
}
}
//收到消息 负责把消息发送给两个客户端。
@OnMessage
public void onMessage(Session session, ChatMessage message)
{
//从WS_SESSION获得 当前session所在 聊天窗口会话消息列表
ChatSession c = ChatEndpoint.sessions.get(session);
//和当前session 关联 另一个session 。
Session other = this.getOtherSession(c, session);
if(c != null && other != null)
{
c.log(message);
try
{
session.getBasicRemote().sendObject(message);
other.getBasicRemote().sendObject(message);
}
catch(IOException | EncodeException e)
{
this.onError(session, e);
}
}
}
@SuppressWarnings("unchecked")
private synchronized ArrayList<Session> getSessionsFor(HttpSession session)
{
try
{
//稍后发到表现层 WS_SESSION
if(session.getAttribute(WS_SESSION_PROPERTY) == null)
session.setAttribute(WS_SESSION_PROPERTY, new ArrayList<>()); return (ArrayList<Session>)session.getAttribute(WS_SESSION_PROPERTY);
}
catch(IllegalStateException e)
{
return new ArrayList<>();
}
}
private Session getOtherSession(ChatSession c, Session s)
{
return c == null ? null :
(s == c.getCustomer() ? c.getRepresentative() : c.getCustomer());
} //handshake 握手
public static class EndpointConfigurator
extends ServerEndpointConfig.Configurator
{
@Override
public void modifyHandshake(ServerEndpointConfig config,
HandshakeRequest request,
HandshakeResponse response)
{
super.modifyHandshake(config, request, response); //做了一个配置
//获得了HttpSession ,就可以保证用户已经登录 config.getUserProperties().put(
ChatEndpoint.HTTP_SESSION_PROPERTY, request.getHttpSession()
);
}
}
基于WebSocket 私聊、ws_session、httpsession的更多相关文章
- workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的)
workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的) 一.总结 1.下面链接里面还有一个来聊的php聊天室源码可以学习 2. ...
- 基于websocket实现的一个简单的聊天室
本文是基于websocket写的一个简单的聊天室的例子,可以实现简单的群聊和私聊.是基于websocket的注解方式编写的.(有一个小的缺陷,如果用户名是中文,会乱码,不知如何处理,如有人知道,请告知 ...
- 基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(二)
我们上一篇<基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)>主要讲解了如何搭建一个实时数据通讯服务器,客户端与服务端是如何通讯的,相信通过上一篇的讲解,再配 ...
- 高效简易开发基于websocket 的通讯应用
websocket的主要是为了解决在web上应用长连接进行灵活的通讯应用而产生,但websocket本身只是一个基础协议,对于消息上还不算灵活,毕竟websocket只提供文本和二进制流这种基础数据格 ...
- Socket.IO – 基于 WebSocket 构建跨浏览器的实时应用
Socket.IO 是一个功能非常强大的框架,能够帮助你构建基于 WebSocket 的跨浏览器的实时应用.支持主流浏览器,多种平台,多种传输模式,还可以集合 Exppress 框架构建各种功能复杂 ...
- python测试基于websocket协议的即时通讯接口
随着html5的广泛应用,基于websocket协议的即时通讯有了越来越多的使用场景,本文使用python中的websocket-client模块来做相关的接口测试 import webclient ...
- 基于 WebSocket 构建跨浏览器的实时应用
Socket.IO – 基于 WebSocket 构建跨浏览器的实时应用 Socket.IO 是一个功能非常强大的框架,能够帮助你构建基于 WebSocket 的跨浏览器的实时应用.支持主流浏览器,多 ...
- 分享基于 websocket 网页端聊天室
博客地址:https://ainyi.com/67 有一个月没有写博客了,也是因为年前需求多.回家过春节的原因,现在返回北京的第二天,想想,应该也要分享技术专题的博客了!! 主题 基于 websock ...
- 第一节:.Net版基于WebSocket的聊天室样例
一. 说在前面的话 该篇文章为实时通讯系列的第一节,基于WebSocket编写了一个简易版聊天样例,主要作用是为引出后面SignalR系列的用法及其强大方便之处,通过这个样例与后续的SignalR对比 ...
随机推荐
- Linux的wget命令详解【转载】
Linux wget是一个下载文件的工具,它用在命令行下.对于Linux用户是必不可少的工具,尤其对于网络管理员,经常要下载一些软件或从远程服务器恢复备份到本地服务器.如果我们使用虚拟主机,处理这样的 ...
- Hello Object Oriented!
继计组之后,北航计算机学院又一大神课! 希望能以此为契机,和更多热爱技术的朋友们交流.让我们一起,共同进步~ [2019.4.27更新] 建立博客园的最初目的,是为了北航计算机学院OO课程设计的需要. ...
- JSP、EL、JSTL
JSP(Java Server Pages) 什么是JSP Java Server Pages(Java服务器端的页面) 使用JSP:SP = HTML + Java代码 + JSP自身的东西.执行J ...
- mui侧滑菜单"点击含有mui-action-menu类的控件"无法实现侧滑
.mui-action-menu 标题栏 菜单按钮 指定href="#id"显示与隐藏侧滑菜单 html: <div class="mui-off-canvas-w ...
- linux:逐行合并两文件(paste命令)
存在file1.txt 1 2 3 4 5 6 file2.txt a b c d e f 现希望生成file3.txt 1 2 a b 3 4 c d 5 6 e f 则可以用到如下命令: past ...
- nginx+keepalived高可用web负载均衡
一:安装环境 准备2台虚拟机,都安装好环境 centos 7keepalived:vip: 192.168.1.112192.168.1.110 nginxip 192.168.1.109 maste ...
- python 购物车小程序
python 购物车小程序 功能要求:1.启动程序后,输入用户名密码后,让用户输入工资,然后打印商品列表2.允许用户根据商品编号购买商品3.用户选择商品后,检测余额是否够,够就直接扣款,不够就提醒4. ...
- [DUBBO] Unexpected error occur at send statistic, cause: Forbid consumer 192.168.3.151 access servic
[DUBBO] Unexpected error occur at send statistic, cause: Forbid consumer 192.168.3.151 access servic ...
- docker swarm(当前官网示例)
介绍 Docker Swarm 是 Docker 公司推出的官方容器集群平台,基于 Go 语言实现 作为容器集群管理器,Swarm 最大的优势之一就是 100% 支持标准的 Docker API.各种 ...
- db mysql / mysql cluster 5.7.19 / my.cnf / thread_pool_stall_limit
s MYSQL Thread Pool简介 在MySQL5.5中,MySQL为每一个数据库连接创建一个线程,并分配其所需要的所有内存,当数据库连接数较大,或者有多个并发同时请求数据库连接时,服务器需要 ...