Mina框架-Android消息发送的服务端和客户端实现

Mina是Apache提供的socket框架。Mina提供基于Java NIO的Reactor网络模型API,并且封装了会话层、表示层,减轻开发者开发编码器、解码器的负担,即便不需要NIO的特性也能很大程序的提高开发效率。通过Mina内置的API可以方便地解决粘包缺包问题,方便的将字节报文转换为应用层消息报文,通过IDLE事件可以轻松开发心跳协议,支持并发地处理应用层报文,满足于文件的并发随机读写需求。

 

官网地址:http://mina.apache.org/mina-project/index.html

 

在Android应用开发时,通过官网下载后解压,一般需要用到的包如下,导入到libs中:

       mina-core-2.0.17.jar   //核心包

       slf4j-api-1.7.25.jar   //辅助使用,提供如Log工具类等,必须

 

Mina的简单实用

服务端 MinaServer

 
  1. try {
  2. NioSocketAcceptor acceptor = new NioSocketAcceptor();
  3. acceptor.setHandler(new ServerHandler); //设置消息处理
  4. acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory()));
  5. acceptor.bind(new InetSocketAddress(8989)); //指定端口号
  6. } catch (IOException e) {
  7. e.printStackTrace();
  8. }

消息处理过程 ServerHandler

 
  1. public class ServerHandler extends IoHandlerAdapter {
  2.  
  3. @Override
  4. public void sessionCreated(IoSession session) throws Exception {
  5. Log.d(TAG, "sessionCreated");
  6. }
  7.  
  8. @Override
  9. public void sessionOpened(IoSession session) throws Exception {
  10. //建立连接
  11. Log.d(TAG, "sessionOpened");
  12. }
  13.  
  14. @Override
  15. public void sessionClosed(IoSession session) throws Exception {
  16. //关闭连接
  17. Log.d(TAG, "sessionClosed");
  18.  
  19. }
  20.  
  21. @Override
  22. public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
  23. //空闲
  24. Log.d(TAG, "sessionIdle status: " + status.toString());
  25.  
  26. }
  27.  
  28. @Override
  29. public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
  30. //异常
  31. Log.e(TAG, "exceptionCaught cause: " + cause);
  32.  
  33. }
  34.  
  35. @Override
  36. public void messageReceived(IoSession session, Object message) throws Exception {
  37. //接收消息
  38. Log.d(TAG, "messageReceived message: " + message.toString());
  39.  
  40. }
  41.  
  42. @Override
  43. public void messageSent(IoSession session, Object message) throws Exception {
  44. //发送消息
  45. Log.d(TAG, "messageSent message: " + message.toString());
  46.  
  47. }
  48. }

 

客户端 MinaClient

 
  1. NioSocketConnector connector = new NioSocketConnector();
  2. connector.setHandler(new ClientHandler());
  3. connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory()));
  4. InetSocketAddress address = new InetSocketAddress(ip, 8989); //指定服务端IP和端口号
  5. ConnectFuture future = connector.connect(address);
  6. future.awaitUninterruptibly();
  7. ioSession = future.getSession();
  8.  
  9. if (ioSession.isConnected()) {
  10. ioSession.write("Hi Server"); //发送消息
  11. }

ClientHandler的实现与Server端是一样的。

 

 

Mina实现Android消息发送的服务端和客户端

前一段时间由于项目需要,需要在两个android设备间实现一个socket消息传送的功能,一个作为服务端,一个作为客户端,建立连接后可以相互发送消息。

 

首先,看一下具体实现涉及的类:

