Mina框架jar包的導(dǎo)入
登錄http://mina.apache.org/downloads.html下載最新 mina壓縮包(我下的是apache-mina-2.0.13-bin.zip),解壓獲得mina-core-2.0.13.jar和slf4j-api-1.7.14.jar(注:slf4j-api-1.7.14.jar文件在apache-mina-2.0.13-bin.zip\apache-mina-2.0.13\lib目錄下)
Mina請求的基本配置
public class ConnectionManager {
private static final String TAG = "MinaClientDemo";
//創(chuàng)建Socket連接對象
private NioSocketConnector connector;
//創(chuàng)建服務(wù)器地址對象
private InetSocketAddress address;
private Context mContext;
private ConnectFuture future;
/**
* 構(gòu)造
* @param
*/
public ConnectionManager(Context context){
mContext= context;
init();
}
private void init() {
//獲取地址
address=new InetSocketAddress(ServerUtil.SERVER_IP,ServerUtil.SERVER_PORT);
//初始化連接對象
connector=new NioSocketConnector();
//配置連接參數(shù)
connector.getSessionConfig().setReadBufferSize(ServerUtil.SERVER_ReadBufferSize);
// connector.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 100);
connector.getFilterChain().addLast("exceutor", new ExecutorFilter());
connector.getFilterChain().addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool()));
// connector.getFilterChain().addLast("codec",
// new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"),
// LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue())));
//自定義編解碼器
connector.getFilterChain().addLast("mycoder", new ProtocolCodecFilter(new MyCodecFactory()));
//設(shè)置超時
connector.setConnectTimeoutCheckInterval(ServerUtil.SERVER_ConnectionTimeout);
connector.setHandler(new DefaultHandler(mContext));
connector.setDefaultRemoteAddress(address);
connector.addListener(new IoListener() {
@Override
public void sessionDestroyed(IoSession ioSession) throws Exception {
super.sessionDestroyed(ioSession);
Log.e(TAG,"斷開連接,開啟連接線程,正在嘗試重新連接......" );
ConnectionThread thread=new ConnectionThread(mContext);
thread.start();
//直接調(diào)用仍然會出現(xiàn)異常
//CheckConnect();
}
});
}
/**
* 與服務(wù)器連接的方法
* 連接過程總可能拋出的各種異常
*/
public void CheckConnect(){
Log.e(TAG, "CheckConnect: 正在準(zhǔn)備連接服務(wù)器......");
try {
Log.i(TAG, "CheckConnect: 正在檢查網(wǎng)絡(luò)配置......");
if (CheckNetWork(mContext)){
Log.e(TAG, "CheckConnect: 網(wǎng)絡(luò)正常,嘗試連接......" );
future=connector.connect(address);
//等待連接創(chuàng)建成功
future.awaitUninterruptibly();
}else{
Log.e(TAG, "CheckConnect: 網(wǎng)絡(luò)異常,10秒后再次啟動連接......");
Thread.sleep(10000);
SessionManager.getInstance().removeSession();
}
}catch (IllegalStateException e){
//future運行占用資源,死鎖
Log.e(TAG, "死鎖異常"+e.getMessage());
}catch (RuntimeIoException e) {
Log.e(TAG, "連接異常,無法獲取session,抓到的異常信息-----"+e.getMessage());
}catch (Exception e){
Log.e("狀態(tài)...", "connect: 連接失敗"+e.getMessage());
}
}
/**
* 檢查網(wǎng)絡(luò)連接的方法
* @param mContext
* @return 連接狀態(tài)返回[true],斷開狀態(tài)返回[false]
*/
private static boolean CheckNetWork(Context mContext) {
ConnectivityManager connectivityManager = (ConnectivityManager) mContext
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = connectivityManager.getActiveNetworkInfo();
if (info == null || !info.isAvailable()) {
return false;
} else {
return true;
}
}
/**
* 斷開連接
*/
public void disConnect(){
connector.dispose();
connector=null;
address=null;
Log.e("tag", "斷開連接");
}
private class DefaultHandler extends IoHandlerAdapter {
private Context mContext;
private DefaultHandler(Context context) {
this.mContext = context;
}
@Override
public void sessionCreated(IoSession session) throws Exception {
super.sessionCreated(session);
SessionManager.getInstance().setSession(session);
Log.i(TAG, "sessionCreated");
}
@Override
public void sessionOpened(IoSession session) throws Exception {
super.sessionOpened(session);
Log.d(TAG, "連接打開===> ID ====>" + session.getId());
}
@Override
public void sessionClosed(IoSession session) throws Exception {
super.sessionClosed(session);
Log.e(TAG, "sessionClosed");
SessionManager.getInstance().closeSession();
SessionManager.getInstance().removeSession();
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
Log.d(TAG, "收到數(shù)據(jù),接下來你要怎么解析數(shù)據(jù)就是你的事了");
IoBuffer buf = (IoBuffer) message;
HandlerEvent.getInstance().handle(buf);
}
@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
super.sessionIdle(session, status);
Log.e(TAG, "sessionIdle");
SessionManager.getInstance().closeSession();
}
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
super.exceptionCaught(session, cause);
Log.e(TAG, "--------------連接發(fā)生異常------------" + cause.toString());
SessionManager.getInstance().closeSession();
}
}
}
長連接的Service
public class MinaService extends Service {
private static final String TAG = "MinaClientDemo";
private ConnectionThread thread;
private ScheduledExecutorService executor;
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onStartCommand:.........................");
//啟動連接
if (thread != null) {
thread.interrupt();
thread = null;
}
thread = new ConnectionThread(this);
thread.start();
executeFixedRate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
thread.DisConnect();
}
/**
* 以固定周期頻率執(zhí)行任務(wù)
*/
public void executeFixedRate() {
if (executor != null) {
executor.shutdown();
executor = null;
}
executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(
new EchoServer(),
0,
1000 * 3,
TimeUnit.MILLISECONDS);
}
class EchoServer implements Runnable {
@Override
public void run() {
sendMsg("001////0124646846");
}
}
/**
* 給服務(wù)器發(fā)送一條消息
*/
public void sendMsg(String msg) {
/**
* 假定消息格式為:消息頭(一個short類型:表示事件號、一個int類型:表示消息體的長度)+消息體
*/
MinaMsgHead msgHead = new MinaMsgHead();
msgHead.bodyLen = msg.length();
IoBuffer buffer = IoBuffer.allocate(4 + msgHead.bodyLen);
buffer.putInt(msgHead.bodyLen);
try {
byte[] b = msg.getBytes("gbk");
String sa = new String(b, "gbk");
b = sa.getBytes("utf-8");
buffer.put(b);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (SessionManager.getInstance().CheckSession()){
//發(fā)送
Log.e("TAG","發(fā)送成功!");
SessionManager.getInstance().writeToServer(buffer);
}else {
Log.e("TAG","發(fā)送失敗!");
}
}
}
長連接的線程
public class MinaService extends Service {
private static final String TAG = "MinaClientDemo";
private ConnectionThread thread;
private ScheduledExecutorService executor;
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onStartCommand:.........................");
//啟動連接
if (thread != null) {
thread.interrupt();
thread = null;
}
thread = new ConnectionThread(this);
thread.start();
executeFixedRate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
thread.DisConnect();
}
/**
* 以固定周期頻率執(zhí)行任務(wù)
*/
public void executeFixedRate() {
if (executor != null) {
executor.shutdown();
executor = null;
}
executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(
new EchoServer(),
0,
1000 * 3,
TimeUnit.MILLISECONDS);
}
class EchoServer implements Runnable {
@Override
public void run() {
sendMsg("001////0124646846");
}
}
/**
* 給服務(wù)器發(fā)送一條消息
*/
public void sendMsg(String msg) {
/**
* 假定消息格式為:消息頭(一個short類型:表示事件號、一個int類型:表示消息體的長度)+消息體
*/
MinaMsgHead msgHead = new MinaMsgHead();
msgHead.bodyLen = msg.length();
IoBuffer buffer = IoBuffer.allocate(4 + msgHead.bodyLen);
buffer.putInt(msgHead.bodyLen);
try {
byte[] b = msg.getBytes("gbk");
String sa = new String(b, "gbk");
b = sa.getBytes("utf-8");
buffer.put(b);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (SessionManager.getInstance().CheckSession()){
//發(fā)送
Log.e("TAG","發(fā)送成功!");
SessionManager.getInstance().writeToServer(buffer);
}else {
Log.e("TAG","發(fā)送失敗!");
}
}
}
添加解碼器
public class MyCodecFactory implements ProtocolCodecFactory {
private MyDataDecoder decoder;
private MyDataEncoder encoder;
public MyCodecFactory() {
encoder = new MyDataEncoder();
decoder = new MyDataDecoder();
}
@Override
public ProtocolDecoder getDecoder(IoSession session) throws Exception {
return decoder;
}
@Override
public ProtocolEncoder getEncoder(IoSession session) throws Exception {
return encoder;
}
}
public class MyDataDecoder extends CumulativeProtocolDecoder {
/**
* 返回值含義:
* 1、當(dāng)內(nèi)容剛好時,返回false,告知父類接收下一批內(nèi)容
* 2、內(nèi)容不夠時需要下一批發(fā)過來的內(nèi)容,此時返回false,這樣父類 CumulativeProtocolDecoder
* 會將內(nèi)容放進IoSession中,等下次來數(shù)據(jù)后就自動拼裝再交給本類的doDecode
* 3、當(dāng)內(nèi)容多時,返回true,因為需要再將本批數(shù)據(jù)進行讀取,父類會將剩余的數(shù)據(jù)再次推送本類的doDecode方法
*/
@Override
public boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)
throws Exception {
/**
* 假定消息格式為:消息頭(int類型:表示消息體的長度、short類型:表示事件號)+消息體
*/
if (in.remaining() < 4)//是用來當(dāng)拆包時候剩余長度小于4的時候的保護,不加容易出錯
{
return false;
}
if (in.remaining() > 1) {
//以便后繼的reset操作能恢復(fù)position位置
in.mark();
////前6字節(jié)是包頭,一個int和一個short,我們先取一個int
int len = in.getInt();//先獲取包體數(shù)據(jù)長度值
//比較消息長度和實際收到的長度是否相等,這里-2是因為我們的消息頭有個short值還沒取
// if (len > in.remaining() - 2) {
if (len > in.remaining() ) {
//出現(xiàn)斷包,則重置恢復(fù)position位置到操作前,進入下一輪, 接收新數(shù)據(jù),以拼湊成完整數(shù)據(jù)
in.reset();
return false;
} else {
//消息內(nèi)容足夠
in.reset();//重置恢復(fù)position位置到操作前
// int sumLen = 6 + len;//總長 = 包頭+包體
int sumLen = 4 + len;//總長 = 包頭+包體
byte[] packArr = new byte[sumLen];
in.get(packArr, 0, sumLen);
IoBuffer buffer = IoBuffer.allocate(sumLen);
buffer.put(packArr);
buffer.flip();
out.write(buffer);
//走到這里會調(diào)用DefaultHandler的messageReceived方法
if (in.remaining() > 0) {//出現(xiàn)粘包,就讓父類再調(diào)用一次,進行下一次解析
return true;
}
}
}
return false;//處理成功,讓父類進行接收下個包
}
}
/**
* 編碼器將數(shù)據(jù)直接發(fā)出去(不做處理)
*/
public class MyDataEncoder extends ProtocolEncoderAdapter {
@Override
public void encode(IoSession session, Object message,
ProtocolEncoderOutput out) throws Exception {
IoBuffer value = (IoBuffer) message;
out.write(value);
out.flush();
}
}
需要注意的地方
1.能收到服務(wù)器發(fā)送過來的數(shù)據(jù),但是顯示如下樣式,無法進行到自己定義的數(shù)據(jù)解析
D/o.a.m.f.c.Protoco: Processing a MESSAGE_RECEIVED for session 2
這種情況一般是因為自己配置的解碼器沒有正確的解碼出來服務(wù)器推送的數(shù)據(jù)導(dǎo)致的。請參考上文添加解碼器部分,在Mina基本配置里面配置好相應(yīng)的解碼器。
2.能連上服務(wù)器,但是發(fā)送的消息無法成功發(fā)送
這個需要和后臺定義好通訊協(xié)議,在demo中我是用的是IoBuffer,兩邊定義好事件號和消息體,并且需要在解碼器中配置好對應(yīng)的解碼的消息類型。demo中我并沒有使用事件號,而是直接將標(biāo)示位和數(shù)據(jù)位一起放在了消息體中。
3.后臺無法解析發(fā)送上去的數(shù)據(jù)
請檢查前后端定義的消息是否一致(事件號+消息體),定義的長度是否一致。
最后附上demo地址:https://download.csdn.net/download/d38825/10303736