mina二进制数据处理粘包断包
来源:互联网 发布:网店交易 知乎 编辑:程序博客网 时间:2024/06/05 06:45
ApacheMINA是一个网络应用程序框架,用来帮助用户简单地开发高性能和高可扩展性的网络应用程序。它提供了一个通过Java NIO在不同的传输例如TCP/IP和UDP/IP上抽象的事件驱动的异步API。
项目结构:
首先是构建项目的pom文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.roden.mina</groupId> <artifactId>mina</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>mina</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.mina</groupId> <artifactId>mina-core</artifactId> <version>2.0.12</version> </dependency> </dependencies></project>
消息实体
package com.roden.mina.entity;/* * 登陆消息 */public class LoginMsg { public short startBit=0x76; //起始固定位 public byte packageLen;//包长度 public byte protocolNo;//协议号 public String content;//信息内容 public short serialNumber;//信息序列号 public short errorCheck;//错误校验 public short endBit;//停止位 public short getStartBit() { return startBit; } public void setStartBit(short startBit) { this.startBit = startBit; } public byte getPackageLen() { return packageLen; } public void setPackageLen(byte packageLen) { this.packageLen = packageLen; } public byte getProtocolNo() { return protocolNo; } public void setProtocolNo(byte protocolNo) { this.protocolNo = protocolNo; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public short getSerialNumber() { return serialNumber; } public void setSerialNumber(short serialNumber) { this.serialNumber = serialNumber; } public short getErrorCheck() { return errorCheck; } public void setErrorCheck(short errorCheck) { this.errorCheck = errorCheck; } public short getEndBit() { return endBit; } public void setEndBit(short endBit) { this.endBit = endBit; }}
编解码
package com.roden.mina.encode;import java.nio.charset.Charset;import org.apache.mina.core.buffer.IoBuffer;import org.apache.mina.core.session.IoSession;import org.apache.mina.filter.codec.ProtocolEncoder;import org.apache.mina.filter.codec.ProtocolEncoderOutput;import com.roden.mina.entity.LoginMsg;public class LoginMsgEncoder implements ProtocolEncoder { private Charset charset; public LoginMsgEncoder(Charset charset) { this.charset = charset; } @Override public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception { LoginMsg lm = (LoginMsg) message; IoBuffer buffer = IoBuffer.allocate(100, false).setAutoExpand(true); buffer.putShort(lm.getStartBit()); buffer.put(lm.getPackageLen()); buffer.put(lm.getProtocolNo()); buffer.put(lm.getContent().getBytes(charset)); buffer.putShort(lm.getSerialNumber()); buffer.putShort(lm.getErrorCheck()); buffer.putShort(lm.getEndBit()); buffer.flip(); out.write(buffer); } @Override public void dispose(IoSession session) throws Exception { }}package com.roden.mina.decode;import java.nio.charset.Charset;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.ProtocolDecoderOutput;import com.roden.mina.entity.LoginMsg;//消息解码器public class LoginMsgDecoder extends CumulativeProtocolDecoder { private Charset charset; public LoginMsgDecoder(Charset charset) { this.charset = charset; } @Override protected boolean doDecode(IoSession session, IoBuffer buffer,ProtocolDecoderOutput out) throws Exception { while (buffer.remaining() > 3) { //记录解码数据起始位置 buffer.mark(); // 读取包格式及大小 short s = buffer.getShort(); byte length = buffer.get(); // 检查读取的包头是否正常,不正常的话清空buffer if (length < 0) { buffer.clear(); break; } else if (length > 3 && buffer.remaining() >= length+7) { LoginMsg lm = new LoginMsg(); lm.setStartBit(s); lm.setPackageLen(length); lm.setProtocolNo(buffer.get()); byte[] content = new byte[lm.getPackageLen()]; buffer.get(content); lm.setContent(new String(content,charset)); lm.setSerialNumber(buffer.getShort()); lm.setErrorCheck(buffer.getShort()); lm.setEndBit(buffer.getShort()); out.write(lm); return true; } else { // 如果消息包不完整,将指针重新移动消息头的起始位置 buffer.reset(); return false; } } return false; }}package com.roden.mina.common;import java.nio.charset.Charset;import org.apache.mina.core.session.IoSession;import org.apache.mina.filter.codec.ProtocolCodecFactory;import org.apache.mina.filter.codec.ProtocolDecoder;import org.apache.mina.filter.codec.ProtocolEncoder;import com.roden.mina.decode.LoginMsgDecoder;import com.roden.mina.encode.LoginMsgEncoder;//编解码器生成工产public class LoginMsgProtocolCodecFactory implements ProtocolCodecFactory { private ProtocolEncoder encoder; private ProtocolDecoder decoder; public LoginMsgProtocolCodecFactory() { this(Charset.forName("UTF-8")); } public LoginMsgProtocolCodecFactory(Charset charset) { encoder = new LoginMsgEncoder(charset); decoder = new LoginMsgDecoder(charset); } @Override public ProtocolDecoder getDecoder(IoSession arg0) throws Exception { return decoder; } @Override public ProtocolEncoder getEncoder(IoSession arg0) throws Exception { return encoder; }}
程序的关键就是解码时需要处理断包和粘包,本例通过继承CumulativeProtocolDecoder实现doDecode方法
A. 你的doDecode()方法返回true 时,CumulativeProtocolDecoder 的decode()方法会首先判断你是否在doDecode()方法中从内部的IoBuffer 缓冲区读取了数据,如果没有,则会抛出非法的状态异常,也就是你的doDecode()方法返回true 就表示你已经消费了本次数据(相当于聊天室中一个完整的消息已经读取完毕),进一步说,也就是此时你必须已经消费过内部的IoBuffer 缓冲区的数据(哪怕是消费了一个字节的数据)。如果验证过通过,那么CumulativeProtocolDecoder 会检查缓冲区内是否还有数据未读取,如果有就继续调用doDecode()方法,没有就停止对doDecode()方法的调用,直到有新
的数据被缓冲。
B. 当你的doDecode()方法返回false 时,CumulativeProtocolDecoder 会停止对doDecode()方法的调用,但此时如果本次数据还有未读取完的,就将含有剩余数据的IoBuffer 缓冲区保存到IoSession 中,以便下一次数据到来时可以从IoSession 中提取合并。如果发现本次数据全都读取完毕,则清空IoBuffer 缓冲区。简而言之,当你认为读取到的数据已经够解码了,那么就返回true,否则就返回false。这个CumulativeProtocolDecoder 其实最重要的工作就是帮你完成了数据的累积,因为这个工作是很烦琐的。
也就是说返回true,那么CumulativeProtocolDecoder会再次调用decoder,并把剩余的数据发下来
返回false就不处理剩余的,当有新数据包来的时候把剩余的和新的拼接在一起然后再调用decoder
服务端
package com.roden.mina.server;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.charset.Charset;import org.apache.mina.core.service.IoAcceptor;import org.apache.mina.core.session.IdleStatus;import org.apache.mina.filter.codec.ProtocolCodecFilter;import org.apache.mina.filter.codec.prefixedstring.PrefixedStringCodecFactory;import org.apache.mina.filter.codec.textline.TextLineCodecFactory;import org.apache.mina.filter.logging.LoggingFilter;import org.apache.mina.transport.socket.nio.NioSocketAcceptor;import com.roden.mina.common.LoginMsgProtocolCodecFactory;public class MinaTimeServer { public static final int PORT= 9800; public static void main(String[] args) throws IOException { // 创建服务端监控线程 IoAcceptor acceptor = new NioSocketAcceptor(); // 设置日志记录器 acceptor.getFilterChain().addLast("logger", new LoggingFilter()); // 设置编码过滤器 //acceptor.getFilterChain().addLast("codec",new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8")))); //acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter( new PrefixedStringCodecFactory(Charset.forName("UTF-8")))); acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter( new LoginMsgProtocolCodecFactory(Charset.forName("UTF-8")))); // 指定业务逻辑处理器 acceptor.setHandler(new TimeServerHandler()); acceptor.getSessionConfig().setReadBufferSize(2048); acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); // 设置端口号 acceptor.bind(new InetSocketAddress(PORT)); }}package com.roden.mina.server;import org.apache.mina.core.service.IoHandlerAdapter;import org.apache.mina.core.session.IdleStatus;import org.apache.mina.core.session.IoSession;import com.roden.mina.entity.LoginMsg;//实现接口IoHandler中的回调方法,优先适配器模式public class TimeServerHandler extends IoHandlerAdapter { @Override public void sessionCreated(IoSession session){ // 显示客户端的ip和端口 System.out.println(session.getRemoteAddress().toString()); } @Override public void messageReceived(IoSession session, Object message) throws Exception { LoginMsg lm=(LoginMsg)message; System.out.println("----------message-----------"); System.out.println(lm.getPackageLen()); System.out.println(lm.getContent()); System.out.println("---------------------"); }}
客户端
package com.roden.mina.client;import java.io.UnsupportedEncodingException;import java.net.InetSocketAddress;import java.nio.charset.Charset;import java.util.Scanner;import org.apache.mina.core.future.ConnectFuture;import org.apache.mina.core.service.IoConnector;import org.apache.mina.core.session.IoSession;import org.apache.mina.filter.codec.ProtocolCodecFilter;import org.apache.mina.filter.codec.prefixedstring.PrefixedStringCodecFactory;import org.apache.mina.filter.codec.textline.TextLineCodecFactory;import org.apache.mina.filter.logging.LoggingFilter;import org.apache.mina.transport.socket.nio.NioSocketConnector;import com.roden.mina.common.LoginMsgProtocolCodecFactory;import com.roden.mina.entity.LoginMsg;import com.roden.mina.server.MinaTimeServer;public class MimaTimeClient { public static void main(String[] args) throws UnsupportedEncodingException { // 创建客户端连接器. NioSocketConnector connector = new NioSocketConnector(); connector.getFilterChain().addLast("logger", new LoggingFilter()); //connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8")))); //connector.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new PrefixedStringCodecFactory(Charset.forName("UTF-8")))); connector.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new LoginMsgProtocolCodecFactory(Charset.forName("UTF-8")))); // 设置连接超时检查时间 connector.setConnectTimeoutCheckInterval(30); connector.setHandler(new TimeClientHander()); // 建立连接 ConnectFuture cf = connector.connect(new InetSocketAddress("127.0.0.1", MinaTimeServer.PORT)); // 等待连接创建完成 cf.awaitUninterruptibly(); IoSession session = cf.getSession(); String str="我是消息休"; byte a=2; LoginMsg lm=new LoginMsg(); short s=78; lm.setStartBit(s); lm.setPackageLen((byte)str.getBytes("utf-8").length); lm.setProtocolNo(a); lm.setContent(str); lm.setSerialNumber(s); lm.setErrorCheck(s); lm.setEndBit(s); session.write(lm); //关闭 if(session!=null){ if(session.isConnected()){ // 等待连接断开 cf.getSession().getCloseFuture().awaitUninterruptibly(); } // 释放连接 connector.dispose(true); } }}package com.roden.mina.client;import org.apache.mina.core.service.IoHandlerAdapter;import org.apache.mina.core.session.IoSession;public class TimeClientHander extends IoHandlerAdapter { @Override public void messageReceived(IoSession session, Object message) throws Exception { System.out.println("client接受信息:"+message.toString()); } @Override public void messageSent(IoSession session, Object message) throws Exception { System.out.println("client发送信息:"+message.toString()); }}
Hander部分采用适配器模式,自己选择要实现的方法
参考:
http://mina.apache.org/mina-project/quick-start-guide.html
- mina二进制数据处理粘包断包
- Erlang二进制数据处理
- 4. 二进制数据处理库
- mina 二进制协议客户端开发
- [Erlang 0024]Erlang二进制数据处理
- Mina二进制传输对象IoBuffer的使用
- Mina二进制传输对象IoBuffer的使用
- Python中二进制数据处理模块struct使用
- jdbc基础 (三) 大文本、二进制数据处理
- MINA粘包断包专题研究
- MINA
- mina
- MINA
- mina
- mina
- MINA
- mina
- Mina
- vi常用快捷键备忘录
- iOS 创建单例的两种方法
- PCANet中PCA算法为什么没有起到降维的作用?
- iOS 创建单例的两种方法
- iOS开发中Settings.bundle的使用
- mina二进制数据处理粘包断包
- iOS开发:使用Settings Bundl…
- iOS控件常用类的常用属性和方法总…
- 在iOS 8中使用UIAlertController
- iOS8 播放系统声音、提醒声音…
- IOS --- OC与Swift混编
- 运算符优先级 (JavaScript)
- php模仿arsort方法
- 内核container_of(ptr,type,member) 解析