设为首页 收藏本站
查看: 893|回复: 0

[经验分享] Apache Mina2.x网络通信框架使用入门

[复制链接]

尚未签到

发表于 2018-11-22 06:50:34 | 显示全部楼层 |阅读模式
  关于Apache Mina的文章,资料已经非常多了,我想再多一篇也不过为。另外Main现在3.x版本正在开发中,且已经有M2(里程碑)发布了。
  本文中主要针对Mina2.0.9(这个版本也是最后一个2.x版本了)来记录学习和使用的过程和体会。

  Mina2.x的文档还算比较全面:http://mina.apache.org/mina-project/userguide/user-guide-toc.html (有些部分可能没有更新,未来讲更多关注和使用3.x)。
  


  •   开发服务器和客户端网络应用
      使用Mina开发,客户端连接器和服务端接收器有更多的相似之处,Mina在API设计的时候使用了更高的抽象如:IoService,对于创建服务器端接收器我们将关注IoAccepor,而客户端连接器则是IoConnector.
      





    下面图1是关于Mina的基本使用的描述:

      DSC0000.jpg

  图1
  

  这里对图1做简单的说明:
  图1由大的三部分组成,通过颜色就很容易区分和理解器表示的意思。对于服务器(Server)和客户端(Client)而言它们都需要中间最大一块的组成部分,其中包含了配置(Configure),会话数据工厂(SessionDataFactory),过滤器链(FilterChina,由多个过滤器组成),监听器组(有多个Listener组成),处理器(IoHandler);第三部分则可以看出服务器对应的是绑定(bind),客户端对应的是连接(connect)由此区分了服务器和客户端。
  

  说了这么多,就中间部分而言,Mina框架最大程度的解放了开发过程要进行的会话管理,会话数据管理,服务监听处理,过滤器,服务配置等的实现,其都提供了默认实现,当然可以根据使用情况实现对应的接口来自行处理。
  

  2.过滤器
  Mina中的过滤器的顶层接口是IoFilter,其自身提供了多种过滤器,比如:LoggingFilter,ExecutorFilter,BlacklistFilter(请求地址黑名单过滤),SSLFilter,编码和解密过滤器等等(更多参考API http://mina.apache.org/mina-project/apidocs/index.html)。
  过滤器是Mina框架中极其重要和有价值的部分,其提供了日志,安全,权限控制,统计等过滤器,并且其是可插拔的,可以通过不同的过滤器以不同的顺序组成过滤器链将实现不同的功能。另外可以通过实现IoFilter接口或者继承IoFilterAdapter来创建更多具体业务中需要的IoFilter,当然这么做之前,可以仔细看看Mina的org.apache.mina.filter包下是否已经提供了实现。
  

  3.编码和解码
  编码和解码作为网络应用开发必须面对的问题,而Mina作为全功能的网络通讯框架,实现对数据报文的编码和解码自然是其分内之事,具体使用者可更多关注IoHandler,即具体处理接收和发送报文的相关业务。
  在org.apache.mina.filter.codec包下有更多的关于编码和解码的实现。
  

  关于编码其方式有很多种,比如Protobuf,serialization(对象序列化),JSON,BASE64等,而解码则涉及到字节流分割的问题,下图2是三种常用的字节流分割的方式:
DSC0001.jpg
图2

  


  

  上面三种方式中2和3在Mina中都有对应的实现,比如3特殊字符结尾标记对应的实现有TextLineEncoder和TextLineDecoder,两者组成了TextLineCodecFactory; 2固定字节的head表示数据字节数有PrefixedStringEncoder和PrefixedStringDecoder,两者组成了PrefixedStringCodecFactory。
  第一种固定长度字节数这种主要应用在传输命令的场景中,其传输的字节数是固定,应用中可以自己根据具体情况来实现对应的编码和解码类。
  

  4.一个具体案例来贯穿全文
  本案例通过客户端发送短信信息到服务器,然后服务器将其短信信息的发送者和接受者对调,短信内容设置"OK",发回给客户端。
  4.1定义短信格式(protobuf):
package secondriver.mina.bo.protobuf;
option java_package = "secondriver.mina.bo.protobuf";
option java_outer_classname = "SmsDataProtocal";
message Sms {
  required string protocol = 1;
  required string sender = 2;
  required string receiver = 3;
  required string content = 4;
}  使用protoc命令将定义个消息生成Java类(使用方式可以参考:)。
  

  4.2编写Sms对象的编码和解密类,这里我们直接编写编码解密工程类,其由编码和解密类组合而成。
package secondriver.mina.bo.protobuf;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import com.google.protobuf.ByteString;
import secondriver.mina.bo.protobuf.SmsDataProtocal.Sms;
public class SmsDataCodecFactory implements ProtocolCodecFactory {
private final Charset charset = StandardCharsets.UTF_8;
private int prefixLength = 4;
private int maxDataLength = 1024;
@Override
public ProtocolEncoder getEncoder(IoSession session) throws Exception {
return new ProtocolEncoderAdapter() {
@Override
public void encode(IoSession session, Object message,
ProtocolEncoderOutput out) throws Exception {
Sms sms = (Sms) message;
String content = sms.toByteString().toStringUtf8();
IoBuffer buffer = IoBuffer.allocate(content.length())
.setAutoExpand(true);
buffer.putPrefixedString(content, prefixLength,
charset.newEncoder());
if (buffer.position() > maxDataLength) {
throw new IllegalArgumentException("Data length: "
+ buffer.position());
}
buffer.flip();
out.write(buffer);
}
};
}
@Override
public ProtocolDecoder getDecoder(IoSession session) throws Exception {
return new CumulativeProtocolDecoder() {
@Override
protected boolean doDecode(IoSession session, IoBuffer in,
ProtocolDecoderOutput out) throws Exception {
if (in.prefixedDataAvailable(prefixLength, maxDataLength)) {
String msg = in.getPrefixedString(prefixLength,
charset.newDecoder());
Sms sms = Sms.parseFrom(ByteString.copyFrom(msg,
charset.name()));
out.write(sms);
return true;
}
return false;
}
};
}
}  4.3参见文中1端来写服务端
  创建IoAccptor对象->设置过滤器->设置IoHandler->配置->绑定到指定IP和端口
package secondriver.mina.server;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.filter.logging.LogLevel;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import secondriver.mina.bo.protobuf.SmsDataCodecFactory;
import secondriver.mina.bo.protobuf.SmsDataProtocal.Sms;
public class SmsServer {
public static final int PORT = 9001;
public static void main(String[] args) throws IOException {
// 接收器
IoAcceptor acceptor = new NioSocketAcceptor();
// 过滤器链
DefaultIoFilterChainBuilder builder = new DefaultIoFilterChainBuilder();
LoggingFilter loggingFilter = new LoggingFilter();
loggingFilter.setExceptionCaughtLogLevel(LogLevel.DEBUG);
builder.addLast("logging", loggingFilter);
builder.addLast("codec", new ProtocolCodecFilter(
new SmsDataCodecFactory()));
builder.addLast("threadPool",
new ExecutorFilter(Executors.newCachedThreadPool()));
acceptor.setFilterChainBuilder(builder);
// 设置处理器IoHandler
acceptor.setHandler(new IoHandlerAdapter() {
@Override
public void messageReceived(IoSession session, Object message)
throws Exception {
Sms sms = (Sms) message;
System.out.println("客户端发来:");
System.out.println(sms.toString());
// 服务器发送
Sms serverSms = Sms.newBuilder().setProtocol(sms.getProtocol())
.setContent("OK").setReceiver(sms.getSender())
.setSender(sms.getSender()).build();
session.write(serverSms);
}
});
// 配置服务器(IoAccptor)
acceptor.getSessionConfig().setReadBufferSize(2048);
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
// 绑定到指定IP和端口
acceptor.bind(new InetSocketAddress(PORT));
}
}  

  4.4 参见文中1端编写客户端
package secondriver.mina.client;
import java.net.InetSocketAddress;
import java.util.Scanner;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.mina.core.RuntimeIoException;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import secondriver.mina.bo.protobuf.SmsDataCodecFactory;
import secondriver.mina.bo.protobuf.SmsDataProtocal.Sms;
public class SmsClient {
private static InetSocketAddress server = new InetSocketAddress(
"127.0.0.1", 9001);
public static void main(String[] args) throws InterruptedException {
// 客户端连接器
IoConnector connector = new NioSocketConnector();
// 过滤器
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new SmsDataCodecFactory()));
connector.getFilterChain().addLast("threadPool",
new ExecutorFilter(Executors.newCachedThreadPool()));
// 处理器
connector.setHandler(new IoHandlerAdapter() {
@Override
public void sessionCreated(IoSession session) throws Exception {
}
@Override
public void messageReceived(IoSession session, Object message)
throws Exception {
System.out.println("服务器响应:");
System.out.println(((Sms) message).toString());
}
});
// 建立会话Session
IoSession session = null;
while (true) {
try {
ConnectFuture future = connector.connect(server);
future.awaitUninterruptibly(100, TimeUnit.SECONDS);
session = future.getSession();
if (null != session) {
break;
}
} catch (RuntimeIoException e) {
System.err.println("Failed to connect with "
+ server.toString());
e.printStackTrace();
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
// 客户端输入
try (Scanner scanner = new Scanner(System.in);) {
while (true) {
String sender = "1814453211";
System.out.println("请输入收信息手机号:");
String receiver = scanner.nextLine();
System.out.println("请输入信息内容:");
String content = scanner.nextLine();
Sms sms = Sms.newBuilder()
.setProtocol("ip.weixin.com TC-C/2.0")
.setSender(sender).setReceiver(receiver)
.setContent(content).build();
session.write(sms);
Thread.sleep(10000);
System.out.println("是否继续,回车继续 , q or quit 退出:");
String line = scanner.nextLine();
if (line.trim().equalsIgnoreCase("q")
|| line.trim().equalsIgnoreCase("quit")) {
break;
}
}
}
session.close(false);
connector.dispose();
}
}  

  4.5 启动服务,启动客户端
  图3是运行的结果:
  

  客户端信息: DSC0002.jpg
  

  
服务器信息: DSC0003.jpg
图3

  

  说明:上面客户端和服务器端的关于IoHandler直接使用了匿名类的方式对数据的接收做了相应的简单处理。Sms对象转换成UTF-8编码的字符串,采用了3端中编码和解密的第2中方式,并且传输的数据最大长度为1024byte(1k)。另外,Potobuf-java和Mina集成,mina3.x提供了对protobuf定义的消息的编码和解码提供了实现支持。
  

  为了需要更多关注Mina3.x,另外Netty的发展势头正旺,netty有种子承父业的感觉,也值得拥有!

  

  另外关于使用Mina2.x的一个多客户端会话的示例见:https://code.csdn.net/snippets/546078.js




运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-637916-1-1.html 上篇帖子: PHP & MySQL & Apache统一编码 下篇帖子: apache配置过程出现的小问题
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表