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

简介: 前言本文将演示一个iOS客户端程序,通过UDP协议与两个典型的NIO框架服务端,实现跨平台双向通信的完整Demo。服务端将分别用MINA2和Netty4进行实现,而通信时服务端你只需选其一就行了。

前言

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

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

实际上,MINA2和Netty4的官方代码里已经有UDP通信的Demo代码,但客户端并不是基于现今流行的移动端(主要是Android和iOS端)来实现,本文将演示用iOS客户端来实现这种跨平台的双向网络通信。演示Demo中,已经解决跨平台通信时的乱码、数据字节异常等问题,请继续往下阅读。

学习交流

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

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

《NIO框架入门》系列文章

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

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

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

NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示

NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战》(本文

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

本文亮点

客户端基于iOS移动端平台实现:

通常这类跨平台的网络通信例子很难找,本文已解决跨平台通信的适配问题,是个难得的实践入门示例;

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

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

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

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

本文Demo的场景逻辑

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

如上所述,服务端和客户端都要实现消息的发送和接收,即实现跨平台的双向通信。如果有心的话,稍加改造,也就很容易实现一个简陋的聊天程序了。下节将将给出真正的实现代码。

iOS客户端准备工作

[Step 1] 去Github上下载最新的CocoaAsyncSocket:

CocoaAsyncSocket源码地址:https://github.com/52im/CocoaAsyncSocket,如下图:

补充说明:iOS里的网络编程有多种途径实现(具体请参看此文),本文选择的是iOS里非常热门的 CocoaAsyncSocket 工程,它对iOS原生网络API做了进一步封装,使得开发者更易使用。

[Step 2] 建好XCode工程,�准备开撸:

建好工程后把CocoaAsyncSocket的源码引用进来就行了,如下图:

补充说明:如何新建一个XCode工程请自行百度之,按照系统默认的简单建立一个就好了,本例不需要作额外配置和额外的系统库引用。

iOS客户端代码实现

[1] 客户端主类 ViewController.m:

//  Copyright (C) 2016 即时通讯网(52im.net)- 即时通讯开发者社区.

//  All rights reserved.

//  Created by JackJiang on 16/06/22.

#import"ViewController.h"

#import"LocalUDPSocketProvider.h"

#import"LocalUDPDataSender.h"

#import"CharsetHelper.h"

#import"UDPUtils.h"

@interfaceViewController ()

@end

@implementationViewController

- (void)viewDidLoad

{

[superviewDidLoad];

// 初始化socket

[[LocalUDPSocketProvider sharedInstance] initialLocalUDPSocket];

// 注意:执行延迟的单位是秒哦

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5target:self selector:@selector(doSend) userInfo:nil repeats:YES];

[timer fire];

}

- (void)doSend

{

NSString *toServer = [NSString stringWithFormat:@"Hi,我是iOS客户端,我的时间戳 %li",[UDPUtils getTimeStampWithMillisecond_l]];

[[LocalUDPDataSender sharedInstance] send:[CharsetHelper getBytesWithString:toServer]];

}

@end

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

[2] 客户端Socket管理类 LocalUDPSocketProvider.m:

//  Copyright (C) 2016 即时通讯网(52im.net)- 即时通讯开发者社区.

//  All rights reserved.

//  Created by JackJiang on 16/06/22.

#import"LocalUDPSocketProvider.h"

#import"GCDAsyncUdpSocket.h"

#import"ConfigEntity.h"

#import"CompletionDefine.h"

@interfaceLocalUDPSocketProvider ()

@property(nonatomic, retain) GCDAsyncUdpSocket *localUDPSocket;

@property(nonatomic, copy) ConnectionCompletion connectionCompletionOnce_;

@end

@implementationLocalUDPSocketProvider

// 本类的单例对象

staticLocalUDPSocketProvider *instance = nil;

+ (LocalUDPSocketProvider *)sharedInstance

{

if(instance == nil)

instance = [[superallocWithZone:NULL] init];

returninstance;

}

- (GCDAsyncUdpSocket *)initialLocalUDPSocket

{

NSLog(@"【IMCORE】new GCDAsyncUdpSocket中...");

// ** Setup our socket.

self.localUDPSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];

// ** START udp socket

// 本地绑定端口合法性检查

intport = [ConfigEntity getLocalUdpSendAndListeningPort];

