NIO框架入門(三):iOS與MINA2、Netty4的跨平臺UDP雙向通訊實戰
前言
本文將演示一個iOS客戶端程式,通過UDP協議與兩個典型的NIO框架服務端,實現跨平臺雙向通訊的完整Demo。服務端將分別用MINA2和Netty4進行實現,而通訊時服務端你只需選其一就行了。同時用MINA2和Netty4分別實現服務端的目的,是因為很多人都在糾結到底是用MINA還是Netty來實現高併發的Java網路通訊服務端,在此乾脆兩個都實現了,就看你怎麼選擇了,夠吊吧。
NIO框架的流行,使得開發大併發、高效能的網際網路服務端成為可能。這其中最流行的無非就是MINA和Netty了,MINA目前的主要版本是MINA2、而Netty的主要版本是Netty3和Netty4(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] 客戶端執行結果:
[2] 服務端執行結果(MINA2方案):
[3] 服務端執行結果(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自行下載。
完整原始碼工程截圖如下:
(本文同步釋出於: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】的作者,可前往下載交流。
相關文章
- UDP雙向通訊UDP
- Channel: flutter平臺層與執行層的雙向通訊Flutter
- Flutter實戰(三)檢驗Flutter的跨平臺能力Flutter
- 雲控平臺的雙向音訊解決方案音訊
- 雙向通訊之websocketWeb
- 雙向通訊之SSE
- java實現UDP通訊JavaUDP
- 跨平臺開源通訊元件elastic communication元件AST
- Android中通過Messenger與Service實現程式間雙向通訊AndroidMessenger
- 跨標籤頁通訊以及實戰排坑
- C# 通過socket實現UDP 通訊C#UDP
- Java Nio通訊Java
- 三端易用的現代跨平臺JavaScript Bridge之 IOS篇JavaScriptiOS
- Java--Socket通訊(雙向,有介面)Java
- 我店平臺模式:商家與消費者雙向探索模式
- Go實戰 22 | 網路程式設計:通過 RPC 實現跨平臺服務Go程式設計RPC
- udp網路通訊UDP
- Python - UDP通訊PythonUDP
- ACE中UDP通訊UDP
- Java入門系列-25-NIO(實現非阻塞網路通訊)Java
- iOS專案開發實戰——通過Http Get方式與伺服器通訊iOSHTTP伺服器
- iOS專案開發實戰——通過Http Post方式與伺服器通訊iOSHTTP伺服器
- Linux中的UDP通訊LinuxUDP
- 前端之React實戰:建立跨平臺的專案架構前端React架構
- Electron實現跨平臺全能視訊播放器播放器
- 實現在安卓平臺下的即時通訊安卓
- 【開源】C#跨平臺物聯網通訊框架ServerSuperIO(SSIO)C#框架Server
- Flutter實現Android、iOS跨平臺經驗總結FlutterAndroidiOS
- 網路通訊1:UDPUDP
- JSBridge框架解決通訊問題實現移動端跨平臺開發JS框架
- 不同頁面通訊與跨域跨域
- 跨網段通訊實戰(支援靜態路由表的家用路由)路由
- 視訊採集:iOS平臺基於AVCaptureDevice的實現iOSAPTdev
- Android AIDL SERVICE 雙向通訊 詳解AndroidAI
- iOS第三發平臺元件化解耦實踐iOS元件化解耦
- Netty4實戰 - 編解碼技術Netty
- Avalonia 實現跨平臺的IM即時通訊、語音視訊通話(原始碼,支援信創國產OS,統信、銀河麒麟)原始碼
- 通過 Socket 實現 UDP 程式設計 入門UDP程式設計