• 新闻
  • 中国
  • 纪录片
  • 娱乐
  • 综艺
  • 影视
  • 主持人
  • 法治
  • 历史
  • 首页
  • 新闻
  • 中国
  • 纪录片
  • 娱乐
  • 综艺
  • 影视
  • 主持人
  • 法治
  • 历史
    首页 > 最新消息

    【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战 - jb2011000

    2016-07-04 浏览:

    当前由于NIO框架的流行,使得开发大并发、高性能的互联网服务端成为可能。这其中最流行的无非就是MINA和Netty了,MINA目前的主要版本是MINA2、而Netty的主要版本是Netty3和Netty4(Netty5已经被取消开发了:详见此文)。

    本文中,服务端将分别用MINA2和Netty4进行实现,但在你实际的项目中服务端实现只需选其一就行了。本文中的Demo同时用MINA2和Netty4分别实现服务端的目的,是因为很多人都在纠结到底是用MINA还是Netty来实现高并发的Java网络通信服务端,在此干脆两个都实现了,就看你怎么选择。

    实际上,MINA2和Netty4的官方代码里有UDP通信的Demo代码,但却不存在针对移动端(主要是Android和iOS端)的Demo,本文将演示用Android客户端来实现这种跨平台的双向网络通信。Demo中,已经解决跨平台通信时的常见的乱码、数据字节异常等问题,如觉得有用,你可直接使用之。

    学习交流

    - 更多即时通讯技术资料:?mod=collection&op=all

    - 移动端即时通讯交流群:215891622 推荐

    《NIO框架入门》系列文章目录

    有关MINA和Netty的入门文章很多,但多数都是复制、粘贴的未经证实的来路不明内容,对于初次接触的人来说,一个可以运行且编码规范的Demo,显然要比各种“详解”、“深入分析”之类的要来的直接和有意义。本系列入门文章正是基于此种考虑而写,虽无精深内容,但至少希望对初次接触MINA、Netty的人有所启发,起到抛砖引玉的作用。

    本文是《NIO框架入门》系列文章中的第 4 篇,目录如下:

    本篇亮点 本文中Demo演示的功能

    本文中的Demo代码实现包含两部分,Android UDP客户端和NIO框架实现的服务端(包括MINA2和Netty4实现两个方案),客户端每隔5秒向服务端发送消息,而服务端在收到消息后马上回复一条消息给客户端。

    如上所述,服务端(PC服务器)和客户端(Android移动端)都要实现消息的发送和接收,即实现跨平台的双向通信。下节将将给出真正的实现代码。

    Android客户端准备工作 [Step 1]:准备好开发环境

    这两年,Google官方已经基本放弃Eclipse+ADT这样的IDE组合,转而大力开发Android Studio,但不得不承认,由于我的OS仍然是XP(Android Studio不支持XP),所以Eclipse+ADT还得继续用(这个组合虽然一直被吐槽,但又不得不用)。

    如果你习惯使用Eclipse+ADT这样的IDE,可以下载我打好包的版本,内含Eclipse4.2+ADT+Android SDK:

    【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战 - jb2011000

    如果你需要Android Studio,可进入此链接下载。

    [Step 2]:新建一个普通的Android工程,准备开撸

    本文以Eclipse+ADT为开发Android开发工具(如你使用Android Studio道理也是一样的),按照提示新建工程即可,无需特殊的设置或其它前前置条件。

    我建好的工程,如下图所示(很多都是默认生成的,用不上的东西就别去管它了):

    【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战 - jb2011000

    补充说明:因为需要进行网络通信,建好的工程里,请务必在 AndroidManifest.xml 加上网络权限的许可,如下图:

    【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战 - jb2011000

    Android客户端代码实现 [1] 客户端主类 MainActivity.java: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 /*  * Copyright (C) 2016 即时通讯网(52im.net) - 即时通讯开发者社区.  * All rights reserved.  */ package net.x52im.example.android.udp;    import net.x52im.example.android.udp.utils.UDPUtils; import android.os.Bundle; import android.os.Handler; import android.support.v7.app.ActionBarActivity; import android.util.Log;    /**  * Demo主类。  *  * @author jack.jiang@52im.net, 2016-06-27  * @version 1.0  */ public class MainActivity extends ActionBarActivity {         private final static String TAG = MainActivity.class.getSimpleName();         // 重复发送的时间间隔(单位:毫秒)         public static int INTERVAL = 5000;         private Handler handler = null;         private Runnable runnable = null;            @Override         protected void onCreate(Bundle savedInstanceState)         {                 super.onCreate(savedInstanceState);                 setContentView(R.layout.activity_main);                    // 初始化本地UDP的Socket                 LocalUDPSocketProvider.getInstance().initSocket();                 // 启动本地UDP监听(接收数据用的)                 LocalUDPDataReciever.getInstance(this).startup();                    // 自动循环发送                 handler = new Handler();                 runnable = new Runnable(){                         @Override                         public void run()                         {                                 sendMessageToServer();                                                                    // 开始下一次循环                                 handler.postDelayed(runnable, INTERVAL);                         }                 };                                    // 立即开始发送                 handler.postDelayed(runnable, 0);         }            private void sendMessageToServer()         {                 try                 {                         // 要发送的数据                         String toServer = "Hi,我是客户端,我的时间戳"+System.currentTimeMillis();                         byte[] soServerBytes = toServer.getBytes("UTF-8");                         // 开始发送                         boolean ok = UDPUtils.send(soServerBytes, soServerBytes.length);                         if(ok)                                 Log.d(TAG, "发往服务端的信息已送出.");                         else                                 Log.e(TAG, "发往服务端的信息没有成功发出!!!");                 }                 catch (Exception e)                 {                         Log.w(TAG, e.getMessage(), e);                 }         } }

    补充说明:本类没有去写UI代码,只是作为本次Demo的主入口类而已,需要查看数据输出的,请在Eclipse下的DDMS控制台看查看log输出哦。

    [2] 客户端本地 UDP Socket 管理类 LocalUDPSocketProvider.java: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 /*  * Copyright (C) 2016 即时通讯网(52im.net) - 即时通讯开发者社区.  * All rights reserved.  */ package net.x52im.example.android.udp;    import net.x52im.example.android.udp.utils.UDPUtils; import android.os.Bundle; import android.os.Handler; import android.support.v7.app.ActionBarActivity; import android.util.Log;    /**  * Demo主类。  *  * @author jack.jiang@52im.net, 2016-06-27  * @version 1.0  */ public class MainActivity extends ActionBarActivity {         private final static String TAG = MainActivity.class.getSimpleName();         // 重复发送的时间间隔(单位:毫秒)         public static int INTERVAL = 5000;         private Handler handler = null;         private Runnable runnable = null;            @Override         protected void onCreate(Bundle savedInstanceState)         {                 super.onCreate(savedInstanceState);                 setContentView(R.layout.activity_main);                    // 初始化本地UDP的Socket                 LocalUDPSocketProvider.getInstance().initSocket();                 // 启动本地UDP监听(接收数据用的)                 LocalUDPDataReciever.getInstance(this).startup();                    // 自动循环发送                 handler = new Handler();                 runnable = new Runnable(){                         @Override                         public void run()                         {                                 sendMessageToServer();                                                                    // 开始下一次循环                                 handler.postDelayed(runnable, INTERVAL);                         }                 };                                    // 立即开始发送                 handler.postDelayed(runnable, 0);         }            private void sendMessageToServer()         {                 try                 {                         // 要发送的数据                         String toServer = "Hi,我是客户端,我的时间戳"+System.currentTimeMillis();                         byte[] soServerBytes = toServer.getBytes("UTF-8");                         // 开始发送                         boolean ok = UDPUtils.send(soServerBytes, soServerBytes.length);                         if(ok)                                 Log.d(TAG, "发往服务端的信息已送出.");                         else                                 Log.e(TAG, "发往服务端的信息没有成功发出!!!");                 }                 catch (Exception e)                 {                         Log.w(TAG, e.getMessage(), e);                 }         } }

    补充说明:以上代码使用的是Android的标准UDP Socket代码,如果你对此不太熟悉请先查阅更多Android UDP通讯的相关实例。

    [3] 客户端本地UDP端口监听和数据接收类 LocalUDPDataSender.java: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 /*  * Copyright (C) 2016 即时通讯网(52im.net) - 即时通讯开发者社区.  * All rights reserved.  */ package net.x52im.example.android.udp;    import java.net.DatagramPacket; import java.net.DatagramSocket;    import net.x52im.example.android.udp.utils.ConfigEntity; import android.content.Context; import android.util.Log;    /**  * 本地UDP端口监听和数据接收类。  *  * @author jack.jiang@52im.net, 2016-06-27  * @version 1.0  */ public class LocalUDPDataReciever {         private static final String TAG = LocalUDPDataReciever.class.getSimpleName();         private static LocalUDPDataReciever instance = null;         private Thread thread = null;         private Context context = null;                    public static LocalUDPDataReciever getInstance(Context context)         {                 if (instance == null)                         instance = new LocalUDPDataReciever(context);                 return instance;         }            private LocalUDPDataReciever(Context context)         {                 this.context = context;         }            public void startup()         {                 this.thread = new Thread(new Runnable()                 {                         public void run()                         {                                 try                                 {                                         Log.d(LocalUDPDataReciever.TAG, "本地UDP端口侦听中,端口=" + ConfigEntity.localUDPPort + "...");                                            //开始侦听                                         LocalUDPDataReciever.this.udpListeningImpl();                                 }                                 catch (Exception eee)                                 {                                         Log.w(LocalUDPDataReciever.TAG, "本地UDP监听停止了(socket被关闭了?)," + eee.getMessage(), eee);                                 }                         }                 });                 this.thread.start();         }            private void udpListeningImpl() throws Exception         {                 while (true)                 {                         byte[] data = new byte[1024];                         // 接收数据报的包                         DatagramPacket packet = new DatagramPacket(data, data.length);                            DatagramSocket localUDPSocket = LocalUDPSocketProvider.getInstance().getLocalUDPSocket();                         if ((localUDPSocket == null) || (localUDPSocket.isClosed()))                                 continue;                                                    // 阻塞直到收到数据                         localUDPSocket.receive(packet);                                                    // 解析服务端发过来的数据                         String pFromServer = new String(packet.getData(), 0 , packet.getLength(), "UTF-8");                         Log.w(LocalUDPDataReciever.TAG, "【NOTE】>>>>>> 收到服务端的消息:"+pFromServer);                 }         } } 服务端准备工作

    本文将分别基于MINA2和Netty4实现两套服务端(你只需要使用其中之一即可),服务端准备工作已在本系列文章的前两篇详细记录了,具体如下:

    - Netty4实现服务端的准备工作请见:《NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示》

    - MINA2实现服务端的准备工作请见:《NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示》

    服务端代码实现

    因两套方案的服务端代码都不复杂,且已经本系列文章的前两篇中详细介绍,本文就不在重复粘贴了。

    - Netty4实现的服务端请见:《NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示》

    - MINA2实现的服务端请见:《NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示》

    Demo 运行截图

    [1] Android客户端运行结果:

    【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战 - jb2011000

    [2] 服务端运行结果(MINA2方案):

    【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战 - jb2011000

    [3] 服务端运行结果(Netty4方案):

    【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战 - jb2011000

    本文小结

    Demo中的客户端代码是从开源即时通讯框架MobileIMSDK 的Android端中复制出来的(为了方便理解做了大幅简化),有兴趣的可看看 MobileIMSDK Android端、Server端,简化一下可以用作你自已的各种用途。

    本文的姊妹篇《NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战》,演示的是iOS端的跨平台UDP双向通信,需要的话可以看看。

    对于服务端的NIO框架来说,如果你阅读过本系列的《NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示》和《NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示》,应该能明显地感觉的出来MINA2的UDP服务端API接口使用要是Netty4的繁琐,而且MINA2还存在独立客户端(非依赖于MINA2客户端)实现时的多余字节和乱码问题。但个人认为MINA2的代码风格更符合一般程序员的编码习惯,更好懂一些,而Netty4因历经多个大版本的进化,虽起来非常简洁,但实现并不是那么直观。当然,至于MINA还是Netty,请客观一评估和使用,因为二者并无本质区别。

    更多NIO框架资料整理

    [1] MINA和Netty的源码在线学习和查阅:

    MINA-2.x地址是:

    MINA-1.x地址是:

    Netty-4.x地址是:

    Netty-3.x地址是:

    [2] MINA和Netty的API文档在线查阅:

    MINA-2.x API文档(在线版):

    MINA-1.x API文档(在线版):

    Netty-4.x API文档(在线版):

    Netty-3.x API文档(在线版):

    [3] 更多有关NIO编程的资料:

    请进入精华资料专辑:?mod=collection&action=view&ctid=9

    [4] 有关IM聊天应用、消息推送技术的资料:

    请进入精华资料专辑:?mod=collection&op=all

    [5] 技术交流和学习:

    可直接进入 即时通讯开发者社区 讨论和学习网络编程、IM聊天应用、消息推送应用的开发。

    完整源码工程下载

    如需完整Eclipse源码工程请联系作者,或者进入链接  自行下载。

    完整源码工程截图如下:

    【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战 - jb2011000

       

    【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战 - jb2011000

    截图说明:左右是Android客户端源码、右边是服务端(MINA2和Netty4两个方案)。

    (本文同步发布于:)

    作者:Jack Jiang (点击作者姓名进入Github) 

    出处: 

    联系方式:QQ: 413980957, 微信: hellojackjiang,Email: jb2011@163.com 

    Jack Jiang同时是【原创Java Swing外观工程BeautyEye】和【轻量级移动端即时通讯框架MobileIMSDK】的作者,可前往下载交流。

    本博文 欢迎转载,转载请注明出处(也可前往 我的52im.net 找到我)。 



    上一篇:原创 说走就走 大众蔚揽旅行车的“洗肺”之旅

    下一篇:突袭新闻


    相关阅读

    • 一个新品牌,从无到有做起来2025-05-13
    • 震雄集团荣登《研发与标准化同步企业》2024-12-26
    • 2024中小企业跨境出海国际合作论坛在广州举行2024-08-31
    • “就不叫你提问”,白宫发言人怼美媒记者,被呛“应感到羞耻”2023-10-13
    • 阿斯巴甜可能致癌!世卫发布评估结果2023-07-14
    • 巴西总统宣布为英国女王哀悼3天,被指别有用心2022-09-09
    • 特朗普发文抨击:“为什么疯狂的佩洛西在台湾,总是制造麻烦”!2022-08-03
    • 何时能使老百姓在每个司法案件中得到公平与正义?2022-06-25
    • 英媒宣布普京“身患绝症或已经死亡”,拉夫罗夫的回应来了2022-06-01
    • 致宁夏回族自治区公安厅杨东厅长的 一封举报信2021-03-24

  • 一个新品牌,从无到有做起来
  • 震雄集团荣登《研发与标准化同步企业》
  • 2024中小企业跨境出海国际合作论坛在广州举行
  • “就不叫你提问”,白宫发言人怼美媒记者,被呛“应感到羞耻”
  • 阿斯巴甜可能致癌!世卫发布评估结果
  • 巴西总统宣布为英国女王哀悼3天,被指别有用心
  • 特朗普发文抨击:“为什么疯狂的佩洛西在台湾,总是制造麻烦”!
  • 何时能使老百姓在每个司法案件中得到公平与正义?
  • 英媒宣布普京“身患绝症或已经死亡”,拉夫罗夫的回应来了
  • 致宁夏回族自治区公安厅杨东厅长的 一封举报信


  • 2019大白鲸原创幻想儿童文学年度盛典在大连举行
  • 湖南发布20支原创广场舞 体现社会主义核心价值观
  • 乖离性百万亚瑟王国服原创感谢型蛋池分析
  • 意大利原创女装品牌ANOTHER ONE盛装来袭
  • 王炸!43分22板姚明后第一人 还率队22分逆转
  • 关于节目方面论文范文素材,与卫视节目:由模仿走向原创相关论文抄袭多少算抄袭
  • 发展电竞产业,提升原创内容是关键
  • 王珉的天真 苏树林的眼泪 武长顺的匪气
  • 向世界讲述多彩贵州 原创舞剧《蝴蝶妈妈》排演进入冲刺阶段
  • 2016动漫汇四川动漫原创周在蓉开幕
人民播报是大型资讯网站。