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

附件是练手写的连接神仙道服务器的例子,可以收到服务器发来的玩家基本信息。
发布了21 篇原创文章 · 获赞 0 · 访问量 3523
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章