mina2用NioSocketConnector连接,以前mina1的时候叫SocketConnector,现在SocketConnector是一个接口,要注意下区别。
需要的东西,一个handler来处理各种io事件,一个ProtocolCodecFactory来对消息进行编码(ProtocolEncoder)和解码(ProtocolDecoder)。此外还需要自己编写一个MessageHandler来处理收到的信息。
编写一个类比如SxdHandler继承自IoHandlerAdapter,实现messageReceived,sessionCreated等方法。
编写一个类比如SxdCodecFactory继承自ProtocolCodecFactory来返回单例的编码者和解码者。
编写一个类比如SxdEncoder继承自ProtocolEncoder,来对自己的信息编码,编为IoBuffer输出。
编写一个类比如SxdDecoder继承自ProtocolDecoder,来对收到的字节缓冲区进行解析,解析成自己的消息放到ProtocolDecoderOutput,这样iohandler的messageReceived方法被调用,这个时候找到该信息对应的messagehandler,来进行消息处理。
程序流程,先创建一个NioSocketConnector,设置handel类,连接目标服务器,创建协议分析器到过滤器链中,然后就是等待操作了。
当连接打开时,iohandler的sessionOpened方法被调用,在这里发送比如登陆服务器的消息就行了,当收到消息,messageReceived会被调用,找到这个消息的handler,开始处理就行了。
下面是创建client实例代码:
NioSocketConnector conn = new NioSocketConnector(); conn.setConnectTimeoutMillis(30*1000l); conn.setHandler(new SxdHandler()); ConnectFuture cf = conn.connect(new InetSocketAddress("8x007.xd.com", 8002)); conn.getFilterChain().addLast("codec", new ProtocolCodecFilter(new SxdCodecFactory())); conn.getFilterChain().addLast( "logging", new LoggingFilter() ); cf.awaitUninterruptibly(); cf.getSession().getCloseFuture().awaitUninterruptibly(); conn.dispose();
关键的一点如何设计消息,消息分C->S的发送消息(CGMessage)和S->C的接受信息(GCMessage),
消息最后是通过字节流在网上传输的,因此需要知道消息的长度,然后是类型,最后是数据客户端和服务端按照相同协议解析和发送消息。
这是消息的抽象定义,提供了读写方法。
import java.io.UnsupportedEncodingException; import org.apache.mina.core.buffer.IoBuffer; public abstract class Message { protected IoBuffer buff; public byte module; public byte func; public Message(byte module, byte func){ this.module = module; this.func = func; }; int length = 0; public void write(){ if(buff == null){ buff = IoBuffer.allocate(0).setAutoExpand(true); } buff.clear(); buff.putInt(0);//先占位,再 buff.put(module); buff.put(func); writeImpl(); length = buff.position() - 4; buff.putInt(0, length); // buff.capacity(length + 4); buff.flip(); } public abstract void writeImpl(); public void read(){ length = buff.getInt() - 4; buff.get(); buff.get(); this.readImpl(); } public abstract void readImpl(); public IoBuffer getBuff() { return buff; } public void setBuff(IoBuffer buff) { this.buff = buff; } protected void writeString(String s){ buff.putShort((short) s.length()); try { buff.put(s.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } protected void writeInt(int i){ buff.putInt(i); } protected void writeShort(short s){ buff.putShort(s); } public int getLength() { return length + 4; } protected String readString(){ short len = buff.getShort(); byte[] bs =new byte[len]; buff.get(bs); try { return new String(bs, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return new String(bs); } } protected int readInt(){ return buff.getInt(); } protected short readShort(){ return buff.getShort(); } protected byte readByte(){ return buff.get(); } protected long readLong(){ return buff.getLong(); } public String getKey(){ return module+"_"+func; } }
针对GCMessage,还需要编写一个MessageHandler来处理这个message,在统一的一个类中进行注册和查找。
附件是练手写的连接神仙道服务器的例子,可以收到服务器发来的玩家基本信息。