原创nio框架入门:android与mina2netty4的跨平台udp双向通信实战

author author     2022-07-29     556

关键词:

概述

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

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

本文中,服务端将分别用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:

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

android与html+js交互入门

...ttp://blog.csdn.net/leejizhou/article/details/50894531李济洲的博客在Android开发中,越来越多的商业项目使用了Android原生控件与WebView进行混合开发,当然不仅仅就是显示一个WebView那么简单,有时候还需要本地Java代码与HTML中的javascript进行... 查看详情

dubbo入门基础与实例讲解(转)

 林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka      Dubbo是阿里巴巴SOA服务化治理方案的核心框架,每天为2,000+个服务提供3,000,000,000+次访问量支持,并被广泛应用于阿里巴巴集团的各成... 查看详情

nio入门

输入/输出:概念性描述I/O简介I/O?或者输入/输出?指的是计算机与外部世界或者一个程序与计算机的其余部分的之间的接口。它对于任何计算机系统都非常关键,因而所有I/O的主体实际上是内置在操作系统中的。单独的程序一般... 查看详情

nio入门-----01

...ava.nio.ByteBuffer;importorg.junit.Test;/** *@authorSico *1、NIO入门:三个核心概念如下: *  |--通道:连接作用,连接数据源和目标地 *  |--缓冲区:用于存取数据 *  |--选择器: *   *2、NI... 查看详情

nio框架之mina源码解析:nio超级陷阱和使用同步io与mina通信

1、NIO超级陷阱   之所以说NIO超级陷阱,就是因为我在本系列开头的那句话,因为使用缺陷导致客户业务系统瘫痪。当然,我对这个问题进行了很深的追踪,包括对MINA源码的深入了解,但其实之所以会出现这个问题,... 查看详情

cocos2d-x与着色器设计--入门篇(游云凌天原创)

...多的被移动端开发者所接受。本人在AppStore上发布了一个原创免费开源无广告的关于着色器的教育型软件,以 查看详情

android测试工具uiautomator入门与介绍(代码片段)

UIAutomator测试工具定义以及用途UIAutomator测试框架提供了一组API,用于构建在用户应用和系统应用上执行交互的界面测试。通过UIAutomatorAPI,您可以执行在测试设备中打开“设置”菜单或应用启动器等操作。UIAutomator测试框架非常... 查看详情

javanio网络编程服务篇入门

概念就不说了。要很多nio的开源框架,我用过mina,在两个项目中用过,主要处理数据的编解码。这块做好了,网络部分也就做完多半了。现在再写网络编程,也不再使用框架了,直接使用javanio的api编写,快速、省事、灵活、自... 查看详情

java网络编程和nio详解9:基于nio的网络编程框架netty(代码片段)

Java网络编程和NIO详解9:基于NIO的网络编程框架Netty转自https://sylvanassun.github.io/2017/11/30/2017-11-30-netty_introduction/netty是基于NIO实现的异步事件驱动的网络编程框架,学完NIO以后,应该看看netty的实现,netty框架涉及的内容特别多,这... 查看详情

java入门系列-23-nio(使用缓冲区和通道对文件操作)(代码片段)

NIO是什么java.nio全称javanon-blocking(非阻塞)IO(实际上是newio),是指jdk1.4及以上版本里提供的新api(NewIO),为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络。NIO与IO的区... 查看详情

nio入门

1、I/O输入输出,所有的IO都被视作是单个字节的移动,通过stream对象一次移动一个字节。流IO负责把对象转换为字节,然后再转换为对象。NIO提供了二套NIO,一套是针对标准输入输出NIO,另一套是网络编程NIO 2、流与块的比较... 查看详情

nio框架之mina源码解析:底层通信与责任链模式应用

本文主要介绍下在mina中责任链模式的应用以及mina对于数据读写的处理。 在mina中,对数据的读操作是在processor类里面触发的,收到新消息后就触发读数据链去处理新消息直到自己的业务逻辑代码(IoHandler)。 在mina中,... 查看详情

java的nio入门基础

1.nio的简介nio的FileChannel类可以获取的方法有FileInputStream和FileOutputStream以及RandomAccessFile的getChannel方法,或者FileChannel类的open方法等获取,SocketChannel,ServerSocketChannel,DatagramChannel,同样可以Socket,ServerSocket的getCh 查看详情

android原生ui开发框架《jetpackcompose入门到精通》最全上手指南

前言在去年的Google/IO大会上,亮相了一个全新的Android原生UI开发框架-JetpackCompose,与苹果的SwiftIUI一样,JetpackCompose是一个声明式的UI框架,随着了今年安卓和苹果两大移动平台相继推出自己的UI开发框架JetpackCompose和Swif... 查看详情

nio入门(代码片段)

NIO入门目录NIO入门输入/输出:概念性描述流与块的比较通道和缓冲区缓冲区缓冲区类型通道通道类型实践:NIO的读与写从文件中读取写入文件读写结合缓冲区内部细节状态变量访问方法get()方法put()方法类型化的get()和put()方法关... 查看详情

nio入门(代码片段)

原文地址:https://www.ibm.com/developerworks/cn/education/java/j-nio/j-nio.htmlNIO入门</div><!--ArticleBody--><h2id="N1003A"class="ibm-h2">在开始之前</h2><h3id="N10043"class="ibm-h3"> 查看详情

nio入门

新的输入/输出(NIO)库是在JDK1.4中引入的。NIO弥补了原来的I/O的不足,它在标准Java代码中提供了高速的、面向块的I/O。通过定义包含数据的类,以及通过以块的形式处理这些数据,NIO不用使用本机代码就可以利用低级优化,这是... 查看详情

nio相关基础篇二

转载请注明原创出处,谢谢!上篇NIO相关基础篇一,主要介绍了一些基本的概念以及缓冲区(Buffer)和通道(Channel),本篇继续NIO相关话题内容,主要就是文件锁、以及比较关键的Selector,后续还会继续有一到二篇左右与NIO内容... 查看详情