练练手,用mina2.0搭建一个nio客户端

练练手,用mina2.0搭建一个nio客户端,连接服务器。

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,在统一的一个类中进行注册和查找。

附件是练手写的连接神仙道服务器的例子,可以收到服务器发来的玩家基本信息。

猜你喜欢

转载自hfutfei.iteye.com/blog/1517487