Mina自定义协议简单实现

因公司需要做个电子秤自动称重系统,需要自定义协议实现,所以就用Mina简单实现了一下,因为比较简单,就直接上代码。有时间的话改成Netty版

服务端

package net.heartma.server;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
import net.heartma.protocol.CustomProtocolCodecFactory;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

/**
 * 服务端
 * @author heartma
 *
 */
public class BalanceServer {
    private static SocketAcceptor acceptor;
    private static DefaultIoFilterChainBuilder filter;
    private static SocketSessionConfig config;
    public static Logger logger = Logger.getLogger("BalanceServer");
    private static Executor threadPool = Executors.newFixedThreadPool(20);
    public static void main(String[] args) {
        // 1、创建服务器端监听
        acceptor = new NioSocketAcceptor();
        // 2、添加日志过滤和编码过滤
        filter = acceptor.getFilterChain();
        filter.addLast("threadPool", new ExecutorFilter(threadPool));
        filter.addLast("logger", new LoggingFilter());
        // ----编码过滤:将二进制或者协议相关数据转换成一个对象。TextLine工厂类可以处理基于文字的信息
        filter.addLast("codec", new ProtocolCodecFilter(new CustomProtocolCodecFactory()));
        // 3、绑定handler到acceptor
        acceptor.setHandler(new BalanceServerHandler());

        // 4、设置socket属性
        // 获取socket的连接参数
        config = acceptor.getSessionConfig();
        // 设置socket的缓冲区大小为2M
        config.setReadBufferSize(2048);

        /**
         * @params IdleStatus arg0 :在未成为idle状态前应该关心的状态(READ_IDLE或者WRITE_IDLE)
         * @params @params IdleStatus arg1 : 变成IDLE状态所需要的时间(超时时间)
         *
         *         如果session持续idle的时间等于arg1时,将会触发handler中的sessionIdle方法
         */
        // 设置空闲状态持续时间:1、这里的状态可以自己设置成只为读取设置空闲状态持续时间,只为写入设置空闲状态等待时间,或者为两者都设置空闲状态等待时间。后面的时间是两次触发handler中的sessionIdel方法的间隔时间。
        config.setIdleTime(IdleStatus.BOTH_IDLE, 10);
        try {
            // 为服务器socket绑定端口
            acceptor.bind(new InetSocketAddress(8081));
            logger.info("服务已经启动... ...");
        } catch (IOException e) {
            logger.info("服务启动异常 :");
            e.printStackTrace();
        }
    }
}

服务端Handler

package net.heartma.server;
import net.heartma.pojo.Message;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;

/**
 * 服务端Handler
 * @author heartma
 *
 */
public class BalanceServerHandler extends IoHandlerAdapter {

    @Override
    public void exceptionCaught(IoSession ioSession, Throwable e)
            throws Exception {
        System.out.println("exceptionCaught");

    }

    @Override
    public void messageReceived(IoSession ioSession, Object obj) throws Exception {
        System.out.println("messageReceived");
        Message message = (Message)obj;
        System.out.println("服务器端接收到的消息:" +message);
    }

    @Override
    public void messageSent(IoSession ioSession, Object obj) throws Exception {
        System.out.println("messageSent");
    }

    @Override
    public void sessionClosed(IoSession ioSession) throws Exception {
        System.out.println("sessionClosed");
    }

    @Override
    public void sessionCreated(IoSession ioSession) throws Exception {
        System.out.println("sessionCreated");
    }

    @Override
    public void sessionIdle(IoSession ioSession, IdleStatus arg1) throws Exception {
        System.out.println("sessionIdle");
    }

    @Override
    public void sessionOpened(IoSession ioSession) throws Exception {

        System.out.println("sessionOpened");
    }

}

客户端

23230E363932383834363630333738370D303030302E3434 为定义的协议报文

package net.heartma.client;

import java.net.InetSocketAddress;
import java.net.SocketAddress;

import net.heartma.protocol.CustomProtocolCodecFactory;

import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

/**
 * Mina客户端
 * @author Administrator
 *
 */
public class BalanceClient {