服务端实现:

 
  1. public class ConnectServer {
  2. private ConnectThread connectThread;
  3. private IoSession ioSession;
  4. private IConnectListener communicationListener;
  5.  
  6. public ConnectServer() {
  7. connectThread = new ConnectThread();
  8. connectThread.start();
  9. }
  10.  
  11. public void setCommunicationListener(IConnectListener listener) {
  12. this.communicationListener = listener;
  13. }
  14.  
  15. public void closeConnectThread() {
  16. if (connectThread != null) {
  17. connectThread.interrupt();
  18. connectThread.disConnect();
  19. }
  20. }
  21.  
  22. private class ConnectThread extends Thread {
  23. NioSocketAcceptor acceptor;
  24.  
  25. @Override
  26. public void run() {
  27. super.run();
  28. try {
  29. acceptor = new NioSocketAcceptor();
  30. TransferControlHandler handler = new TransferControlHandler();
  31. handler.setHandlerListener(handlerListener);
  32. acceptor.setHandler(handler); //指定消息处理类
  33. //指定消息编解码处理类
  34. acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TransferControlCodecFactory()));
  35.  
  36. //心跳检测
  37. KeepAliveFilter keepAliveFilter = new KeepAliveFilter(new TransferKeepAliveFactory(), IdleStatus.BOTH_IDLE);
  38. keepAliveFilter.setRequestInterval(5); //设置心跳包请求时间间隔,单位秒
  39. keepAliveFilter.setRequestTimeout(10); //设置超时时间,单位秒
  40. acceptor.getFilterChain().addLast("heartbeat", keepAliveFilter);
  41.  
  42. acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 11); //空闲时间间隔,单位秒
  43. //解决exception: bind failed: EADDRINUSE (Address already in use). 用户未正常退出server,client又很快请求连接时出现
  44. acceptor.setReuseAddress(true);
  45.  
  46. acceptor.bind(new InetSocketAddress(CommunicationProtocol.PORT));
  47. } catch (IOException e) {
  48. e.printStackTrace();
  49. }
  50. }
  51.  
  52. public void disConnect() { //断开服务端连接
  53. if (acceptor != null) {
  54. acceptor.unbind();
  55. acceptor.dispose();
  56. }
  57. }
  58.  
  59. }
  60.  
  61. public void sendMsg(CommunicationData data) { //发送消息
  62. if (ioSession != null && ioSession.isConnected()) {
  63. ioSession.write(data);
  64. }
  65. }
  66.  
  67. private TransferControlHandler.HandlerListener handlerListener = new TransferControlHandler.HandlerListener() {
  68. @Override
  69. public void sessionCreated(IoSession session) {
  70.  
  71. }
  72.  
  73. @Override
  74. public void sessionOpened(IoSession session) {
  75. ioSession = session; //连接上后获取IoSession,用于发送消息等
  76. if (communicationListener != null) {
  77. communicationListener.onConnected();
  78. }
  79. }
  80.  
  81. @Override
  82. public void sessionClosed(IoSession session) {
  83. if (communicationListener != null) {
  84. communicationListener.onDisConnect();
  85. }
  86. }
  87.  
  88. @Override
  89. public void sessionIdle(IoSession session, IdleStatus status) {
  90. if (session.isBothIdle()) {
  91. session.closeOnFlush(); //读写超时后断开连接
  92. }
  93. }
  94.  
  95. @Override
  96. public void messageReceived(IoSession session, final CommunicationData data) { //接收消息
  97. ioSession = session;
  98. if (communicationListener != null) {
  99. communicationListener.onReceiveMsg(data);
  100. }
  101. }
  102.  
  103. @Override
  104. public void messageSent(IoSession session, CommunicationData data) {
  105. ioSession = session;
  106. }
  107.  
  108. @Override
  109. public void exceptionCaught(IoSession session, final Throwable cause) {
  110. if (communicationListener != null) {
  111. communicationListener.onException(cause);
  112. }
  113. }
  114. };
  115.  
  116. }

 

编解码处理类 TransferControlCodecFactory 是在socket消息接收和发送时最先获取到的,对消息内容进行处理后才转发给Handler类。

 
  1. public class TransferControlCodecFactory implements ProtocolCodecFactory {
  2. private TransferControlEncoder transferEncoder;
  3. private TransferControlDecoder transferDecoder;
  4.  
  5. public TransferControlCodecFactory() {
  6. transferEncoder = new TransferControlEncoder(); //编码类
  7. transferDecoder = new TransferControlDecoder(); //解码类
  8. }
  9.  
  10. @Override
  11. public ProtocolEncoder getEncoder(IoSession ioSession) throws Exception {
  12. return transferEncoder;
  13. }
  14.  
  15. @Override
  16. public ProtocolDecoder getDecoder(IoSession ioSession) throws Exception {
  17. return transferDecoder;
  18. }
  19. }

 

