【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:2.技术简介之MinaFilter(1)
Filter
filter:过滤器?(不知道是不是这么翻译,算了知道意思就好了╮(╯▽╰)╭),这种东西在很多语言中都是有的,功能是在某两层之间插入一层,进行拦截加工处理,这样可以方便的添加和删除处理,并且可以添加多层的Fileter。
在Mina的Filter之中,filter是有序的,也就是说,根据你加入filter的方式不同,运行的顺序也是不同的,加入方法有点类似于链表。对Mina中Filter的详细使用请参考Mina Filter,在这里,我们只概述两种Filter的使用:
ProtocolCodecFilter
协议编码过滤器,这是一个在服务器接收到客户端数据,或者服务器往客户端发送数据时使用的一个编码器(在客户端的使用亦然),由于网络的传输只能通过字节流或者字符串,所以数据对象的发送接收都必须通过编码(成字节流)和解码(成对象)。这时,只要在网络层添加这么一个ProtocolCodecFilter,便可以隐藏编码和解码的实现模块,而对逻辑层的表现为直接发送对象,接收对象,简便了使用。废话不多说,咱们先来看看ProtocolCodecFilter的使用:
首先,在开启服务器网络连接的时候添加一个ProtocolCodecFilter (代码来自开源项目《迷你微信》服务器
public void init() {
// 自己写的,负责处理网络层回调的类
MinaServerHandle minaServerHandle = new MinaServerHandle
// 建立一个NIO(非阻塞)的连接
acceptor = new NioSocketAcceptor();
// 添加 ProtocolCodecFilter
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MinaEncoder(), new MinaDecoder()));
acceptor.setHandler(minaServerHandle);
try {
// 绑定端口
acceptor.bind(new InetSocketAddress(8081));
} catch (IOException e) {
e.printStackTrace();
}
}
MinaEncoder
大家发现,插入的这个ProtocolCodecFilter中,还有两个参数:MinaEncoder和MinaDecoder,这两个类都是帖主自己实现的,请继续看这两个类的实现: (代码来自开源项目《迷你微信》服务器)
public class MinaEncoder extends ProtocolEncoderAdapter {
public final int INT_SIZE = 4
@Override
public void encode(IoSession ioSession, Object message, ProtocolEncoderOutput output) throws Exception {
byteArrayOutputStream = new ByteArrayOutputStream();
byte[] byteArray;
if (message.getClass().equals(PacketFromServer.class)) {
packetWillSend = (PacketFromServer) message;
// 加入数据包
byteArray = byteArrayOutputStream.write(packetWillSend.getMessageBoty());
int sizeOfAll = byteArrayOutputStream.size() + INT_SIZE;
byteArray = byteArrayOutputStream.toByteArray();
IoBuffer buffer = IoBuffer.allocate(sizeOfAll);
buffer.put(DataTypeTranslater.intToByte(sizeOfAll)); // header
buffer.put(byteArray); // body
buffer.flip();
output.write(buffer);
}
}
}
首先,是这个MinaEncoder ,这是在使用ioSession.write(myPacketFromServer)从服务器往客户端发送数据时调用,需要继承于ProtocolEncoderAdapter 并覆盖父类的encode方法。
先解释一下这个类的用处,当你调用IoSession.write(myPacketFromServer) 的时候,传入了一个myPacketFromServer的对象,这是一个帖主自己编写的类PacketFromServer的实例化对象,然而,IO流并不能直接传输一个Java对象,即便能够传输(序列化,也会导致灵活性降低,因为客户端就必须使用Java了,所以,在网络层加上这么一个Filter,拦截住你发送的Java对象,将其转化为字节流,才能传输,而这个类MinaEncoder 便是做的这件事。
使用看看实现,首先,判断传进来的是不是PacketFromServer对象,接着,将内容一份一份的取出,转化成byte数组,放入byteArrayOutputStream中,最后,从byteArrayOutputStream中将之前塞入的全部byte数组全部取出,往IoBuffer中塞入,然后从ProtocolEncoderOutput 写入通往客户端的输出流。其中
- IoBuffer buffer = IoBuffer.allocate(sizeOfAll); 表示只开sizeOfAll这么大的输出流,超出后超出的部分将被直接抛弃。
MinaDecoder
接着,轮到MinaDecoder了** (代码来自开源项目《迷你微信》服务器)**
public class MinaDecoder extends CumulativeProtocolDecoder {
@Override
protected boolean doDecode(IoSession ioSession, IoBuffer ioBuffer, ProtocolDecoderOutput output) throws Exception {// 如果没有接收完Size部分(4字节),直接返回false
if (ioBuffer.remaining() < 4)
return false;
else {
// 标记开始位置,如果一条消息没传输完成则返回到这个位置
ioBuffer.mark();
byteArrayOutputStream = new ByteArrayOutputStream();
// 读取Size
byte[] bytes = new byte[4];
ioBuffer.get(bytes); // 读取4字节的Size
byteArrayOutputStream.write(bytes);
int bodyLength = DataTypeTranslater.bytesToInt(bytes, 0) - DataTypeTranslater.INT_SIZE; // 按小字节序转int
// 如果body没有接收完整,直接返回false
if (ioBuffer.remaining() < bodyLength) {
ioBuffer.reset(); // IoBuffer position回到原来标记的地方
return false;
} else {
byte[] bodyBytes = new byte[bodyLength];
ioBuffer.get(bodyBytes);
// String body = new String(bodyBytes, "UTF-8");
byteArrayOutputStream.write(bodyBytes);
// 创建对象
NetworkPacket packetFromClient = new NetworkPacket(ioSession, byteArrayOutputStream.toByteArray());
output.write(packetFromClient); // 解析出一条消息
return true;
}
}
}
与MinaEncoder 差不多,MinaDecoder 是在接收到数据的时候被调用到,将数据转化为想要的对象,并交给逻辑层的一个模块。注意,由于帖主的网络协议是自己定义的size + objectByte 格式,所以首先接收到的事4byte,将其转化为int后表明整个包的大小。所以,在客户端传递数据前,要先将整个数据包的大小告诉服务器哦!这是防止粘包和缺包的一种非常有效的方法,这里,插播一下黏包和缺包的解释:
黏包:由于TCP协议在优化传输时,可能将多条小的数据包连接成一个大的数据包一起发送,以此来减少IO次数,提高效率,但这将导致本应该是两条消息的数据被服务器一次收到,黏在一起,所以叫做粘包。
缺包: 由于TCP协议在优化传输时,可能将一个大的数据包分割成几次发送,导致收到的数据包不全,所以,自行编写size来保证不会出现缺包问题。
欢迎阅读我的开源项目《迷你微信》服务器与《迷你微信》客户端
[1]: https://github.com/MrNerverDie/MiniWeChat-Server
[2]: https://github.com/MrNerverDie/MiniWeChat-Client
【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:2.技术简介之MinaFilter(1)的更多相关文章
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:0.概述
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 序言 帖主和队友仿制了一个简单版的微信,其中,队友是用Unity3D做前段,帖主用Java的Mina.Hiberna ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:7.项目介绍之架构(1)
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 前言 <迷你微信>服务器端是使用Java语言,Mina框架编写的,一个良好的架构关系到后期迭代的方便程度 ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:9.观察者模式
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 前言 在一个程序的迭代过程中,复杂度渐渐上升,可能会出现一些跨模块的调用的需求,若是直接得到引用来进行使用,会导致模 ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:8.自定义传输协议
欢迎阅读我的开源项目<迷你微信>服务器)与<迷你微信>客户端 前言 在上一篇中,我们讲到了<迷你微信>服务器)的主体架构,还讲到了如何在现有功能上进行拓展,但是拓展 ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:6.技术简介之Protobuf
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 protocolbuffer(以下简称Protobuf)是google 的一种数据交换的格式,它独立于语言,独立于平 ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:5.技术简介之Hibernate
目录 序言 配置 hibernate.cfg.xml配置文件 加载hibernate.cfg.html配置文件并获取Session 对象的注解配置 增删改查 具体的增删改查代码 数据库操作的封装 连接 ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统 :1.技术简介之Mina连接
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 Apache MINA(Multipurpose Infrastructure for Network Applic ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:4.技术简介之Spring
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 Spring是一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:3.技术简介之MinaFilter——LoggingFilter (转)
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 LoggingFilter 接下来,使我们对Filter介绍的最后一个——LoggingFilter. 与Proto ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:10.项目介绍之架构(2)
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 前言 前面我们讲到<迷你微信>服务器端的主架构,现在我们来描述一下它的模块详细信息. 网络模块 从上图我 ...
随机推荐
- Redis实现求交集操作结果缓存的设计方案
Redis的集合操作 实话说,Redis提供的集合操作是我选择它成为内存数据库的一个主要理由,它弥补了传统关系型数据库在这方面带来的复杂度,使得只需要简单的一个命令就可以完成一个复杂SQL任务,并且交 ...
- cassandra根据用户名密码登录cqlsh
修改conf目录下cassandra.yaml文件 authenticator: PasswordAuthenticator //将authenticator修改为PasswordAuthentic ...
- 47: error: undefined reference to `QWebView::QWebView(QWidget*)'
QT 5.6版本 用Qt界面设计器打开界面文件,在界面上托入QWebView控件,这时运行会出现错误,错误如下: ......... undefined reference to `QWebView ...
- 转:在Linux上安装ant环境
1.从http://ant.apache.org 上下载tar.gz版ant 2.复制到/usr下 3.tar -vxzf apahce-ant-1.9.2-bin.tar.gz 解压 4.chow ...
- channelartlist中autoindex无效的解决方法
{dede:channelartlist}中有使用autoindex无效的解决方法 在设计频道首页的时候,使用{dede:channelartlist}标签时,有很多朋友想做一些高级的开发,让重复的频 ...
- rest framework 认证
一.简单认证示例 需求: 用户名密码正确:没有 token 则产生一个 token,有 token 则更新,返回登录成功: 若用户名或密码错误,返回错误信息. 1.models.py from dja ...
- js上传文件到后台时序列化数据
let fd = new FormData() // 定义传递的序列化对象,for (let i = 0; i < addArr.length; i++) { // addArr是选中文件的输入 ...
- 杭电1003_Max Sum
这是原题的链接http://acm.hdu.edu.cn/showproblem.php?pid=1003 起初我是利用暴力的方法,求出所有序列的和的情况,每取一个序列就和以知道的最大和作对比,取大者 ...
- 洛谷P1587 [NOI2016]循环之美
传送门 不会,先坑着 https://kelin.blog.luogu.org/solution-p1587 //minamoto #include<cstdio> #include< ...
- urlencode()和urldecode()
urlencode()函数原理就是首先把中文字符转换为十六进制,然后在每个字符前面加一个标识符%.一般用在url链接地址编码urldecode()函数与urlencode()函数原理相反,用于解码已编 ...