【解码器跟编码器】为了可以直接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. [ZJOI2010]贪吃的老鼠(网络流+建图)

    题目描述 奶酪店里最近出现了m只老鼠!它们的目标就是把生产出来的所有奶酪都吃掉.奶酪店中一天会生产n块奶酪,其中第i块的大小为pi,会在第ri秒被生产出来,并且必须在第di秒之前将它吃掉.第j只老鼠吃 ...

  2. 区块链使用Java,以太坊 Ethereum, web3j, Spring Boot

    Blockchain is one of the buzzwords in IT world during some last months. This term is related to cryp ...

  3. Building Microservices with Spring Boot and Apache Thrift. Part 2. Swifty services

    http://bsideup.blogspot.com/2015/04/spring-boot-thrift-part2.html     In previous article I showed y ...

  4. OpenLayers学习笔记(六)— 拖拽叠加层overlayer

    是在官网例子基础上增加的拖拽功能 GitHub:八至 作者:狐狸家的鱼 本文链接:拖拽叠加层overlayer 全部代码 <!DOCTYPE html> <html> < ...

  5. codeforces Hello 2019(未写完)

    A. Gennady and a Card Game a题惯例签到题 题意:给你一张牌,再给你5张牌,判断能不能出一次牌... 所以只要检查第二行中的某个卡是否与第一行中的卡具有共同字符 有就输出YE ...

  6. 【SPOJ116】Intervals

    题目大意:有 N 个区间,在区间 [a, b] 中至少取任意互不相同的 c 个整数.求在满足 N 个区间约束的情况下,至少要取多少个正整数. 题解:差分约束系统模板题. 差分约束系统是对于 N 个变量 ...

  7. APP reset.css

    html { box-sizing: border-box; } * { user-select: none; -webkit-tap-highlight-color: rgba(255,255,25 ...

  8. Django 分页器的使用

    Django 分页器的使用 Django作为Python Web开发框架的一哥,提供了企业级网站开发所需要的几乎所有功能,其中就包括自带分页功能.利用Django自带的Paginator类,我们可以很 ...

  9. django 分类搜索(根据不同的单选框,改变form提交的地址)

    前端html部分form <form id="searchform" action="#" method="get" class=&q ...

  10. 配置ssl

    1.配置 <Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000&q ...