Mina Tcp服务器开发

因项目架构需求,需要开发一个Mina Tcp服务器。我的Mina服务器是Java winForm,这与在web项目使用会有少许不同。

1、Maven依赖

        <dependency>
            <groupId>org.apache.mina</groupId>
            <artifactId>mina-core</artifactId>
            <version>2.2.1</version>
        </dependency>

2、开发网络数据包编/解码器(传输文本消息可以配置mina使用TextLineCodeFactory工厂类获取编/解码器),用于socket传输数据的过滤处理。本次开发Tcp服务器传输的是字节数据,并且自定义了通信的协议;所以编写了下面的编解码器及工厂类:

//解码器,用于解包socket接收的数据
public
class TcpByteDecoder extends CumulativeProtocolDecoder { /** * 固定包头长度 */ private static final int Package_Header_Length = 6; @Override protected boolean doDecode(IoSession ioSession, IoBuffer in, ProtocolDecoderOutput out) throws Exception { if (Package_Header_Length < in.remaining()) { //缓冲区中的数据长度大于包头,说明可以尝试解码 //标记当前position,以便后继的reset操作能恢复position位置 in.mark(); //获取协议头的5、6字节对应的剩余包长度(协议格式固定为5、6字节存储的是一包数据后续剩余字节数量) int len = in.getUnsignedShort(4); if ((Package_Header_Length + len) > in.remaining()) { //包头长度+包头内指明的后续字节长度>接收数据缓冲区数据长度,则说明接收数据缓冲区的数据还不够一包数据。重置position到操作前,继续接收数据 in.reset(); return false; } //创建byte数组,接收一包数据。一包数据的长度=包头字节数+包字节剩余字节数 byte[] msg = new byte[Package_Header_Length + len];
//获取缓冲区中的数据到数据包byte数组中 in.get(msg);
//将数据包byte数组输出后续过滤器或处理器
out.write(msg);
//读取包后,如果缓冲区还有数据且>包头
if (Package_Header_Length < in.remaining()) { //返回ture,再次进行解包 return true; } } return false;//返回false继续接收数据 } }

 

//编码器,用于编码要发送出去的数据(程序上层应用数据格式为byte[],此处只将byte[]原样输出)
public class TcpByteEncoder extends ProtocolEncoderAdapter {

    @Override
    public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
        byte[] data = (byte[])message;//获取要发送的数据
        IoBuffer buf = IoBuffer.allocate(data.length).setAutoExpand(true);//准备一个大小与数据长度一样的输出缓冲区
        buf.put(data);//将数据放入缓冲区
        buf.flip();//固定数据
        out.write(buf);//将缓冲区内容输出
    }
}

 

//编解码器工厂
public class TcpProtocolCodecFactory implements ProtocolCodecFactory {
    private TcpByteDecoder decoder;
    private TcpByteEncoder encoder;

    public ModbusTcpProtocolCodecFactory() {
        decoder = new TcpByteDecoder();
        encoder = new TcpByteEncoder();
    }

    @Override
    public ProtocolEncoder getEncoder(IoSession ioSession) throws Exception {
        return encoder;
    }

    @Override
    public ProtocolDecoder getDecoder(IoSession ioSession) throws Exception {
        return decoder;
    }
}

 

3、开发Mina事件处理程序

public class MinaClientHandler extends IoHandlerAdapter {
        private String deviceNo;

        public MinaClientHandler(String deviceNo) {
            this.deviceNo = deviceNo;
        }

        @Override
        public void sessionCreated(IoSession session) throws Exception {
            super.sessionCreated(session);
        }

        @Override
        public void sessionOpened(IoSession session) throws Exception {
            super.sessionOpened(session);
        }

        @Override
        public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
            if (session.isConnected()) {
                session.closeNow();
            }
            super.exceptionCaught(session, cause);
        }

        @Override
        public void messageReceived(IoSession session, Object message) throws Exception {
            //此处获取到的message,实际就是在上述解码器中out.write的内容
       byte[] data = (byte[])message;
//原样输出给客户端 session.write(message); } @Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { super.sessionIdle(session, status); } }

4、Mina Server

//这里要开启多个server,只开启一个server的情况需要修改下面的代码
public class MinaService {
   
    public boolean startMinaServer(Integer port) {
        // 1. 创建IoAcceptor
        IoAcceptor acceptor = new NioSocketAcceptor();
        // 2. 加入日志记录过滤器,用SL4J库记录信息
        acceptor.getFilterChain().addLast("logger", new LoggingFilter());
        // 3. 加入编码过滤器,用于解码所有收到的信息,使用 new TextLineCodecFactory() r
        // 发送的信息进行编码,返回是MINA自带的,功能有限,只能处理文本戒者String类型。
        acceptor.getFilterChain().addLast("socketFilter", new ProtocolCodecFilter(new TcpProtocolCodecFactory()));
        // 4. 设置ServerHandler, 自定义的Handler,TimeServerHandler
        acceptor.setHandler(new MinaClientHandler());
        // 5. 设置空闲时间, 这里的BOTH_IDLE指EADER_IDLE和WRITER_IDLE都为120秒
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 120);
        // 6. 绑定监听端口.
        try {
            acceptor.bind(new InetSocketAddress(port));
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
}

5、使用Telnet进行测试(本Server因为只处理特定协议的byte[]数据,因此不能直接使用telnet测试)

telnet ip 端口

6、特别注意:尽量把日志系统配置好并开启,可以快速定位错误并有助于跟踪服务的运行。

posted @ 2023-02-19 14:59  DW039  阅读(82)  评论(0编辑  收藏  举报