if(port <0|| port >65535)

port =0;

NSError *error = nil;

// 绑定到指定端口(以便收发数据)

if(![self.localUDPSocket bindToPort:port error:&error])

{

NSLog(@"【IMCORE】localUDPSocket创建时出错,原因是 bindToPort: %@", error);

returnnil;

}

// 开启收数据处理

if(![self.localUDPSocket beginReceiving:&error])

{

NSLog(@"【IMCORE】localUDPSocket创建时出错,原因是 beginReceiving: %@", error);

returnnil;

}

NSLog(@"【IMCORE】localUDPSocket创建已成功完成.");

returnself.localUDPSocket;

}

。。。。。。

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data

fromAddress:(NSData *)address

withFilterContext:(id)filterContext

{

NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

if(msg)

NSLog(@"【UDP_SOCKET】【NOTE】>>>>>> 收到服务端的消息: %@", msg);

else

{

NSString *host = nil;

uint16_t port =0;

[GCDAsyncUdpSocket getHost:&host port:&port fromAddress:address];

NSLog(@"【UDP_SOCKET】RECV: Unknown message from: %@:%hu", host, port);

}

}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address

{

NSLog(@"【UDP_SOCKET】成收到的了UDP的connect反馈, isCOnnected?%d", [sock isConnected]);

// 连接结果回调

if(self.connectionCompletionOnce_ != nil)

self.connectionCompletionOnce_(YES);

}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError *)error

{

NSLog(@"【UDP_SOCKET】成收到的了UDP的connect反馈,但连接没有成功, isCOnnected?%d", [sock isConnected]);

// 连接结果回调

if(self.connectionCompletionOnce_ != nil)

self.connectionCompletionOnce_(NO);

}

@end

补充说明:�因代码较多,文中没有列出该类的所有代码,请前往文末下载完整XCode工程自行查看哦。

[3] 数据发送实现类 LocalUDPDataSender.m:

//  Copyright (C) 2016 即时通讯网(52im.net)- 即时通讯开发者社区.

//  All rights reserved.

//  Created by JackJiang on 16/06/22.

#import"LocalUDPDataSender.h"

#import"CharsetHelper.h"

#import"GCDAsyncUdpSocket.h"

#import"LocalUDPSocketProvider.h"

#import"ConfigEntity.h"

#import"UDPUtils.h"

#import"CompletionDefine.h"

@implementationLocalUDPDataSender

// 本类的单例对象

staticLocalUDPDataSender *instance = nil;

- (BOOL) send:(NSData *)dataWithBytes

{

// 获得UDPSocket实例

GCDAsyncUdpSocket *ds = [[LocalUDPSocketProvider sharedInstance] getLocalUDPSocket];

// 确保发送数据开始前,已经进行connect的操作:如果Socket没有“连接”上服务端,尝试“连接”一次

if(ds != nil && ![ds isConnected])

{

// 此次数据只在“连接”成功后发出,“连接”成功则会调用此回调block代码

ConnectionCompletion observerBlock = ^(BOOL connectResult) {

// 成功建立了UDP连接后就把包发出去

if(connectResult)

[UDPUtils sendImpl:ds withData:dataWithBytes];

};

// 调置连接回调

[[LocalUDPSocketProvider sharedInstance] setConnectionObserver:observerBlock];

NSError *connectError = nil;

BOOL connectResult = [[LocalUDPSocketProvider sharedInstance] tryConnectToHost:&connectError withSocket:ds completion:observerBlock];

// 如果连接意图没有成功发出则返回错误码

returnconnectResult;

}

else

return[UDPUtils sendImpl:ds withData:dataWithBytes];

}

// 获取本类的单例。使用单例访问本类的所有资源是唯一的合法途径。

+ (LocalUDPDataSender *)sharedInstance

{

if(instance == nil)

instance = [[superallocWithZone:NULL] init];

returninstance;

}

@end

服务端准备工作

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

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

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

服务端代码实现

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

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

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

Demo 运行行截图

[1] 客户端运行结果:

iOS 客户端运行结果

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

服务端运行结果(MINA2方案)

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

服务端运行结果(Netty4方案)

本文小结

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

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

更多学习资源

