【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战
概述
本文演示的是一个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框架入门》系列文章中的第 4 篇,目录如下:
- 《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;
@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:
/*
* 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:
/*
* 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)
【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战的更多相关文章
- 【原创】NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战
前言 本文将演示一个iOS客户端程序,通过UDP协议与两个典型的NIO框架服务端,实现跨平台双向通信的完整Demo.服务端将分别用MINA2和Netty4进行实现,而通信时服务端你只需选其一就行了.同 ...
- 【原创】NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示
前言 NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能.这其中最流行的无非就是MINA和Netty了,MINA目前的主要版本是MINA2.而Netty的主要版本是Netty3和Netty ...
- 【原创】NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示
申明:本文由作者基于日常实践整理,希望对初次接触MINA.Netty的人有所启发.如需与作者交流,见文签名,互相学习. 学习交流 更多学习资料:点此进入 推荐 移动端即时通讯交流: 215891622 ...
- 使用Spring框架入门四:基于注解的方式的AOP的使用
一.简述 前面讲了基于XML配置的方式实现AOP,本文简单讲讲基于注解的方式实现. 基于注解的方式实现前,要先在xml配置中通过配置aop:aspectj-autoproxy来启用注解方式注入. &l ...
- 【原创】新手入门一篇就够:从零开发移动端IM
一.前言 IM发展至今,已是非常重要的互联网应用形态之一,尤其移动互联网时代,它正以无与论比的优势降低了沟通成本和沟通代价,对各种应用形态产生了深远影响. 做为IM开发者或即将成为IM开发者的技术人员 ...
- 高性能NIO框架Netty入门篇
http://cxytiandi.com/blog/detail/17345 Netty介绍 Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具 ...
- 【Netty】NIO框架Netty入门
Netty介绍 Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序. 也就是说,Netty ...
- 轻松应对并发,Newbe.Claptrap 框架入门,第四步 —— 利用 Minion,商品下单
接上一篇 Newbe.Claptrap 框架入门,第三步 —— 定义 Claptrap,管理商品库存 ,我们继续要了解一下如何使用 Newbe.Claptrap 框架开发业务.通过本篇阅读,您便可以开 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)
前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...
随机推荐
- openssl证书制作详细教程
自签名证书及验证 模拟证书涉及的角色 创建证书目录 mkdir ~/certs cd ~/certs 认证机构.网站.浏览器/用户 mkdir root web user 机构自签名证书生成和发布 生 ...
- MySQL的limit查询优化
MySQL的limit查询优化以下的文章主要是对MySQL limit查询优化的具体内容的介绍,我们大家都知道MySQL数据库的优化是相当重要的.其他最为常用也是最为需要优化的就是limit.MySQ ...
- javascript练习-方法借用
方法借用其实也可以叫做多重继承 var generic = { //返回一个字符串,这个字符串包含构造函数的名字(如果构造函数包含名字) //这个以及所有非继承来的,非函数属性的名字和值 toStri ...
- oracle入门必备
//................创建表空间 \ 赋予角色 \ 创建数据表 \ 插入数据 \ 创建序列 \ 添加注释 ........................... --创 ...
- 自定义struts实现
一:struts2运行机制: Tomcat一启动,一些信息就已经加载完成,例如StrutsPrepareAndExecuteFilter加载的那些strut.xml以及Action的class类文件 ...
- php composer使用经验
1.使用composer引用了一个包,但是这个包没有使用命名空间,在项目中该如何使用这个包? 编辑composer.json文件 "autoload":{ "files& ...
- EventBus的简单使用与原理
一.概述 EventBus是一款针对Android优化的发布/订阅事件总线.主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间 ...
- UICollectionView(集合视图)以及自定义集合视图
一.UICollectionView集合视图 其继承自UIScrollView. UICollectionView类是iOS6新引进的API,用于展示集合视图,布局 ...
- 解压缩c#
protected void btn_ServerClick(object sender, EventArgs e) { string strtxtPath = "E:/ ...
- 如何编写 Cloud9 JavaScript IDE 的功能扩展
上周末我们在JSConf.eu发布了 Cloud9 IDE ,同时发布了对应的GitHub项目.在4天时间里该项目得到340个人的关注和将近50个fork.Cloud9的口号是由"由Java ...