在socket通信时,如果消息内容比较复杂,需要定义通信协议,对收发的消息根据协议进行处理。协议格式可以自己约定,比如约定消息协议如下:

 
  1. public class CommunicationProtocol {
  2. /**
  3. * 消息协议
  4. * Header 头部, 4 bytes {0xff,0xfe,0xff,0xfe}
  5. * Length 总长度, 4 bytes =(DataType length + DataContent length)
  6. * TypeLen 消息类型长度, 4 bytes
  7. * DataType 消息类型
  8. * DataContent 消息内容
  9. */
  10. public static final byte[] COMMUNICATION_HEAD = {-1, -2, -1, -2};//0xff,0xfe,0xff,0xfe 报文头
  11.  
  12. public static final int PORT = 7200; //端口号
  13.  
  14. }

 

那么,对于接收到的消息,需要按照协议格式进行解析

 
  1. public class TransferControlDecoder extends CumulativeProtocolDecoder {
  2. private CommunicationData data;
  3.  
  4. public TransferControlDecoder(){
  5. data = new CommunicationData();
  6. }
  7.  
  8. @Override
  9. protected boolean doDecode(IoSession ioSession, IoBuffer ioBuffer, ProtocolDecoderOutput output) throws Exception {
  10. int start = ioBuffer.position();
  11.  
  12. if (ioBuffer.remaining() < 12) {
  13. return false;
  14. }
  15.  
  16. //判断是否是协议头
  17. for (int i = 0; i < 4; i++) {
  18. byte b = ioBuffer.get();
  19. if (b != CommunicationProtocol.COMMUNICATION_HEAD[i]) {
  20. return false;
  21. }
  22. }
  23.  
  24. int dataLen = ioBuffer.getInt();
  25. int dataTypeLen = ioBuffer.getInt();
  26.  
  27. if (ioBuffer.remaining() < dataLen) {
  28. //如果IoBuffer剩余字节数小于协议内容长度,重新定位到开始位置,并返回false(表示将数据缓存起来,下次和新数据一起合并处理)
  29. ioBuffer.position(start);
  30. return false;
  31. }
  32.  
  33. byte[] dataTypeByte = new byte[dataTypeLen];
  34. ioBuffer.get(dataTypeByte, 0, dataTypeLen); //获取消息类型的内容
  35. data.type = new String(dataTypeByte);
  36.  
  37. int dataContentLen = dataLen - dataTypeLen;
  38. byte[] dataContentByte = new byte[dataContentLen];
  39. ioBuffer.get(dataContentByte, 0, dataContentLen); //获取消息内容
  40. data.content = new String(dataContentByte);
  41.  
  42. output.write(data); //将数据内容写出(类型是Object,因此可以是任意类型数据,此处我们将数据转化为CommunicationData类型,方便处理)
  43.  
  44. return true;
  45. }
  46.  
  47. }

 