	public static void main(String[] args) {
		//	创建客户端连接器 基于tcp/ip
		NioSocketConnector connector = new NioSocketConnector();

		//	连接的地址和端口
		SocketAddress address = new InetSocketAddress("localhost",8081);

		//	获取过滤器链
		DefaultIoFilterChainBuilder chain = connector.getFilterChain();

		//	配置日志过滤器和自定义编解码器
		chain.addLast("logger", new LoggingFilter());
		chain.addLast("mycodec",new ProtocolCodecFilter(new CustomProtocolCodecFactory()));

		//	添加处理器
		connector.setHandler(new BalanceClientHandler());

		// 连接到服务器 
		ConnectFuture future = connector.connect(address);

		//	等待连接创建完成
		future.awaitUninterruptibly();

		//	会话创建后发送消息到服务器
		future.getSession().write("23230E363932383834363630333738370D303030302E3434");

		//	等待28000毫秒后连接断开
		future.getSession().getCloseFuture().awaitUninterruptibly(28000);

		//	关闭连接
		connector.dispose();
	}

}

客户端Handler

package net.heartma.client;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;

/**
 * 客户端Handler
 * @author heartma
 *
 */
public class BalanceClientHandler extends IoHandlerAdapter {

    public void exceptionCaught(IoSession ioSession, Throwable e)
            throws Exception {
        System.out.println("exceptionCaught");
    }

    public void messageReceived(IoSession ioSession, Object obj) throws Exception {
        System.out.println("messageReceived");

    }

    public void messageSent(IoSession ioSession, Object obj) throws Exception {
        System.out.println("客户端发送消息...");
        //super.messageSent(ioSession, obj);
    }

    public void sessionClosed(IoSession ioSession) throws Exception {
        System.out.println("sessionClosed");

    }

    public void sessionCreated(IoSession ioSession) throws Exception {
        System.out.println("sessionCreated");

    }

    public void sessionIdle(IoSession ioSession, IdleStatus idle) throws Exception {

        System.out.println("sessionIdle");
    }

    public void sessionOpened(IoSession ioSession) throws Exception {
        System.out.println("sessionOpened");

    }

}

实体消息体Message

package net.heartma.pojo;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;

/**
 * 定义消息体属性
 * @author heartma
 *
 */
public class Message {
    private String header;  //头
    private int length;    //卡号长度
    private String card;   //卡号 
    private double weight;  //重量
    public String getHeader() {
        return header;
    }
    public void setHeader(String header) {
        this.header = header;
    }
    public int getLength() {
        return length;
    }
    public void setLength(int length) {
        this.length = length;
    }
    public String getCard() {
        return card;
    }
    public void setCard(String card) {
        this.card = card;
    }
    public double getWeight() {
        return weight;
    }
    public void setWeight(double weight) {
        this.weight = weight;
    }

    /**
     * 解析字节数组
     * @param messageBytes
     */
    public final boolean ReadFromBytes(byte[] messageBytes) {
        //获取头部
        byte[] head = new byte[2];
        System.arraycopy(messageBytes, 0, head, 0,2);
        setHeader(new String(head));
        //获取长度
        byte[] len = new byte[1];
        System.arraycopy(messageBytes, 2, len, 0,1);
        setLength(len[0]);
        //判断卡号长度是否为0,为0则说明为心跳信息,否则为真实数据
        if((int)len[0]>0){
            //卡号解析
            byte[] cardDest = new byte[len[0]];
            System.arraycopy(messageBytes, 3, cardDest, 0,len[0]);
            setCard(new String(cardDest));
            byte[] weightDest = new byte[7];
            System.arraycopy(messageBytes, 3+length, weightDest, 0,7);
            setWeight(Double.parseDouble(new String(weightDest)));
            return true;
        }
        return false;
    }   

    @Override
    public String toString() {
        return "Message [header=" + header + ", length=" + length + ", card="
                + card + ", weight=" + weight + "]";
    }
}

自定义协议工厂类

package net.heartma.protocol;
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;

/**
 * 自定义协议工厂类
 * @author heartma
 */
