【解码器跟编码器】为了可以直接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的更多相关文章

  1. workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的)

    workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的) 一.总结 1.下面链接里面还有一个来聊的php聊天室源码可以学习 2. ...

  2. 基于websocket实现的一个简单的聊天室

    本文是基于websocket写的一个简单的聊天室的例子,可以实现简单的群聊和私聊.是基于websocket的注解方式编写的.(有一个小的缺陷,如果用户名是中文,会乱码,不知如何处理,如有人知道,请告知 ...

  3. 基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(二)

    我们上一篇<基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)>主要讲解了如何搭建一个实时数据通讯服务器,客户端与服务端是如何通讯的,相信通过上一篇的讲解,再配 ...

  4. 高效简易开发基于websocket 的通讯应用

    websocket的主要是为了解决在web上应用长连接进行灵活的通讯应用而产生,但websocket本身只是一个基础协议,对于消息上还不算灵活,毕竟websocket只提供文本和二进制流这种基础数据格 ...

  5. Socket.IO – 基于 WebSocket 构建跨浏览器的实时应用

     Socket.IO 是一个功能非常强大的框架,能够帮助你构建基于 WebSocket 的跨浏览器的实时应用.支持主流浏览器,多种平台,多种传输模式,还可以集合 Exppress 框架构建各种功能复杂 ...

  6. python测试基于websocket协议的即时通讯接口

    随着html5的广泛应用,基于websocket协议的即时通讯有了越来越多的使用场景,本文使用python中的websocket-client模块来做相关的接口测试 import webclient ...

  7. 基于 WebSocket 构建跨浏览器的实时应用

    Socket.IO – 基于 WebSocket 构建跨浏览器的实时应用 Socket.IO 是一个功能非常强大的框架,能够帮助你构建基于 WebSocket 的跨浏览器的实时应用.支持主流浏览器,多 ...

  8. 分享基于 websocket 网页端聊天室

    博客地址:https://ainyi.com/67 有一个月没有写博客了,也是因为年前需求多.回家过春节的原因,现在返回北京的第二天,想想,应该也要分享技术专题的博客了!! 主题 基于 websock ...

  9. 第一节:.Net版基于WebSocket的聊天室样例

    一. 说在前面的话 该篇文章为实时通讯系列的第一节,基于WebSocket编写了一个简易版聊天样例,主要作用是为引出后面SignalR系列的用法及其强大方便之处,通过这个样例与后续的SignalR对比 ...

随机推荐

  1. thinkphp5中__PUBLIC__的使用

    在使用thinkphp5.1开发的时候遇到设置__PUBLIC__无法生效的问题.这次的版本升级有比较大的改动,很多写法已经被更改,下面说下怎么去解决这个问题. 工具/原料   phpstorm ln ...

  2. 【转】MySQL常见错误代码及代码说明参考

    Mariadb文档:https://mariadb.com/kb/zh-cn/mariadb/ MySQL文档:https://dev.mysql.com/doc/refman/8.0/en/ 100 ...

  3. [SDOI2011]计算器(exgcd&BSGS)

    k=1:裸的快速幂k=2:xy=z+kp,直接exgcd,这个可以不用解释了,不懂的同学可以看代码 k=3:裸的BSGS 重点是k=3(BSGS学习)ax=b(mod p)求解这个同余方程只能求gcd ...

  4. eCharts使用图表简单示例

    https://blog.csdn.net/hlbt0112/article/details/48862427 1. eCharts官网 http://echarts.baidu.com/index. ...

  5. 斯坦福大学公开课机器学习: machine learning system design | prioritizing what to work on : spam classification example(设计复杂机器学习系统的主要问题及构建复杂的机器学习系统的建议)

    当我们在进行机器学习时着重要考虑什么问题.以垃圾邮件分类为例子.假如你想建立一个垃圾邮件分类器,看这些垃圾邮件与非垃圾邮件的例子.左边这封邮件想向你推销东西.注意这封垃圾邮件有意的拼错一些单词,就像M ...

  6. TestNg 7.依赖测试

    我本个测试方法执行的时候,依赖于其他的方法.用到关键字dependsOnmethods(依赖于那个方法)也有依赖于哪个组(dependsOnGroups). 看以下的一段代码: package com ...

  7. arm-fsl-linux-gnueabi交叉编译器安装

    系统:Ubuntu 14.04 64bit 编译器gcc version 4.4.4 (4.4.4_09.06.2010) 解压编译器到相应路径(注:当我解压放到/home/cross_compile ...

  8. mongoDB-Explain

    新版的MongoDB中的Explain已经变样了 Explain支持三种Mode queryPlanner Mode db.collection.explain() 默认mode是queryPlann ...

  9. vue 本地存储数据 sessionStorage

    在vuex 下的 action下的userAction.js中添加 export function login(from, self) { axPost('/api/login', from, fun ...

  10. java静态变量和final关键字

    静态变量其实就是全局变量 静态方法不需要实例化对象,直接可以调用. public class StaticVariable { public static int num1=20; public vo ...