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

概述

本文演示的是一个Android客户端程序,通过UDP协议与两个典型的NIO框架服务端,实现跨平台双向通信的完整Demo。

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

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

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

学习交流

- 更多即时通讯技术资料:http://www.52im.net/forum.php?mod=collection&op=all

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

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

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

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

本篇亮点

  • 客户端基于Android移动端平台:

    直接使用Android的标准UDP代码,不依赖第3方包,且已解决与Java NIO服务端的跨平台通信问题,是个难得的Android端实践入门示例;

  • 完整可执行源码、方便学习:

    完整的Demo源码,适合新手直接运行,便于学习和研究。

  • Demo中的代码源自作者的开源工程,有实用价值:

    源码均修改自作者的即时通讯开源工程 MobileIMSDK,只是为了方便学习理解而作了简化,有一定的实用价值;

本文中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:

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

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

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

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

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

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客户端运行结果:

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

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

本文小结

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地址是:http://docs.52im.net/extend/docs/src/mina2/

MINA-1.x地址是:http://docs.52im.net/extend/docs/src/mina1/

Netty-4.x地址是:http://docs.52im.net/extend/docs/src/netty4/

Netty-3.x地址是:http://docs.52im.net/extend/docs/src/netty3/

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

MINA-2.x API文档(在线版):http://docs.52im.net/extend/docs/api/mina2/

MINA-1.x API文档(在线版):http://docs.52im.net/extend/docs/api/mina1/

Netty-4.x API文档(在线版):http://docs.52im.net/extend/docs/api/netty4/

Netty-3.x API文档(在线版):http://docs.52im.net/extend/docs/api/netty3/

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

请进入精华资料专辑:http://www.52im.net/forum.php?mod=collection&action=view&ctid=9

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

请进入精华资料专辑:http://www.52im.net/forum.php?mod=collection&op=all

[5] 技术交流和学习:

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

完整源码工程下载

如需完整Eclipse源码工程请联系作者,或者进入链接 http://www.52im.net/thread-388-1-1.html 自行下载。

完整源码工程截图如下:

   

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

(本文同步发布于:http://www.52im.net/thread-388-1-1.html)

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

出处:http://www.52im.net/space-uid-1.html

联系方式:QQ: 413980957, 微信: hellojackjiang,Email: [email protected]

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

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

时间: 2024-10-24 10:28:39

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

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

前言 本文将演示一个iOS客户端程序,通过UDP协议与两个典型的NIO框架服务端,实现跨平台双向通信的完整Demo.服务端将分别用MINA2和Netty4进行实现,而通信时服务端你只需选其一就行了.同时用MINA2和Netty4分别实现服务端的目的,是因为很多人都在纠结到底是用MINA还是Netty来实现高并发的Java网络通信服务端,在此干脆两个都实现了,就看你怎么选择了,够吊吧. NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能.这其中最流行的无非就是MINA和Netty了,M

【原创】NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示

申明:本文由作者基于日常实践整理,希望对初次接触MINA.Netty的人有所启发.如需与作者交流,见文签名,互相学习. 学习交流 更多学习资料:点此进入 推荐 移动端即时通讯交流: 215891622 推荐 前言 NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能.这其中最流行的无非就是MINA和Netty了,MINA目前的主要版本是MINA2.而Netty的主要版本是Netty3和Netty4(Netty5已经被取消开发了:详见此文). 本文将演示的是一个基于Netty4的UDP服

【原创】新手入门一篇就够:从零开发移动端IM

一.前言 IM发展至今,已是非常重要的互联网应用形态之一,尤其移动互联网时代,它正以无与论比的优势降低了沟通成本和沟通代价,对各种应用形态产生了深远影响. 做为IM开发者或即将成为IM开发者的技术人员,IM的价值和重要性不言自明.但从技术实现来说,IM系统的开发(尤其是移动端IM)还是存在许多技术难点和坑点的.也正因如此,优质的IM开发相关的资料.实践性成果,对于没有太多技术储备的新手来说,尤其难以获得. 本文将以新手的视角引导你阅读相关文章,以便为从零开发一个移动端IM做好方方面面的知识准备:

RxJava入门系列四,Android中的响应式编程

RxJava入门系列四,Android中的响应式编程 在入门系列1,2,3中,我基本介绍了RxJava是如何使用的.但是作为一名Android开发人员,你怎么让RxJava能为你所用呢?这篇博客我将针对Android开发来介绍一下RxJava的使用场景. RxAndroid RxAndroid是为Android打造的RxJava扩展.通过RxAndroid可以让你的Android开发变得更轻松. 首先,RxAndroid中提供了AndroidSchedulers,你可以用它来切换Android线

【Netty】NIO框架Netty入门

Netty介绍 Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序. 也就是说,Netty 是一个基于NIO的客户.服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用.Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发. 官网地址:http://netty.io/ 使用场景 Nett

Thinkphp入门 四 —布局、缓存、系统变量 (48)

原文:Thinkphp入门 四 -布局.缓存.系统变量 (48) [控制器操作方法参数设置] http://网址/index.php/控制器/操作方法 [页面跳转] [变量调节器] Smarty变量调节器 TP变量调节器:普通的php函数 (count  strlen   str_replace) 定义:前者的输出,是后者的输入 [子模板包含] 当前模块彼此包含 <include  file=”模板名称”  /> [使用布局layout] 1. 开启布局,配置变量信息config.php 2.

Dwz(J-UI)框架--入门

http://www.cnblogs.com/chenyongsai/p/4933982.html Dwz(J-UI)框架--入门 一.了解 概述:是中国人自己开发的基于jQuery实现的Ajax RIA开源框架. 目的:简单实用.扩展方便(在原有架构基础上扩展方便).快速开发.RIA思路.轻量级 使用:用html扩展的方式来代替javascript代码 思路:根据官网页面样例,查看官方代码包,查阅相关子页面,参阅帮助文档,添加固定的标签属性语法 优势:第一次打开页面时载入界面到客户端, 之后和

Java异步NIO框架Netty实现高性能高并发

1. 背景 1.1. 惊人的性能数据 近期一个圈内朋友通过私信告诉我,通过使用Netty4 + Thrift压缩二进制编解码技术,他们实现了10W TPS(1K的复杂POJO对象)的跨节点远程服务调用.相比于传统基于Java序列化+BIO(同步堵塞IO)的通信框架.性能提升了8倍多. 其实,我对这个数据并不感到吃惊,依据我5年多的NIO编程经验.通过选择合适的NIO框架,加上高性能的压缩二进制编解码技术,精心的设计Reactor线程模型,达到上述性能指标是全然有可能的. 以下我们就一起来看下Ne

使用Mina框架开发 QQ Android 客户端

Apache MINA是一个网络应用程序框架,用来帮助用户简单地开发高性能和高可靠性的网络应用程序.它提供了一个通过Java NIO在不同的传输例如TCP/IP和UDP/IP上抽象的事件驱动的异步API. Apache MINA 也称为: ● NIO 框架库 ● 客户端服务器框架库 ● 一个网络套接字库 MINA虽然简单但是仍然提供了全功能的网络应用程序框架: ● 为不同的传输类型提供了统一的API: ○ 通过Java NIO提供TCP/IP 和 UDP/IP支持 ○ 通过RXTX提供串口通讯(