public class CustomProtocolCodecFactory implements ProtocolCodecFactory{
    private final CustomProtocolDecoder decoder;
    private final CustomProtocolEncoder encoder;
    public CustomProtocolCodecFactory(){
        this.decoder = new CustomProtocolDecoder(Charset.forName("utf-8"));
        this.encoder = new CustomProtocolEncoder(Charset.forName("utf-8"));
    }
    @Override
    public ProtocolDecoder getDecoder(IoSession ioSession) throws Exception {
        return decoder;
    }

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

自定义协议编码

package net.heartma.protocol;
import java.nio.charset.Charset;
import net.heartma.tools.ByteUtil;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;

/**
 * 自定义协议编码
 * @author heartma
 */
public class CustomProtocolEncoder extends ProtocolEncoderAdapter {
    private Charset charset;

    public CustomProtocolEncoder(Charset charset) {
        this.charset = charset;
    }

    @Override
    public void encode(IoSession session, Object message,
            ProtocolEncoderOutput out) throws Exception {
        byte[] hexStrToByte = ByteUtil.hexStr2ByteArray(message.toString());
        IoBuffer buf = IoBuffer.allocate(hexStrToByte.length).setAutoExpand(false);

        //byte[] content = Tools.HexString2Bytes(message.toString());
        buf.put(hexStrToByte);
        buf.flip();
        out.write(buf);
        out.flush();
        buf.free();
    }
}

自定义协议解码

package net.heartma.protocol;

import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;

import net.heartma.pojo.Message;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderAdapter;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;

/**
 * 自定义协议解码
 * @author heartma
 */
public class CustomProtocolDecoder  extends ProtocolDecoderAdapter {
    private CharsetDecoder decoder;
    public CustomProtocolDecoder(Charset charset){
        this.decoder = charset.newDecoder();
    }
    @Override
    public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)
            throws Exception {
        int limit = in.limit();
        byte[] bytes = new byte[limit];
        in.get(bytes);
        Message message = new Message();
        message.ReadFromBytes(bytes);
        out.write(message);
    }
}

代码下载:

http://download.csdn.net/download/cos18661062156/10141901

追求卓越,成功就会在不经意间追上你!

时间: 2024-10-11 20:57:20

Mina自定义协议简单实现的相关文章

mina的编码和解码以及断包的处理,发送自定义协议,仿qq聊天,发送xml或json和

最近一段时间以来,mina很火,和移动开发一样,异常的火爆.前面写了几篇移动开发的文章,都还不错,你们的鼓励就是我最大的动力.好了,废话少说.我们来看下tcp通讯吧. tcp通讯对于java来说是很简单的.就是socket,也就是大家常说的套接字.大家不要把它看的很难.说白了tcp通讯其实就是数据流的读写.一条输入流,一条输出流.分别复杂发消息和接收消息. 明白了这些,ok,我们来看看我写的例子吧.先看服务器端的测试类的源码: package com.minaqq.test; import co

Netty 5 自定义协议 教程