相应地,对于要发送出的数据,也需要遵守协议格式,对数据进行处理

 
  1. public class TransferControlEncoder extends ProtocolEncoderAdapter {
  2.  
  3. @Override
  4. public void encode(IoSession ioSession, Object message, ProtocolEncoderOutput output) throws Exception {
  5. if (message instanceof CommunicationData) { //判断是否是CommunicationData类型
  6. IoBuffer ioBuffer = wrapData((CommunicationData) message);
  7. output.write(ioBuffer);
  8. }
  9. }
  10.  
  11. //封装协议内容
  12. private IoBuffer wrapData(CommunicationData data) {
  13. if (data.type == null) {
  14. return null;
  15. }
  16.  
  17. int headLen = CommunicationProtocol.COMMUNICATION_HEAD.length;
  18. int dataTypeLen = data.type.getBytes().length;
  19. int dataContentLen = data.content != null ? data.content.getBytes().length : 0;
  20.  
  21. int dataLen = dataTypeLen + dataContentLen;
  22.  
  23. IoBuffer ioBuffer = IoBuffer.allocate(headLen + dataLen + 8);
  24. ioBuffer.setAutoExpand(true);
  25. ioBuffer.put(CommunicationProtocol.COMMUNICATION_HEAD); //协议头
  26. ioBuffer.putInt(dataLen); //消息总长度
  27. ioBuffer.putInt(dataTypeLen); //消息类型长度
  28. ioBuffer.put(data.type.getBytes()); //消息类型
  29. if (data.content != null) {
  30. ioBuffer.put(data.content.getBytes()); //消息内容
  31. }
  32. ioBuffer.flip();
  33. return ioBuffer;
  34. }
  35.  
  36. }

 

下面实现消息处理类

 
  1. public class TransferControlHandler extends IoHandlerAdapter {
  2. private static final String TAG = "TransferControl";
  3. private HandlerListener handlerListener;
  4.  
  5. public TransferControlHandler() {
  6.  
  7. }
  8.  
  9. public void setHandlerListener(HandlerListener listener) {
  10. this.handlerListener = listener;
  11. }
  12.  
  13. @Override
  14. public void sessionCreated(IoSession session) throws Exception {
  15. Log.d(TAG, "sessionCreated");
  16. if (handlerListener != null) {
  17. handlerListener.sessionCreated(session);
  18. }
  19. }
  20.  
  21. @Override
  22. public void sessionOpened(IoSession session) throws Exception {
  23. Log.d(TAG, "sessionOpened");
  24. if (handlerListener != null) {
  25. handlerListener.sessionOpened(session);
  26. }
  27. }
  28.  
  29. @Override
  30. public void sessionClosed(IoSession session) throws Exception {
  31. Log.d(TAG, "sessionClosed");
  32. if (handlerListener != null) {
  33. handlerListener.sessionClosed(session);
  34. }
  35. }
  36.  
  37. @Override
  38. public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
  39. Log.d(TAG, "sessionIdle status: " + status.toString());
  40. if (handlerListener != null) {
  41. handlerListener.sessionIdle(session, status);
  42. }
  43. }
  44.  
  45. @Override
  46. public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
  47. Log.e(TAG, "exceptionCaught cause: " + cause);
  48. if (handlerListener != null) {
  49. handlerListener.exceptionCaught(session, cause);
  50. }
  51. }
  52.  
  53. @Override
  54. public void messageReceived(IoSession session, Object message) throws Exception {
  55. Log.d(TAG, "messageReceived message: " + message.toString());
  56. if (handlerListener != null) {
  57. CommunicationData data = null;
  58. if (message instanceof CommunicationData) { //经过Decoder后message类型已经是CommunicationData类型了
  59. data = (CommunicationData) message;
  60. }
  61. handlerListener.messageReceived(session, data);
  62. }
  63. }
  64.  
  65. @Override
  66. public void messageSent(IoSession session, Object message) throws Exception {
  67. Log.d(TAG, "messageSent message: " + message.toString());
  68. if (handlerListener != null) {
  69. CommunicationData data = null;
  70. if (message instanceof CommunicationData) {
  71. data = (CommunicationData) message;
  72. }
  73. handlerListener.messageSent(session, data);
  74. }
  75. }
  76.  
  77. public interface HandlerListener {
  78. void sessionCreated(IoSession session);
  79.  
  80. void sessionOpened(IoSession session);
  81.  
  82. void sessionClosed(IoSession session);
  83.  
  84. void sessionIdle(IoSession session, IdleStatus status);
  85.  
  86. void messageReceived(IoSession session, CommunicationData data);
  87.  
  88. void messageSent(IoSession session, CommunicationData data);
  89.  
  90. void exceptionCaught(IoSession session, Throwable cause);
  91. }
  92. }

 

 

