为什么80%的码农都做不了架构师?>>>   

概述

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

当前由于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中,已经解决跨平台通信时的常见的乱码、数据字节异常等问题,如觉得有用,你可直接使用之。

学习交流

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

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

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

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

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

  • 《NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示》
  • 《NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示》
  • 《NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战》
  • 《NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战》(本文)

本篇亮点

  • 客户端基于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:

/** 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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化本地UDP的SocketLocalUDPSocketProvider.getInstance().initSocket();// 启动本地UDP监听(接收数据用的)LocalUDPDataReciever.getInstance(this).startup();// 自动循环发送handler = new Handler();runnable = new Runnable(){@Overridepublic 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, "发往服务端的信息已送出.");elseLog.e(TAG, "发往服务端的信息没有成功发出!!!");}catch (Exception e){Log.w(TAG, e.getMessage(), e);}}
}

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

[2] 客户端本地 UDP Socket 管理类 LocalUDPSocketProvider.java:

/** Copyright (C) 2016 即时通讯网(52im.net) - 即时通讯开发者社区.* All rights reserved.*/
package net.x52im.example.android.udp;import java.net.DatagramSocket;
import java.net.InetAddress;import net.x52im.example.android.udp.utils.ConfigEntity;
import android.util.Log;/*** 本地 UDP Socket 管理类。* * @author jack.jiang@52im.net, 2016-06-27* @version 1.0*/
public class LocalUDPSocketProvider
{private static final String TAG = LocalUDPSocketProvider.class.getSimpleName();private static LocalUDPSocketProvider instance = null;private DatagramSocket localUDPSocket = null;public static LocalUDPSocketProvider getInstance(){if (instance == null)instance = new LocalUDPSocketProvider();return instance;}public void initSocket(){try{// UDP本地监听端口(如果为0将表示由系统分配,否则使用指定端口)this.localUDPSocket = new DatagramSocket(ConfigEntity.localUDPPort);// 调用connect之后,每次send时DatagramPacket就不需要设计目标主机的ip和port了// * 注意:connect方法一定要在DatagramSocket.receive()方法之前调用,// * 不然整send数据将会被错误地阻塞。这或许是官方API的bug,也或许是调// * 用规范就应该这样,但没有找到官方明确的说明this.localUDPSocket.connect(InetAddress.getByName(ConfigEntity.serverIP), ConfigEntity.serverUDPPort);this.localUDPSocket.setReuseAddress(true);Log.d(TAG, "new DatagramSocket()已成功完成.");}catch (Exception e){Log.w(TAG, "localUDPSocket创建时出错,原因是:" + e.getMessage(), e);}}public DatagramSocket getLocalUDPSocket(){return this.localUDPSocket;}
}

[3] 客户端本地UDP端口监听和数据接收类 LocalUDPDataSender.java:

/** 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);}}
}

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

服务端准备工作

本文将分别基于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)

转载于:https://my.oschina.net/jb2011/blog/703511

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

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

    2019独角兽企业重金招聘Python工程师标准>>> 前言 NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能.这其中最流行的无非就是MINA和Netty了,MINA目 ...

  2. java netty 教程,Java NIO框架Netty教程(十七) - Netty4 Hello world

    最近很多人问我有没有Netty4的Hello World样例,很早之前知道Netty要出4,当时只知道4的包名完全边了,因为Netty从JBoss中独立出来了,并采用了新的netty.io的域名,但是 ...

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

    一.前言 IM发展至今,已是非常重要的互联网应用形态之一,尤其移动互联网时代,它正以无与论比的优势降低了沟通成本和沟通代价,对各种应用形态产生了深远影响. 做为IM开发者或即将成为IM开发者的技术人员 ...

  4. 高性能NIO框架Netty入门篇

    http://cxytiandi.com/blog/detail/17345 Netty介绍 Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具 ...

  5. Android Glide图片加载框架(四)回调与监听

    文章目录 Android Glide图片加载框架系列文章 Android Glide图片加载框架(一)基本用法 Android Glide图片加载框架(二)源码解析之with() Android Gl ...

  6. JAVA NIO编程入门(二)

    一.回顾 上一篇文章 JAVA NIO编程入门(一)我们学习了NIO编程的基础知识,并通过一个小demo实战帮助了解NIO编程的channel,buffer等概念.本文会继续学习JAVA NIO编程, ...

  7. (转载)MFC入门(四)  作者 zhoujiamurong

    关键字 MFC 原作者姓名 zhoujiamurong 介绍 这一节,介绍工具条和状态栏 读者评分 71 评分次数 18 正文 MFC入门(四) 工具条和状态栏 原创  作者:zhoujiamuron ...

  8. Java NIO框架Mina、Netty、Grizzly介绍与对比

    Java NIO框架Mina.Netty.Grizzly介绍与对比 原文地址:https://blog.csdn.net/e765741668/article/details/45234711 Min ...

  9. netty框架android,隻需五步,即可基於Netty框架實現Android內網推送功能。

    隻需五步,即可基於Netty框架實現Android內網推送功能. 一.先引入依賴,客戶端和服務端用的都是同一個依賴netty-all. Android Studio中Gradle配置: compile ...

  10. 实验四 Android程序设计

    实验四 Android程序设计 课程:Java程序设计 班级:1652 姓名:孔月 学号:20165208 指导教师:娄嘉鹏 实验日期:2018.5.14 实验名称:Android程序设计 实验要求: ...

最新文章

  1. 做个md5查询站(2)初步设计
  2. WINCE6.0 error C2220: warning treated as error问题解决
  3. php execl 列的长度,php生成excel列名超过26列大于Z时的解决方法
  4. gin 项目结构_Gin框架 - 项目目录
  5. linux vim配置怎么打开文件,Linux如何设置默认VIM打开文件
  6. 满足条件的两个数或多个数
  7. search-guard 在 Elasticsearch 2.3 上的运用
  8. input发送a.jax_Java REST JAX-RS 2.0 –如何处理日期,时间和时间戳记数据类型
  9. mysql cluster 查看数据库表名称_MySQL Cluster如何创建磁盘表方法解读
  10. 英特尔表示:元宇宙的路还很长
  11. (计算机组成原理)第一章计算机系统概述-王道重点习题及杂项总结
  12. 最近纠结致死的一个java报错java.net.SocketException: Connection reset 终于得到解决
  13. MongoDB独特查询
  14. router阻止telnet自身的两种方法
  15. 用php打竖的文字_总结PHP竖排文字的方法
  16. word中如何将所有同一级标题统一格式
  17. 关于Javascript的学习心得
  18. Android 中在Android studio2.3中 NASA 的World Wind地图应用
  19. 独热编码 (One-Hot Encoding) 介绍及MATLAB命令
  20. 非常全面的NFS文档(FOR LINUX)

热门文章

  1. Ubuntu 16.04安装Docker
  2. linux SCP远程拷贝文件方法及not a regular file 错误解决方法
  3. 动脑学院_张晨到梅州职业技术学院施工现场调研:强力加快进度,确保项目如期建成...
  4. cenOS 安装opencv(for matlab)
  5. 【2019 NWERC - D 】Disposable Switches【最短路、单调栈、数学思维】
  6. 【CF Contest-1228 E】Another Filling the Grid【容斥】
  7. POJ_3984迷宫问题(bfs基础题)
  8. Raki的读paper小记:A Unified MRC Framework for Named Entity Recognition
  9. 226.翻转二叉树 (力扣leetcode) 博主可答疑该问题
  10. JDK ThreadLocal解析