`

Mina基础(三)

阅读更多

        自定义协议编解码,自定义协议是使用最广泛的,因为它非常的灵活!

制定协议

        协议需求:向服务端发送请求(频道的ID和说明文字),返回响应结果(该频道下所有的节目信息)。

        协议格式如下:

        请求格式

Syntax

No. of Bits

Identifier

_ description (){

 

 

         Descriptor tag

16

0x0001

         descriptor length

32

从下一字节开始至末尾的数据长度

         ID

16

channel ID值

     chanel_des_len

8

频道说明文字

         for(i=0;i< chanel_des_len;i++){

 

 

                  Byte_data

8

 

         }

 

 

}

 

// end _description

        响应格式

Syntax

No. of Bits

Identifier

_ description (){

 

 

         Tag

16

0x8001

         Data_length

32

从下一字节开始至末尾的数据长度

         channel_addr

32

频道名称的地址

         channel _len

8

频道名称的字符串长度

         programme_count

16

节目个数

         for(i=0;i< programme_count;i++){

 

 

                  dayIndex

8

属于哪一天(以当日为基准)(-1表示前一天;0表示当天;1表示下一天;2表示后两天)

                  event_addr

32

节目名称的地址

                  event _len

8

节目名称的字符串长度

                  StartTime

32

节目偏移开始时间

                  TotalTime

16

节目总时长(以秒为单位)

                  Status

8

节目当前状态(已录制【0x01】//待录制【0x00】)

                  url_addr

32

节目播放地址的addr

                  url _len

8

节目播放地址的长度

}

 

 // end for

         For(j=0;j<;j++){

 

 

                  Byte_data

8

真实数据

         }

 

 

}

 

// end _description

        协议解释如下:

        a.协议前两个字节(16Bits)是协议的唯一标识值;如上:请求部分的tag = 0x0001,响应部分的 tag = 0x8001

        b.接着四个字节(32Bits)是传输消息的长度;

        c.接下来是数据区。

 

1.分析请求部分:

Syntax

No. of Bits

Identifier

_ description (){

 

 

         Descriptor tag

16

0x0001

         descriptor length

32

从下一字节开始至末尾的数据长度

         ID

16

channel ID值

     chanel_des_len

8

频道说明文字

         for(i=0;i< chanel_des_len;i++){

 

 

                  Byte_data

8

 

         }

 

 

}

 

// end _description

        请求部分是客户端(机顶盒)向服务端发送的请求;协议I的请求只发送了两个个参数:channelID和channel_dec(频道描述信息) 

        各个参数分析:

        a.descriptor tag:请求的唯一标识;         -- 2个字节

        b.descriptor length:数据区长度;          -- 4个字节  

        c.ID:channelID;                          -- 2个字节

        d.channel_dec_len:频道说明信息的字节长度  -- 1个字节

        e.for循环:存放频道说明信息的真实数据(字节数组中)

 

2.协议格式总结

        前面2个绿色部分称为报文头,固定6个字节;

        中间2个蓝色部分称为基本数据区,用Java的8个基本数据类型描述;

        最后的红色部分称为真实数据区,所有String类型的信息都放在这里;

        基本数据区+真实数据区 =数据区

        协议格式:报文头+数据区

        图示如下:

        总之,对于基本数据类型,直接存放在基本数据区,对于String类型,在基本数据区描述它的长度和在真实数据区的地址,然后存在在真实数据区;而Java对象,则是把对象属性分解为基本数据类型和String类型发送;因此,解码必须获得三个信息:

        a.请求标识:根据请求的不同进行不同的解码

        b.数据区总长度:定是否接受数据成功;

        c.偏移地址:知道真实数据区位置,就可以解码String数据。

        图示如下:

 

3.代码实现

        a.首先定义消息的抽象类,定义获取3个解码信息的方法

 

package com.bijian.study.mina.message;

import java.nio.charset.Charset;

public abstract class AbstrMessage {
	// 协议编号
	public abstract short getTag();

	// 数据区长度
	public abstract int getLen(Charset charset);

	// 真实数据偏移地址
	public abstract int getDataOffset();
}
        b.定义请求对象和响应对象;
        请求的Java对象:
package com.bijian.study.mina.message;

import java.nio.charset.Charset;

import org.apache.log4j.Logger;

/*
 * 请求的Java对象
 */
public class ChannelInfoRequest extends AbstrMessage {
	private Logger logger = Logger.getLogger(ChannelInfoRequest.class);

	private String channel_desc;

	private int channel_id;

	@Override
	public short getTag() {
		return (short) 0x0001;
	}

	@Override
	public int getLen(Charset charset) {
		int len = 2 + 1;
		try {
			if (channel_desc != null && !"".equals(channel_desc)) {
				len += channel_desc.getBytes(charset).length;
			}
		} catch (Exception e) {
			logger.error("频道说明转换为字节码错误...", e);
		}
		return len;
	}

	@Override
	public int getDataOffset() {
		int len = 2 + 4 + 2 + 1;
		return len;
	}

	public String getChannel_desc() {
		return channel_desc;
	}

	public void setChannel_desc(String channel_desc) {
		this.channel_desc = channel_desc;
	}

	public int getChannel_id() {
		return channel_id;
	}

	public void setChannel_id(int channel_id) {
		this.channel_id = channel_id;
	}
}
        响应的Java对象:
package com.bijian.study.mina.message;

import java.nio.charset.Charset;

import org.apache.log4j.Logger;
/*
 * 响应的Java对象
 */
public class ChannelInfoResponse extends AbstrMessage {
	private Logger logger = Logger.getLogger(ChannelInfoResponse.class);

	private String ChannelName;

	private EventDto[] events;

	@Override
	public short getTag() {
		return (short) 0x8001;
	}

	@Override
	public int getLen(Charset charset) {
		int len = 4 + 1 + 2;
		try {
			if (events != null && events.length > 0) {
				for (int i = 0; i < events.length; i++) {
					EventDto edt = events[i];
					len += 1 + 4 + 1 + 4 + 2 + 1 + 4 + 1 + edt.getLen(charset);
				}
			}
			if (ChannelName != null && !"".equals(ChannelName)) {
				len += ChannelName.getBytes(charset).length;
			}
		} catch (Exception e) {
			logger.error("频道信息转换为字节码错误...", e);
		}
		return len;
	}

	@Override
	public int getDataOffset() {
		int len = 2 + 4 + 4 + 1 + 2;
		if (events != null && events.length > 0) {
			len += events.length * (1 + 4 + 1 + 4 + 2 + 1 + 4 + 1);
		}
		return len;
	}

	public String getChannelName() {
		return ChannelName;
	}

	public void setChannelName(String channelName) {
		ChannelName = channelName;
	}

	public EventDto[] getEvents() {
		return events;
	}

	public void setEvents(EventDto[] events) {
		this.events = events;
	}
}
package com.bijian.study.mina.message;

import java.nio.charset.Charset;
import org.apache.log4j.Logger;

public class EventDto {
	private Logger logger = Logger.getLogger(EventDto.class);

	private String eventName;

	private int beginTime;

	private int totalTime;

	private int dayIndex;

	private int status;

	private String url;

	// 节目中字符数据的字节长度
	public int getLen(Charset charset) {
		int len = 0;
		try {
			if (eventName != null && !"".equals(eventName)) {
				len += eventName.getBytes(charset).length;
			}
			if (url != null && !"".equals(url)) {
				len += url.getBytes(charset).length;
			}
		} catch (Exception e) {
			logger.error("节目信息转换为字节码错误...", e);
		}
		return len;
	}

	public String getEventName() {
		return eventName;
	}

	public void setEventName(String eventName) {
		this.eventName = eventName;
	}

	public int getBeginTime() {
		return beginTime;
	}

	public void setBeginTime(int beginTime) {
		this.beginTime = beginTime;
	}

	public int getTotalTime() {
		return totalTime;
	}

	public void setTotalTime(int totalTime) {
		this.totalTime = totalTime;
	}

	public int getDayIndex() {
		return dayIndex;
	}

	public void setDayIndex(int dayIndex) {
		this.dayIndex = dayIndex;
	}

	public int getStatus() {
		return status;
	}

	public void setStatus(int status) {
		this.status = status;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}
}
        c.解码器
package com.bijian.study.mina.codec;

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

import org.apache.log4j.Logger;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.demux.MessageDecoder;
import org.apache.mina.filter.codec.demux.MessageDecoderResult;

import com.bijian.study.mina.message.AbstrMessage;
import com.bijian.study.mina.message.ChannelInfoRequest;
import com.bijian.study.mina.message.ChannelInfoResponse;
import com.bijian.study.mina.message.EventDto;

public class MyMessageDecoder implements MessageDecoder {
	private Logger logger = Logger.getLogger(MyMessageDecoder.class);

	private Charset charset;

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

	// 检查给定的IoBuffer是否适合解码
	public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
		// 报头长度==6
		if (in.remaining() < 6) {
			return MessageDecoderResult.NEED_DATA;
		}

		// tag正常
		short tag = in.getShort();
		// 注意先把16进制标识值转换为short类型的十进制数据,然后与tag比较
		if (tag == (short) 0x0001 || tag == (short) 0x8001) {
			logger.info("请求标识符:" + tag);
		} else {
			logger.error("未知的解码类型....");
			return MessageDecoderResult.NOT_OK;
		}

		// 真实数据长度
		int len = in.getInt();
		if (in.remaining() < len) {
			return MessageDecoderResult.NEED_DATA;
		}

		return MessageDecoderResult.OK;
	}

	public MessageDecoderResult decode(IoSession session, IoBuffer in,
			ProtocolDecoderOutput out) throws Exception {
		logger.info("解码:" + in.toString());
		CharsetDecoder decoder = charset.newDecoder();
		AbstrMessage message = null;
		short tag = in.getShort(); // tag
		int len = in.getInt(); // len

		byte[] temp = new byte[len];
		in.get(temp); // 数据区

		// ===============解析数据做准备======================
		IoBuffer buf = IoBuffer.allocate(100).setAutoExpand(true);
		buf.put(temp);
		buf.flip(); // 为获取基本数据区长度做准备

		IoBuffer databuf = IoBuffer.allocate(100).setAutoExpand(true);
		databuf.putShort(tag);
		databuf.putInt(len);
		databuf.put(temp);
		databuf.flip(); // 为获取真实数据区长度做准备

		// ================开始解码=========================
		// 注意先把16进制标识值转换为short类型的十进制数据,然后与tag比较
		if (tag == (short) 0x0001) { // 服务端解码
			ChannelInfoRequest req = new ChannelInfoRequest();

			short channel_id = buf.getShort();
			byte channel_desc_len = buf.get();
			String channel_desc = null;
			if (channel_desc_len > 0) {
				channel_desc = buf.getString(channel_desc_len, decoder);
			}

			req.setChannel_id(channel_id);
			req.setChannel_desc(channel_desc);

			message = req;
		} else if (tag == (short) 0x8001) { // 客户端解码
			ChannelInfoResponse res = new ChannelInfoResponse();

			int channel_addr = buf.getInt();
			byte channel_len = buf.get();
			if (databuf.position() == 0) {
				databuf.position(channel_addr);
			}
			String channelName = null;
			if (channel_len > 0) {
				channelName = databuf.getString(channel_len, decoder);
			}
			res.setChannelName(channelName);

			short event_num = buf.getShort();
			EventDto[] events = new EventDto[event_num];
			for (int i = 0; i < event_num; i++) {
				EventDto edt = new EventDto();
				byte dayIndex = buf.get();

				buf.getInt();
				byte eventName_len = buf.get();
				String eventName = null;
				if (eventName_len > 0) {
					eventName = databuf.getString(eventName_len, decoder);
				}

				int beginTime = buf.getInt();
				short totalTime = buf.getShort();
				byte status = buf.get();

				buf.getInt();
				byte url_len = buf.get();
				String url = null;
				if (url_len > 0) {
					url = databuf.getString(url_len, decoder);
				}
				edt.setDayIndex(dayIndex);
				edt.setEventName(eventName);
				edt.setBeginTime(beginTime);
				edt.setTotalTime(totalTime);
				edt.setStatus(status);
				edt.setUrl(url);

				events[i] = edt;
			}
			res.setEvents(events);
			message = res;
		} else {
			logger.error("未找到解码器....");
		}
		out.write(message);
		// ================解码成功=========================
		return MessageDecoderResult.OK;
	}

	public void finishDecode(IoSession session, ProtocolDecoderOutput out)
			throws Exception {
	}
}
        d.编码器
package com.bijian.study.mina.codec;

import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;

import org.apache.log4j.Logger;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.apache.mina.filter.codec.demux.MessageEncoder;

import com.bijian.study.mina.message.AbstrMessage;
import com.bijian.study.mina.message.ChannelInfoRequest;
import com.bijian.study.mina.message.ChannelInfoResponse;
import com.bijian.study.mina.message.EventDto;

public class MyMessageEncoder implements MessageEncoder<AbstrMessage> {
	private Logger logger = Logger.getLogger(MyMessageEncoder.class);

	private Charset charset;

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

	public void encode(IoSession session, AbstrMessage message,
			ProtocolEncoderOutput out) throws Exception {
		IoBuffer buf = IoBuffer.allocate(100).setAutoExpand(true);
		buf.putShort(message.getTag());
		buf.putInt(message.getLen(charset));

		// ===========编码数据区===============
		if (message instanceof ChannelInfoRequest) {
			ChannelInfoRequest req = (ChannelInfoRequest) message;
			buf.putShort((short) req.getChannel_id());
			buf.put((byte) req.getChannel_desc().getBytes(charset).length);
			buf.putString(req.getChannel_desc(), charset.newEncoder());
		} else if (message instanceof ChannelInfoResponse) {
			ChannelInfoResponse res = (ChannelInfoResponse) message;
			CharsetEncoder encoder = charset.newEncoder();
			IoBuffer dataBuffer = IoBuffer.allocate(100).setAutoExpand(true); // 定义真实数据区
			int offset = res.getDataOffset(); // 偏移地址

			buf.putInt(offset); // 频道名称地址(偏移开始位置)
			byte channelName_len = 0;
			if (res.getChannelName() != null) {
				channelName_len = (byte) res.getChannelName().getBytes(charset).length;
			}
			buf.put(channelName_len);
			offset += channelName_len;
			if (channelName_len > 0) {
				dataBuffer.putString(res.getChannelName(), encoder);
			}

			EventDto[] events = res.getEvents();
			if (events != null) {
				buf.putShort((short) events.length);
				for (int i = 0; i < events.length; i++) {
					EventDto edt = events[i];

					buf.put((byte) edt.getDayIndex());

					buf.putInt(offset);
					String eventName = edt.getEventName();
					byte eventName_len = 0;
					if (eventName != null) {
						eventName_len = (byte) eventName.getBytes(charset).length;
					}
					offset += eventName_len;
					buf.put(eventName_len);
					if (eventName_len > 0) {
						dataBuffer.putString(eventName, encoder);
					}

					buf.putInt(edt.getBeginTime());
					buf.putShort((short) edt.getTotalTime());
					buf.put((byte) edt.getStatus());

					buf.putInt(offset);
					String url = edt.getUrl();
					byte url_len = 0;
					if (url != null) {
						url_len = (byte) url.getBytes(charset).length;
					}
					offset += url_len;
					buf.put(url_len);
					if (url_len > 0) {
						dataBuffer.putString(url, encoder);
					}
				}
			}

			// 真实数据追加在基本数据后面
			if (dataBuffer.position() > 0) {
				buf.put(dataBuffer.flip());
			}
		}

		// ==========编码成功=================
		buf.flip();
		logger.info("编码" + buf.toString());
		out.write(buf);
	}
}

        e.编解码器工厂

package com.bijian.study.mina.codec;

import org.apache.mina.filter.codec.demux.DemuxingProtocolCodecFactory;
import org.apache.mina.filter.codec.demux.MessageDecoder;
import org.apache.mina.filter.codec.demux.MessageEncoder;

import com.bijian.study.mina.message.AbstrMessage;

public class MyMessageCodecFactory extends DemuxingProtocolCodecFactory {
	private MessageDecoder decoder;

	private MessageEncoder<AbstrMessage> encoder;
	// 注册编解码器
	public MyMessageCodecFactory(MessageDecoder decoder,
			MessageEncoder<AbstrMessage> encoder) {
		this.decoder = decoder;
		this.encoder = encoder;
		addMessageDecoder(this.decoder);
		addMessageEncoder(AbstrMessage.class, this.encoder);
	}
}

        f.服务端和服务端处理类

package com.bijian.study.mina.server;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;

import org.apache.log4j.Logger;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSessionConfig;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.logging.LogLevel;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

import com.bijian.study.mina.codec.MyMessageCodecFactory;
import com.bijian.study.mina.codec.MyMessageDecoder;
import com.bijian.study.mina.codec.MyMessageEncoder;
import com.bijian.study.mina.handler.Demo2ServerHandler;

public class TestServer02 {
	private static Logger logger = Logger.getLogger(TestServer02.class);

	private static int PORT = 3005;

	public static void main(String[] args) {
		IoAcceptor acceptor = null;
		try {
			// 创建一个非阻塞的server端的Socket
			acceptor = new NioSocketAcceptor();

			// 设置过滤器(添加自带的编解码器)
			acceptor.getFilterChain().addLast(
					"codec",
					new ProtocolCodecFilter(new MyMessageCodecFactory(
							new MyMessageDecoder(Charset.forName("utf-8")),
							new MyMessageEncoder(Charset.forName("utf-8")))));
			// 设置日志过滤器
			LoggingFilter lf = new LoggingFilter();
			lf.setMessageReceivedLogLevel(LogLevel.DEBUG);
			acceptor.getFilterChain().addLast("logger", lf);
			// 获得IoSessionConfig对象
			IoSessionConfig cfg = acceptor.getSessionConfig();
			// 读写通道10秒内无操作进入空闲状态
			cfg.setIdleTime(IdleStatus.BOTH_IDLE, 100);

			// 绑定逻辑处理器
			acceptor.setHandler(new Demo2ServerHandler());
			// 绑定端口
			acceptor.bind(new InetSocketAddress(PORT));
			logger.info("服务端启动成功...     端口号为:" + PORT);
		} catch (Exception e) {
			logger.error("服务端启动异常....", e);
			e.printStackTrace();
		}
	}
}
package com.bijian.study.mina.handler;

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

import com.bijian.study.mina.message.ChannelInfoRequest;
import com.bijian.study.mina.message.ChannelInfoResponse;
import com.bijian.study.mina.message.EventDto;

public class Demo2ServerHandler extends IoHandlerAdapter {
	public static Logger logger = Logger.getLogger(Demo2ServerHandler.class);

	@Override
	public void sessionCreated(IoSession session) throws Exception {
		logger.info("服务端与客户端创建连接...");
	}

	@Override
	public void sessionOpened(IoSession session) throws Exception {
		logger.info("服务端与客户端连接打开...");
	}

	@Override
	public void messageReceived(IoSession session, Object message)
			throws Exception {
		if (message instanceof ChannelInfoRequest) {
			ChannelInfoRequest req = (ChannelInfoRequest) message;
			int channel_id = req.getChannel_id();
			String channel_desc = req.getChannel_desc();
			logger.info("服务端接收到的数据为:channel_id=" + channel_id
					+ "   channel_desc=" + channel_desc);
			// ================具体操作,比如查询数据库等,这里略....=============
			ChannelInfoResponse res = new ChannelInfoResponse();
			res.setChannelName("CCTV1高清频道");
			EventDto[] events = new EventDto[2];
			for (int i = 0; i < events.length; i++) {
				EventDto edt = new EventDto();
				edt.setBeginTime(10);
				edt.setDayIndex(1);
				edt.setEventName("风云第一的" + i);
				edt.setStatus(1);
				edt.setTotalTime(100 + i);
				edt.setUrl("www.baidu.com");
				events[i] = edt;
			}
			res.setEvents(events);
			session.write(res);
		} else {
			logger.info("未知请求!");
		}

	}

	@Override
	public void messageSent(IoSession session, Object message) throws Exception {
		session.close();
		logger.info("服务端发送信息成功...");
	}

	@Override
	public void sessionClosed(IoSession session) throws Exception {

	}

	@Override
	public void sessionIdle(IoSession session, IdleStatus status)
			throws Exception {
		logger.info("服务端进入空闲状态...");
	}

	@Override
	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
		logger.error("服务端发送异常...", cause);
	}
}

        g.客户端和客户端处理类

package com.bijian.study.mina.client;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;

import org.apache.log4j.Logger;
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.transport.socket.nio.NioSocketConnector;

import com.bijian.study.mina.codec.MyMessageCodecFactory;
import com.bijian.study.mina.codec.MyMessageDecoder;
import com.bijian.study.mina.codec.MyMessageEncoder;
import com.bijian.study.mina.handler.Demo2ClientHandler;
import com.bijian.study.mina.message.ChannelInfoRequest;

public class TestClient02 {
	private static Logger logger = Logger.getLogger(TestClient02.class);

	private static String HOST = "127.0.0.1";

	private static int PORT = 3005;

	public static void main(String[] args) {
		// 创建一个非阻塞的客户端程序
		IoConnector connector = new NioSocketConnector();
		// 设置链接超时时间
		connector.setConnectTimeout(30000);
		// 添加过滤器
		connector.getFilterChain().addLast(
				"codec",
				new ProtocolCodecFilter(new MyMessageCodecFactory(
						new MyMessageDecoder(Charset.forName("utf-8")),
						new MyMessageEncoder(Charset.forName("utf-8")))));
		// 添加业务逻辑处理器类
		connector.setHandler(new Demo2ClientHandler());
		IoSession session = null;
		try {
			ConnectFuture future = connector.connect(new InetSocketAddress(
					HOST, PORT));// 创建连接
			future.awaitUninterruptibly();// 等待连接创建完成
			session = future.getSession();// 获得session
			ChannelInfoRequest req = new ChannelInfoRequest(); // 发送请求
			req.setChannel_id(12345);
			req.setChannel_desc("mina在做测试哦哦....哇呀呀!!!");
			session.write(req);// 发送消息
		} catch (Exception e) {
			logger.error("客户端链接异常...", e);
		}

		session.getCloseFuture().awaitUninterruptibly();// 等待连接断开
		connector.dispose();
	}
}
package com.bijian.study.mina.handler;

import org.apache.log4j.Logger;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;

import com.bijian.study.mina.message.ChannelInfoResponse;
import com.bijian.study.mina.message.EventDto;

public class Demo2ClientHandler extends IoHandlerAdapter {
	private static Logger logger = Logger.getLogger(Demo2ClientHandler.class);

	@Override
	public void messageReceived(IoSession session, Object message)
			throws Exception {
		if (message instanceof ChannelInfoResponse) {
			ChannelInfoResponse res = (ChannelInfoResponse) message;
			String channelName = res.getChannelName();
			EventDto[] events = res.getEvents();
			logger.info("客户端接收到的消息为:channelName=" + channelName);
			if(events!=null && events.length>0){
				for (int i = 0; i < events.length; i++) {
					EventDto edt = events[i];
					logger.info("客户端接收到的消息为:BeginTime=" + edt.getBeginTime());
					logger.info("客户端接收到的消息为:DayIndex=" + edt.getDayIndex());
					logger.info("客户端接收到的消息为:EventName=" + edt.getEventName());
					logger.info("客户端接收到的消息为:Status=" + edt.getStatus());
					logger.info("客户端接收到的消息为:TotalTime=" + edt.getTotalTime());
					logger.info("客户端接收到的消息为:url=" + edt.getUrl());
				}
			}
		}else{
			logger.info("未知类型!");
		}
	}

	@Override
	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
		logger.error("客户端发生异常...", cause);
	}
}

        h.运行测试

        服务端输出信息:

2016-02-15 23:05:26,539 INFO  TestServer02  - 服务端启动成功...     端口号为:3005
2016-02-15 23:05:36,531 INFO  LoggingFilter  - CREATED
2016-02-15 23:05:36,547 INFO  Demo2ServerHandler  - 服务端与客户端创建连接...
2016-02-15 23:05:36,547 INFO  LoggingFilter  - OPENED
2016-02-15 23:05:36,548 INFO  Demo2ServerHandler  - 服务端与客户端连接打开...
2016-02-15 23:05:36,550 DEBUG ProtocolCodecFilter  - Processing a MESSAGE_RECEIVED for session 1
2016-02-15 23:05:36,552 INFO  MyMessageDecoder  - 请求标识符:1
2016-02-15 23:05:36,553 INFO  MyMessageDecoder  - 解码:HeapBuffer[pos=0 lim=53 cap=2048: 00 01 00 00 00 2F 30 39 2C 6D 69 6E 61 E5 9C A8...]
2016-02-15 23:05:36,554 DEBUG LoggingFilter  - RECEIVED: com.bijian.study.mina.message.ChannelInfoRequest@21ba1b
2016-02-15 23:05:36,554 INFO  Demo2ServerHandler  - 服务端接收到的数据为:channel_id=12345   channel_desc=mina在做测试哦哦....哇呀呀!!!
2016-02-15 23:05:36,558 INFO  MyMessageEncoder  - 编码HeapBuffer[pos=0 lim=124 cap=128: 80 01 00 00 00 76 00 00 00 31 11 00 02 01 00 00...]
2016-02-15 23:05:36,561 INFO  LoggingFilter  - SENT: com.bijian.study.mina.message.ChannelInfoResponse@487cc7
2016-02-15 23:05:36,561 INFO  Demo2ServerHandler  - 服务端发送信息成功...
2016-02-15 23:05:36,562 INFO  LoggingFilter  - CLOSED

        客户端输出信息:

2016-02-15 23:05:36,538 INFO  MyMessageEncoder  - 编码HeapBuffer[pos=0 lim=53 cap=100: 00 01 00 00 00 2F 30 39 2C 6D 69 6E 61 E5 9C A8...]
2016-02-15 23:05:36,563 DEBUG ProtocolCodecFilter  - Processing a MESSAGE_RECEIVED for session 1
2016-02-15 23:05:36,566 INFO  MyMessageDecoder  - 请求标识符:-32767
2016-02-15 23:05:36,567 INFO  MyMessageDecoder  - 解码:HeapBuffer[pos=0 lim=124 cap=2048: 80 01 00 00 00 76 00 00 00 31 11 00 02 01 00 00...]
2016-02-15 23:05:36,568 INFO  Demo2ClientHandler  - 客户端接收到的消息为:channelName=CCTV1高清频道
2016-02-15 23:05:36,568 INFO  Demo2ClientHandler  - 客户端接收到的消息为:BeginTime=10
2016-02-15 23:05:36,569 INFO  Demo2ClientHandler  - 客户端接收到的消息为:DayIndex=1
2016-02-15 23:05:36,569 INFO  Demo2ClientHandler  - 客户端接收到的消息为:EventName=风云第一的0
2016-02-15 23:05:36,569 INFO  Demo2ClientHandler  - 客户端接收到的消息为:Status=1
2016-02-15 23:05:36,569 INFO  Demo2ClientHandler  - 客户端接收到的消息为:TotalTime=100
2016-02-15 23:05:36,570 INFO  Demo2ClientHandler  - 客户端接收到的消息为:url=www.baidu.com
2016-02-15 23:05:36,570 INFO  Demo2ClientHandler  - 客户端接收到的消息为:BeginTime=10
2016-02-15 23:05:36,570 INFO  Demo2ClientHandler  - 客户端接收到的消息为:DayIndex=1
2016-02-15 23:05:36,570 INFO  Demo2ClientHandler  - 客户端接收到的消息为:EventName=风云第一的1
2016-02-15 23:05:36,570 INFO  Demo2ClientHandler  - 客户端接收到的消息为:Status=1
2016-02-15 23:05:36,571 INFO  Demo2ClientHandler  - 客户端接收到的消息为:TotalTime=101
2016-02-15 23:05:36,572 INFO  Demo2ClientHandler  - 客户端接收到的消息为:url=www.baidu.com

 

4.总结

        IoFilter是转码和解码用滴,它是Mina最值得研究的地方,建议阅读它的源码!

        在实际的应用开发中,自定义协议是必用的,因为很多客户端和服务端是不同语言实现的。

 

三.IoHandler接口

        IoHandler是Mina实现其业务逻辑的顶级接口;它相当简单,你就理解它是根据事件触发的简单应用程序即可。

        在IoHandler中定义了7个方法,根据I/O事件来触发对应的方法:

import java.io.IOException;
public interface IoHandler {
    void sessionCreated(IoSession session) throws Exception;
    void sessionOpened(IoSession session) throws Exception;
    void sessionClosed(IoSession session) throws Exception;
    void sessionIdle(IoSession session, IdleStatus status) throws Exception;
    void exceptionCaught(IoSession session, Throwable cause) throws Exception;
    void messageReceived(IoSession session, Object message) throws Exception;
    void messageSent(IoSession session, Object message) throws Exception;
}

        sessionCreated:当一个新的连接建立时,由I/O processor thread调用;

        sessionOpened:当连接打开是调用;

        messageReceived:当接收了一个消息时调用;

        messageSent:当一个消息被(IoSession#write)发送出去后调用;

        sessionIdle:当连接进入空闲状态时调用;

        sessionClosed:当连接关闭时调用;

        exceptionCaught:当实现IoHandler的类抛出异常时调用;

        一般情况下,我们最关心的只有messageReceived方法,接收消息并处理,然后调用IoSession的write方法发送出消息!(注意:这里接收到的消息都是Java对象,在IoFilter中所有二进制数据都被解码啦!)

        一般情况下很少有人实现IoHandler接口,而是继承它的一个实现类IoHandlerAdapter,这样不用覆盖它的7个方法,只需要根据具体需求覆盖其中的几个方法就可以!

        a.Iohandler的7个方法其实是根据session的4个状态值间变化来调用的:

        b.Connected:会话被创建并使用;

        c.Idle:会话在一段时间(可配置)内没有任何请求到达,进入空闲状态;

        d.Closing:会话将被关闭(剩余message将被强制flush);

        e.Closed:会话被关闭;

        状态转换图如下:

 

学习资料:http://wenku.baidu.com/link?url=VyKQnsn4b0BDJ8cQlLUu9cvpGz-Iou_499U4lJE9I0s5nPPY5kF5BDd8qo1yRMOiqsM8wxDPEL_S0koiFp8v5y36G9OGJydC2C12juo0bTW

  • 大小: 4.9 KB
  • 大小: 5.4 KB
  • 大小: 9.2 KB
分享到:
评论

相关推荐

    mina学习基础-入门实例-传输定长报文(三)

    在"mina学习基础-入门实例-传输定长报文(三)"这个主题中,我们将深入探讨如何使用Mina实现定长报文的传输,并且利用Mina内置的SSL过滤器进行报文加密。 首先,让我们了解什么是定长报文。在通信协议中,定长报文是...

    Mina2.0学习笔记(修订版)

    #### 二、Mina基础概念详解 **IoService接口** IoService接口是Mina中的关键组件,用于管理网络连接和数据传输。其主要职责包括: - 开启或关闭网络连接 - 注册或取消注册IoHandler - 启动或停止IoAcceptor和...

    mina2学习笔记

    #### 二、Mina基础深入 ##### 2.1 IoService接口 - **类结构**:IoService是Mina框架的核心接口,包含IoAcceptor和IoConnector两个实现,分别负责服务端和客户端的网络通信。 - **应用**:IoAcceptor用于服务端...

    mina框架资源包

    本资源包包含了实现Mina框架基础功能所需的组件,对于搭建即时通讯服务器至关重要。 1. **Mina框架核心**:`apache-mina-2.0.19-bin.zip` 这个文件是Mina框架的二进制发行版,包含了Mina运行所需的jar文件和其他可...

    mina基础技术

    ### Mina基础技术知识点 #### 一、MINA框架简介 **1.1 MINA是什么?** Apache MINA是一个强大的网络应用框架,旨在帮助开发者轻松构建高性能和高扩展性的网络应用。它通过Java NIO(非阻塞I/O)提供了一个抽象的...

    Apache Mina 2 完全自学手册

    #### Mina基础 **1. IoService接口** IoService是Mina中的核心接口之一,它定义了网络服务的基本操作,包括启动、停止服务以及连接管理。它是IoAcceptor和IoConnector的超类型,为它们提供统一的接口。 **2. ...

    Mina实现长连接和短连接实例

    在IT行业中,网络通信是至关重要的,特别是在分布式系统和服务器应用程序中。...通过学习和实践Mina实现长连接和短连接的例子,我们可以深入了解网络编程的细节,提升我们的技能,为构建复杂的企业级应用打下坚实基础。

    MIna中转服务

    这个过程涉及到Socket的创建、三次握手等网络通信的基础步骤。 2. 阶段二:数据传输 建立连接后,客户端可以将数据封装成Mina的I/O事件,如WriteRequest,然后发送给中转服务器。Mina框架会自动处理这些事件,进行...

    Mina案例+使用文档.pdf

    #### 三、Mina快速入门 ##### 1. 环境准备 - **下载Mina**:首先,访问Apache Mina官方网站(http://mina.apache.org/downloads.html)下载最新版本的Mina。Mina有1.0.x和1.1.x两个主要分支,其中1.0.x版本适合...

    Mina官方教程_中文版.rar

    《Mina官方教程_中文版》是一份专为学习Mina框架的中文教育资源,它涵盖了Mina框架的基础知识、核心概念以及实际应用技巧。Mina(MinA Socket API)是Apache软件基金会的一个开源项目,主要设计用于构建高性能、高可...

    Mina2源码分析

    IoService是所有网络通信的基础接口,无论是客户端还是服务器都需要通过该接口进行通信。IoService的主要职责包括: 1. **TransportMetadata**:提供底层传输层的信息,如使用的网络协议(NIO、RXTX等)。 2. **...

    mina传文件案例,客户端加服务端

    三、Mina服务端实现 1. 初始化IoAcceptor:Mina服务端首先需要创建一个IoAcceptor实例,用于监听特定端口的连接请求。通常使用NioSocketAcceptor,它基于Java的非阻塞I/O(NIO)模型,可以高效地处理大量并发连接。...

    mina服务器实例

    一、Mina基础概念 1. **事件驱动模型**:Mina采用I/O多路复用技术,如NIO(非阻塞I/O)和EPOLL,通过事件驱动模型处理网络连接,有效提升了并发处理能力。 2. **过滤器链**:Mina的过滤器链是其核心设计之一,它...

    高性能网络架构Mina框架 下载

    Mina在此基础上进行了优化和封装,提供了一个更加友好、易于使用的接口。 #### 六、Mina框架的学习资源 对于想要深入了解和学习Mina框架的开发者来说,可以从以下几个方面入手: 1. **官方文档**:Mina官方网站...

    socket 与 mina 交互数据

    Socket是TCP/IP通信的基础,而Mina是一个高效的网络应用框架,它简化了网络编程的复杂性。 Socket,也被称为套接字,是网络通信的基本单元。在Java中,Socket类提供了客户端和服务器之间的连接。通过Socket,我们...

    Apache MINA 2.0 用户指南中英文对照阅读版[带书签]

    第二章:基础知识 第三章:IO 服务 第四章:会话 第五章:过滤器 第六章:传输 第七章:事件处理器 第八章:字节缓存 第九章:编解码器过滤器 第十章:执行者过滤器 第十一章:SSL 过滤器 第十二章:日志过滤器 第十...

    Mina2进阶

    而在`lib`目录下,一般会包含Mina2的依赖库以及其他必要的第三方库,这些库支持Mina2的运行和我们的应用程序。 在实际开发中,我们需要关注以下几点: 1. **性能优化**:合理配置缓冲区大小、心跳机制以及线程池,...

    mina-core-2.0.0-RC1.jar,mina-filter-compression-2.0.0-RC1.jar

    在实际应用中,这三个组件通常一起使用,`mina-core`提供基础架构,`mina-filter-compression`负责数据压缩,而`mina-transport-apr`则优化了网络传输性能。通过这些组件,开发者可以构建出高效、可扩展且具有压缩...

    Mina学习资料

    Apache Mina是一个高度可扩展的网络通信框架,它为开发者提供了构建高性能、高可用性的网络应用程序的基础。Mina的核心理念是将网络通信的复杂性抽象出来,让开发者能够专注于业务逻辑,而不是底层的网络实现。在本...

Global site tag (gtag.js) - Google Analytics