客户端实现

 
  1. public class ConnectClient {
  2. private Context mContext;
  3. private ConnectThread connectThread;
  4. private IConnectListener communicationListener;
  5.  
  6. public ConnectClient(Context context) {
  7. this.mContext = context;
  8. connectThread = new ConnectThread();
  9. connectThread.start();
  10. }
  11.  
  12. public void setCommunicationListener(IConnectListener listener) {
  13. this.communicationListener = listener;
  14. }
  15.  
  16. public void sendMsg(CommunicationData data) { //发送消息
  17. connectThread.sendMsg(data);
  18. }
  19.  
  20. public void closeConnectThread() {
  21. if (connectThread != null) {
  22. connectThread.interrupt();
  23. connectThread.disConnect();
  24. }
  25. }
  26.  
  27.  
  28. private class ConnectThread extends Thread {
  29. private NioSocketConnector connector;
  30. private IoSession ioSession;
  31.  
  32. @Override
  33. public void run() {
  34. super.run();
  35. connector = new NioSocketConnector();
  36. TransferControlHandler handler = new TransferControlHandler();
  37. handler.setHandlerListener(handlerListener);
  38. connector.setHandler(handler); //指定消息处理类
  39. //指定消息编解码处理类
  40. connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TransferControlCodecFactory()));
  41. reConnect();
  42. }
  43.  
  44. public void reConnect() {
  45. boolean bool = false;
  46. while (!bool) {
  47. bool = connect();
  48. try {
  49. Thread.sleep(5000); //5秒重连
  50. } catch (InterruptedException e) {
  51. e.printStackTrace();
  52. }
  53. }
  54. }
  55.  
  56. private boolean connect() {
  57. if (connector == null) {
  58. return true;
  59. }
  60. String ip = WifiUtils.getInstance(mContext).getServerIPAddress();
  61. InetSocketAddress address = new InetSocketAddress(ip, CommunicationProtocol.PORT); //指定服务端IP和端口号
  62. try {
  63. ConnectFuture future = connector.connect(address);
  64. future.awaitUninterruptibly();
  65. ioSession = future.getSession(); //连接成功后的IoSession,可用于发送消息等
  66. } catch (Exception e) {
  67. return false;
  68. }
  69. return ioSession != null;
  70. }
  71.  
  72. public void disConnect() {
  73. if (connector != null) {
  74. connector.dispose();
  75. connector = null;
  76. }
  77. }
  78.  
  79. public void sendMsg(CommunicationData data) {
  80. if (ioSession != null && ioSession.isConnected()) {
  81. ioSession.write(data);
  82. }
  83. }
  84. }
  85.  
  86. private TransferControlHandler.HandlerListener handlerListener = new TransferControlHandler.HandlerListener() {
  87. @Override
  88. public void sessionCreated(IoSession session) {
  89.  
  90. }
  91.  
  92. @Override
  93. public void sessionOpened(IoSession session) {
  94. if (communicationListener != null) {
  95. communicationListener.onConnected();
  96. }
  97. }
  98.  
  99. @Override
  100. public void sessionClosed(IoSession session) {
  101. if (communicationListener != null) {
  102. communicationListener.onDisConnect();
  103. }
  104. }
  105.  
  106. @Override
  107. public void sessionIdle(IoSession session, IdleStatus status) {
  108.  
  109. }
  110.  
  111. @Override
  112. public void messageReceived(IoSession session, final CommunicationData data) { //接收消息
  113. if (communicationListener != null) {
  114. communicationListener.onReceiveMsg(data);
  115. }
  116. }
  117.  
  118. @Override
  119. public void messageSent(IoSession session, CommunicationData data) {
  120.  
  121. }
  122.  
  123. @Override
  124. public void exceptionCaught(IoSession session, final Throwable cause) {
  125. if (communicationListener != null) {
  126. communicationListener.onException(cause);
  127. }
  128. }
  129. };
  130. }

 

好了,现在基于Mina框架 基本实现了一个完整的socket传送消息的功能,利用上述实现可以非常方便的发送和接收消息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值