title: Mina服务端客户端通讯
date: 2018-09-30 09:00:30
tags:编程
- [mina] - [tcp]
categories:segmentfault
- [编程]
[TOC]缓存
前两章节已经完整的介绍了理论部分,今天咱们就利用这些理论来实现tcp协议的c/s 通讯。首先咱们简单回顾下以前的介绍,
在mina中咱们的客户端和服务端简直就是如出一辙,只是咱们用不一样适配器。可是他的数据处理流程是同样的。今天咱们就重点看看如何创建服务端、客户端
而且处理二者之间的消息通讯处理微信
服务端和客户端不一样的就是咱们建立的监听对象不一样而已,客户端发送消息到服务端,服务端须要经历过滤器的处理才能到达消息中心,可是在过滤器中咱们就须要将消息进行解码,而后才会到消息接收的地方处理咱们的业务。正常状况下咱们处理完消息须要对客户端进行回应。回应的时候也会经历过滤器中的编码逻辑,进行数据编码而后发送。信息发送到客户端咱们能够当作服务端的方向。也是须要进行编解码的。下面看看服务端的建立代码session
//建立监听对象 IoAcceptor acceptor = new NioSocketAcceptor(); TextLineCodecFactory textLineCodecFactory = new TextLineCodecFactory(Charset.forName("utf-8"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue()); //添加过滤器 acceptor.getFilterChain().addLast("logger",new LoggingFilter()); acceptor.getFilterChain().addLast("protocal",new ProtocolCodecFilter( textLineCodecFactory )); //设置时间处理的handler acceptor.setHandler(new ServerMessageHandler()); //设置读取数据缓存区的大小 acceptor.getSessionConfig().setReadBufferSize(Constaint.READSIZE); //设置多久没有消息就进入空闲状态 acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE,Constaint.IDLETIME); //绑定端口 try { acceptor.bind(new InetSocketAddress(Constaint.REMOTE_PORT)); } catch (IOException e) { logger.error(String.format("bind %s error",Constaint.REMOTE_PORT)); e.printStackTrace(); } logger.info(String.format("bind %s success",Constaint.REMOTE_PORT));
//建立监听对象 IoConnector connector = new NioSocketConnector(); TextLineCodecFactory textLineCodecFactory = new TextLineCodecFactory(Charset.forName("utf-8"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue()); //添加过滤器 //日志过滤器 。 sltf日志设置 connector.getFilterChain().addLast("logger",new LoggingFilter()); //在这个过滤器中提供了编解码,这里的编码是以信息中已\r\n结尾算是一条信息 connector.getFilterChain().addLast("protocal",new ProtocolCodecFilter( new SocketFactory() )); //设置时间处理的handler , 提供session生命周期的监听函数,消息接受,发送的函数 connector.setHandler(new ClientMessageHandler()); //设置读取数据缓存区的大小 connector.getSessionConfig().setReadBufferSize(Constaint.READSIZE); //设置多久没有消息就进入空闲状态 connector.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE,Constaint.IDLETIME); ConnectFuture future = connector.connect(new InetSocketAddress(Constaint.REMOTE_IP,Constaint.REMOTE_PORT)); //是异步处理,这里不会形成阻塞 future.addListener(new IoFutureListener<IoFuture>() { @Override public void operationComplete(IoFuture ioFuture) { logger.info("链接准备完成"); IoSession session = ioFuture.getSession(); } });
private static Logger logger = LogManager.getLogger(ServerMessageHandler.class); public void sessionCreated(IoSession session) throws Exception { super.sessionCreated(session); logger.info("sessionCreated"); } public void sessionOpened(IoSession session) throws Exception { super.sessionOpened(session); try { IoBuffer buffer = IoBuffer.allocate(30); buffer.clear(); buffer.putString("quit\r\n", Charset.forName("utf-8").newEncoder()); buffer.flip(); session.write(buffer); } catch (Exception e) { logger.error(e.toString()); } logger.info("sessionOpened"); } public void sessionClosed(IoSession session) throws Exception { super.sessionClosed(session); logger.info("sessionClosed"); } public void sessionIdle(IoSession session, IdleStatus idleStatus) throws Exception { super.sessionIdle(session,idleStatus); try { IoBuffer buffer = IoBuffer.allocate(30); buffer.clear(); buffer.putString("quit\r\n", Charset.forName("utf-8").newEncoder()); buffer.flip(); session.write(buffer); } catch (Exception e) { logger.error(e.toString()); } // logger.info("sessionIdle"); } public void exceptionCaught(IoSession ioSession, Throwable throwable) throws Exception { logger.info("exceptionCaught"); throwable.printStackTrace(); } public void messageReceived(IoSession session, Object message) throws Exception { super.messageReceived(session, message); String info = message.toString(); Date date = new Date(System.currentTimeMillis()); SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd HH:mm:ss"); String time = sdf.format(date); session.write(time); System.out.println("接收到的消息:"+info); } public void messageSent(IoSession session, Object message) throws Exception { super.messageSent(session, message); logger.info("messageSent"); }
public class SocketFactory implements ProtocolCodecFactory { private MessageDecoder decoder; private MessageEncoder encoder; public SocketFactory() { decoder = new MessageDecoder(); encoder = new MessageEncoder(); } public ProtocolDecoder getDecoder(IoSession session) throws Exception { return this.decoder; } public ProtocolEncoder getEncoder(IoSession session) throws Exception { return this.encoder; } }
解码器写好以后只须要在上面自定义工厂中建立就行了。至于自定义编码器只须要继承CumulativeProtocolDecoder这个类就行了。并且复写doDecode方法就行了。这个方法的返回值是boolean类型。返回值不一样表明意义不一。这里须要重点理清楚异步
记住三种状况 半包 、 正常 、 粘包tcp
public class MessageDecoder extends CumulativeProtocolDecoder { /** * 此方法return true : 表示父类中CumulativeProtocolDecoder会不断的调用此方法进行消息的消费 * return false: 表示消息已经消费彻底了,缓存中就算有数据也不会再消费了。等待再次客户端 * 发送消息时会触发消息发送接口,此时会将新旧消息拼接再一块儿进行处理 * @param ioSession * @param ioBuffer * @param protocolDecoderOutput * @return * @throws Exception */ @Override protected boolean doDecode(IoSession ioSession, IoBuffer ioBuffer, ProtocolDecoderOutput protocolDecoderOutput) throws Exception { IoBuffer buffer = IoBuffer.allocate(10); while (ioBuffer.hasRemaining()) { if (ioBuffer.remaining()<3) { //继续接受 return false; } //获取三个字节 int oldLimit = ioBuffer.limit(); ioBuffer.limit(ioBuffer.position()+3); String text = ioBuffer.getString(Charset.forName("UTF-8").newDecoder()); protocolDecoderOutput.write(text); ioBuffer.limit(oldLimit); if (ioBuffer.hasRemaining()) { return true; } } return false; } }
public class MessageEncoder extends ProtocolEncoderAdapter { @Override public void encode(IoSession ioSession, Object o, ProtocolEncoderOutput protocolEncoderOutput) throws Exception { //TODO 根据协议编码 //组装好以后 ioSession.write(IoBuffer)写出 System.out.println(o); } }
加入战队ide