【解码器跟编码器】为了可以直接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. 【php】php实现数组反转

    php里面有个函数可以反转数组,工作中也经常用到,非常方便.今天来自己实现这样的功能. $arr = [2,5,6,1,8,16,12]; function reverse($arr){ $left ...

  2. Python 分布式进程

    #-*-coding:utf-8-*- '''分布式进程指的是将Process进程分不到多台机器上,充分利用多台机器的性能完成复杂的任务''' #服务器端 #--------------------- ...

  3. InnoDB,5项最佳实践,知其所以然?

    InnoDB,5项最佳实践,知其所以然? 原创: 58沈剑 架构师之路 昨天 缓存讲了一个月<缓存架构,一篇足够>.今天,开始写数据库. 第一篇,说说MySQL两个最常用的存储引擎,MyI ...

  4. COGS 2392 2393 2395 有标号的二分图计数

    有黑白关系: 枚举左部点(黑色点),然后$2^{i*(n-i)}$处理方法同:COGS 2353 2355 2356 2358 有标号的DAG计数 无关系: 发现,假设$f(i)$是一个连通块,对于一 ...

  5. Django(十一)请求生命周期之CBV与FBV

    https://www.cnblogs.com/yuanchenqi/articles/8715364.html FBV FBV(function base views) 就是在视图里使用函数处理请求 ...

  6. mysql建立索引的几大原则

    (转)仅供自己学习,特此记录 1.选择唯一性索引 唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录.例如,学生表中学号是具有唯一性的字段.为该字段建立唯一性索引可以很快的确定某个学生的信息 ...

  7. plink:ped格式转换为bed格式

    命令行如下: plink --file FILENAME --make-bed --out FILENAME 第一个FILENAME的后缀为.ped和.map,生成的第二个FILENAME的后缀为.b ...

  8. 第三节,使用OpenCV 3处理图像(模糊滤波、边缘检测)

    一 不同色彩空间的转换 OpenCV中有数百种关于在不同色彩空间之间转换的方法.当前,在计算机中有三种常用的色彩空间:灰度,BGR以及HSV(Hue,Saturation,Value). 灰度色彩空间 ...

  9. Linux下学习摄像头使用

    刚接触Linux硬件驱动有关的项目,配置摄像头经历的一些操作 (这篇文章是刚接触Linux下V4L时作为记录记下的,感觉只有几个命令还有参考作用) 一 确定摄像头种类 确定是否符合UVC标准协议,一般 ...

  10. java 中二维数组的定义和遍历

    定义格式 * a 第一种定义格式: * int[][] arr = new int[3][4];// arr里面包含3个数组 每个数组里面有四个元素 * 上面的代码相当于定义了一个3*4的二维数组,即 ...