网上好多连接好多demo都不是netty5的,都是以前的版本,况且还有好多运行时老报错.入门级demo就不写了,估计都是那些老套路.好多公司都会有最佳实践,今天就说说如何自定义协议,一般自定义协议都是公司内部各个部门定义的,当然了我写的比较简单. 注意: 本教程是采用netty-all-5.0.0.Alpha2.jar,netty5的版本,不是网上很多的例子都是采用以前老大版本. 自定义协议: 协议 {   协议头(header)   消息体(body) } header格式 {   固定头,

C#综合揭秘——通过修改注册表建立Windows自定义协议

引言 本文主要介绍注册表的概念与其相关根项的功能,以及浏览器如何通过连接调用自定义协议并与客户端进行数据通信.文中讲及如何通过C#程序.手动修改.安装项目等不同方式对注册表进行修改.其中通过安装项目对注册表进行修改的情况最为常见,在一般的应用程序中都会涉及.当中最为实用的例子将介绍如何通过"安装项目"修改注册表建立自定义协议,在页面通过ajax方式发送路径请求,并在回调函数中调用自定义协议.最后一节还将介绍如何调用自定义协议去保持数据的保密性.希望本篇文章能对各位的学习研究有所帮助,当

wireshark插件开发 - 自定义协议

虽然wireshark自带了很多知名协议的解析插件,譬如HTTP.DHCP等等,然而在实际应用环境中,有不少软件之间的通信协议都是私有的,如游戏客户端和服务器之间的交互协议通常都是私有的,wireshark无法具体解析出各种字段之间的含义,只能显示接收到的二进制数据,给协议的分析和问题的排查带来了一定的困难,尤其是协议内容比较复杂时. 本文一个自定义的简单协议入手,分析如何基于wireshark开发自定义协议分析插件. 1.1. 概述 本书使用Go语言来描述协议的交互过程.Go由Google出品

【Win10 UWP】URI Scheme(二):自定义协议的处理和适用场景

上一篇提到Windows Store协议的使用,其实Windows Store协议仅是系统内建的一种协议规则.我们也可以自己定义一套规范的URI-Scheme,除了可以给其他App调用外,本应用也可以进行调用,解决一些特殊的场景,本讲具体探讨这一问题. 关于Windows Store协议的解析和使用,可先阅读上一篇:http://www.cnblogs.com/zhxilin/p/4819372.html 一.自定义协议的处理 前面提到,通过协议,应用可以被“激活(Activated)”,并且可

Netty自定义协议解析原理与应用

目前,大家都选择Netty做为游戏服务器框架网络通信的框架,而且目前也有很多优秀的产品是基于Netty开发的.它的稳定性,易用性和高效率性已得到广泛的认同.在游戏服务器开发中,选择netty一般就意味着我们要使用长连接来建立与客户端的通信,并且是自定义协议,在网络开发中,我们不得不处理断包,粘包的问题,因为Tcp/ip是基于数据流的传输,包与包之间没有明确的界限,而且于由网络路由的复杂性,大包有可能分成小包,小包也有可能被组装成大包进行传输.而Netty就考虑到了这一点,而且它用一个类就帮我们处

HTTP协议简单介绍

协议 协议是通信计算机双方必须共同遵从的一组约定.如怎么样建立连接.怎么样互相识别等.只有遵守这个约定,计算机之间才能相互通信交流.它的三要素是:语法.语义.时序. 简单来说,协议就是 通信双方/多方都遵从共同的一个规范. 协议的一些特点: 1) 协议中的每个人都必须了解协议,并且预先知道所要完成的所有的步骤. 2) 协议中的每个人都必须同意并遵循它. 3) 协议必须是清楚的,每一步必须明确定义,并且不会引起误解. HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型.HTT

【转】C#综合揭秘——通过修改注册表建立Windows自定义协议

引言 本文主要介绍注册表的概念与其相关根项的功能,以及浏览器如何通过连接调用自定义协议并与客户端进行数据通信.文中讲及如何通过C#程序.手动修改.安装项目等不同方式对注册表进行修改.其中通过安装项目对注册表进行修改的情况最为常见,在一般的应用程序中都会涉及.当中最为实用的例子将介绍如何通过"安装项目"修改注册表建立自定义协议,在页面通过ajax方式发送路径请求,并在回调函数中调用自定义协议.最后一节还将介绍如何调用自定义协议去保持数据的保密性.希望本篇文章能对各位的学习研究有所帮助,当

swoole入门教程05-Swoole的自定义协议功能的使用

环境说明: 系统:Ubuntu14.04 (安装教程包括CentOS6.5) PHP版本:PHP-5.5.10 swoole版本:1.7.8-alpha 1.为什么要提供自定义协议 熟悉TCP通信的朋友都会知道,TCP是一个流式协议.客户端向服务器发送的一段数据,可能并不会被服务器一次就完整的收到;客户端向服务器发送的多段数据,可能服务器一次就收到了全部的数据.而实际应用中,我们希望在服务器端能一次接收一段完整的数据,不多也不少.传统的TCP服务器中,往往需要由程序员维护一个缓存区,先将读到的数