練練手,用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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章