Mina框架jar包的導入
登錄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";
//創建Socket連接對象
private NioSocketConnector connector;
//創建服務器地址對象
private InetSocketAddress address;
private Context mContext;
private ConnectFuture future;
/**
* 構造
* @param
*/
public ConnectionManager(Context context){
mContext= context;
init();
}
private void init() {
//獲取地址
address=new InetSocketAddress(ServerUtil.SERVER_IP,ServerUtil.SERVER_PORT);
//初始化連接對象
connector=new NioSocketConnector();
//配置連接參數
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()));
//設置超時
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();
//直接調用仍然會出現異常
//CheckConnect();
}
});
}
/**
* 與服務器連接的方法
* 連接過程總可能拋出的各種異常
*/
public void CheckConnect(){
Log.e(TAG, "CheckConnect: 正在準備連接服務器......");
try {
Log.i(TAG, "CheckConnect: 正在檢查網絡配置......");
if (CheckNetWork(mContext)){
Log.e(TAG, "CheckConnect: 網絡正常,嘗試連接......" );
future=connector.connect(address);
//等待連接創建成功
future.awaitUninterruptibly();
}else{
Log.e(TAG, "CheckConnect: 網絡異常,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("狀態...", "connect: 連接失敗"+e.getMessage());
}
}
/**
* 檢查網絡連接的方法
* @param mContext
* @return 連接狀態返回[true],斷開狀態返回[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, "收到數據,接下來你要怎么解析數據就是你的事了");
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, "--------------連接發生異常------------" + 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();
}
/**
* 以固定周期頻率執行任務
*/
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");
}
}
/**
* 給服務器發送一條消息
*/
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()){
//發送
Log.e("TAG","發送成功!");
SessionManager.getInstance().writeToServer(buffer);
}else {
Log.e("TAG","發送失敗!");
}
}
}
長連接的線程
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();
}
/**
* 以固定周期頻率執行任務
*/
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");
}
}
/**
* 給服務器發送一條消息
*/
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()){
//發送
Log.e("TAG","發送成功!");
SessionManager.getInstance().writeToServer(buffer);
}else {
Log.e("TAG","發送失敗!");
}
}
}
添加解碼器
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、當內容剛好時,返回false,告知父類接收下一批內容
* 2、內容不夠時需要下一批發過來的內容,此時返回false,這樣父類 CumulativeProtocolDecoder
* 會將內容放進IoSession中,等下次來數據后就自動拼裝再交給本類的doDecode
* 3、當內容多時,返回true,因為需要再將本批數據進行讀取,父類會將剩余的數據再次推送本類的doDecode方法
*/
@Override
public boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)
throws Exception {
/**
* 假定消息格式為:消息頭(int類型:表示消息體的長度、short類型:表示事件號)+消息體
*/
if (in.remaining() < 4)//是用來當拆包時候剩余長度小于4的時候的保護,不加容易出錯
{
return false;
}
if (in.remaining() > 1) {
//以便后繼的reset操作能恢復position位置
in.mark();
////前6字節是包頭,一個int和一個short,我們先取一個int
int len = in.getInt();//先獲取包體數據長度值
//比較消息長度和實際收到的長度是否相等,這里-2是因為我們的消息頭有個short值還沒取
// if (len > in.remaining() - 2) {
if (len > in.remaining() ) {
//出現斷包,則重置恢復position位置到操作前,進入下一輪, 接收新數據,以拼湊成完整數據
in.reset();
return false;
} else {
//消息內容足夠
in.reset();//重置恢復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);
//走到這里會調用DefaultHandler的messageReceived方法
if (in.remaining() > 0) {//出現粘包,就讓父類再調用一次,進行下一次解析
return true;
}
}
}
return false;//處理成功,讓父類進行接收下個包
}
}
/**
* 編碼器將數據直接發出去(不做處理)
*/
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.能收到服務器發送過來的數據,但是顯示如下樣式,無法進行到自己定義的數據解析
D/o.a.m.f.c.Protoco: Processing a MESSAGE_RECEIVED for session 2
這種情況一般是因為自己配置的解碼器沒有正確的解碼出來服務器推送的數據導致的。請參考上文添加解碼器部分,在Mina基本配置里面配置好相應的解碼器。
2.能連上服務器,但是發送的消息無法成功發送
這個需要和后臺定義好通訊協議,在demo中我是用的是IoBuffer,兩邊定義好事件號和消息體,并且需要在解碼器中配置好對應的解碼的消息類型。demo中我并沒有使用事件號,而是直接將標示位和數據位一起放在了消息體中。
3.后臺無法解析發送上去的數據
請檢查前后端定義的消息是否一致(事件號+消息體),定義的長度是否一致。
最后附上demo地址:https://download.csdn.net/download/d38825/10303736