[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-378-1-1.html自行下载。

完整源码工程截图如下:

iOS客户端的Demo源码
服务端(MINA2和Netty4两个方案)

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

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

出处:http://www.52im.net/thread-373-1-1.html

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

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

目录
相关文章
|
3月前
|
物联网 区块链 vr&ar
未来已来:探索区块链、物联网与虚拟现实技术的融合与应用安卓与iOS开发中的跨平台框架选择
【8月更文挑战第30天】在科技的巨轮下,新技术不断涌现,引领着社会进步。本文将聚焦于当前最前沿的技术——区块链、物联网和虚拟现实,探讨它们各自的发展趋势及其在未来可能的应用场景。我们将从这些技术的基本定义出发,逐步深入到它们的相互作用和集成应用,最后展望它们如何共同塑造一个全新的数字生态系统。
|
1月前
|
网络协议 Linux 网络性能优化
Linux C/C++之TCP / UDP通信
这篇文章详细介绍了Linux下C/C++语言实现TCP和UDP通信的方法,包括网络基础、通信模型、编程示例以及TCP和UDP的优缺点比较。
38 0
Linux C/C++之TCP / UDP通信
|
1月前
|
开发框架 移动开发 Android开发
安卓与iOS开发中的跨平台解决方案:Flutter入门
【9月更文挑战第30天】在移动应用开发的广阔舞台上,安卓和iOS两大操作系统各自占据半壁江山。开发者们常常面临着选择:是专注于单一平台深耕细作,还是寻找一种能够横跨两大系统的开发方案?Flutter,作为一种新兴的跨平台UI工具包,正以其现代、响应式的特点赢得开发者的青睐。本文将带你一探究竟,从Flutter的基础概念到实战应用,深入浅出地介绍这一技术的魅力所在。
82 7
|
2月前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台解决方案
【9月更文挑战第27天】在移动应用开发的广阔天地中,安卓和iOS两大操作系统如同双子星座般耀眼。开发者们在这两大平台上追逐着创新的梦想,却也面临着选择的难题。如何在保持高效的同时,实现跨平台的开发?本文将带你探索跨平台开发的魅力所在,揭示其背后的技术原理,并通过实际案例展示其应用场景。无论你是安卓的忠实拥趸,还是iOS的狂热粉丝,这篇文章都将为你打开一扇通往跨平台开发新世界的大门。
|
2月前
NIO-编程实战(二)
NIO-编程实战(二)
|
2月前
|
Java
Netty BIO/NIO/AIO介绍
Netty BIO/NIO/AIO介绍
|
2月前
|
设计模式 前端开发 JavaScript
探索移动应用开发:从Android到iOS的跨平台之旅
【9月更文挑战第21天】在这篇文章中,我们将一同揭开移动应用开发的神秘面纱,从Android和iOS这两个主流平台出发,探讨如何利用现代技术栈实现跨平台开发。文章将通过具体的代码示例,带领读者理解不同平台间的差异与联系,以及如何运用React Native框架简化开发流程,实现一次编写,多平台运行的目标。无论你是刚入门的新手还是希望拓展技能的老手,这篇文章都将为你提供宝贵的知识和启示。
70 3
|
2月前
|
C语言
C语言 网络编程(七)UDP通信创建流程
本文档详细介绍了使用 UDP 协议进行通信的过程,包括创建套接字、发送与接收消息等关键步骤。首先,通过 `socket()` 函数创建套接字,并设置相应的参数。接着,使用 `sendto()` 函数向指定地址发送数据。为了绑定地址,需要调用 `bind()` 函数。接收端则通过 `recvfrom()` 函数接收数据并获取发送方的地址信息。文档还提供了完整的代码示例,展示了如何实现 UDP 的发送端和服务端功能。
|
3月前
|
开发工具 C语言 Swift
探索iOS开发之旅:从入门到精通
【8月更文挑战第30天】在这篇文章中,我们将一起踏上一场关于iOS开发的奇妙旅程。无论你是刚刚接触iOS开发的新手,还是希望提升自己技能的开发者,这篇文章都将为你提供有价值的指导和启示。我们将从基础的iOS开发概念开始,逐步深入到高级技巧和最佳实践。通过这篇文章,你将了解到如何构建一个成功的iOS应用程序,以及如何不断提升自己的开发技能。让我们一起开启这场探索之旅吧!
57 4
|
2月前
|
缓存 Java Linux
NIO-编程实战(一)
NIO-编程实战(一)