<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Jack Jiang

    我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
    posts - 494, comments - 13, trackbacks - 0, articles - 1

     


    申明:本文由作者基于日常實(shí)踐整理,希望對(duì)初次接觸MINA、Netty的人有所啟發(fā)。如需與作者交流,見文簽名,互相學(xué)習(xí)。 



    學(xué)習(xí)交流

     

    • 更多學(xué)習(xí)資料:點(diǎn)此進(jìn)入 推薦
    • 移動(dòng)端即時(shí)通訊交流: 215891622 推薦

      

    前言

    NIO框架的流行,使得開發(fā)大并發(fā)、高性能的互聯(lián)網(wǎng)服務(wù)端成為可能。這其中最流行的無非就是MINA和Netty了,MINA目前的主要版本是MINA2、而Netty的主要版本是Netty3Netty4(Netty5已經(jīng)被取消開發(fā)了:詳見此文)。


    本文將演示的是一個(gè)基于Netty4的UDP服務(wù)端和一個(gè)標(biāo)準(zhǔn)UDP客戶端(Java實(shí)現(xiàn))雙向通信的完整例子。實(shí)際上,Netty4的UDP例子非常難找(恕我愚鈍,找遍全網(wǎng)也沒有有價(jià)值的代碼,有也是Netty3,而Netty3和Netty4的風(fēng)格差的不是一點(diǎn)點(diǎn),參考意義不大),官方的代碼演示里只有一個(gè)簡單的UDP廣播例子,不足以用于演示Netty4的UDP通信最佳實(shí)踐。

     

    本文亮點(diǎn)

    [1] Netty4的UDP例子太難找:

    Netty4的完整雙向UDP通信例子很難找(官方?jīng)]有),本文就是要用代碼來演示這個(gè);

    [2] 本例中客戶端UDP實(shí)現(xiàn)無需第3方依賴:

    通常MINA或Netty的例子里,客戶端也都是用的MINA或Netty的客戶端lib,本文將直接使用標(biāo)準(zhǔn)UDP代碼(便于跨平臺(tái)實(shí)現(xiàn),比如iOS平臺(tái)),不依賴于第3方包;

    [3] 完整可執(zhí)行源碼、方便學(xué)習(xí):

    完整的Demo源碼,適合新手直接運(yùn)行,便于學(xué)習(xí)和研究。

     

    題外話

    社區(qū)里經(jīng)常在爭論到底該用MINA還是Netty,比如這幾篇文章:《開源NIO框架八卦——到底是先有MINA還是先有Netty?》、《有關(guān)“為何選擇Netty”的11個(gè)疑問及解答》、《選Netty還是Mina:深入研究與對(duì)比(一)》,哈哈這確實(shí)挺難抉擇的。不過,個(gè)人觀點(diǎn)是,對(duì)那個(gè)熟悉就用哪個(gè)吧,沒什么好糾結(jié)的,必竟二者并比本質(zhì)區(qū)別,也都是出自同一作者之手。

     

    寫個(gè)什么樣的Demo?

    言歸正傳,本文要演示的Demo包含兩部分,Java UDP客戶端和Netty4 UDP服務(wù)端,客戶端將每隔3秒向服務(wù)端發(fā)送一條消息,而服務(wù)端在收到消息后馬上回復(fù)一條消息給客戶端。


    也就是說,服務(wù)端和客戶端都要實(shí)現(xiàn)消息的發(fā)送和接收,這也就實(shí)現(xiàn)了雙向通信。如果有心的話,稍加改造,也就很容易實(shí)現(xiàn)一個(gè)簡陋的聊天程序了。下節(jié)將將給出真正的實(shí)現(xiàn)代碼。

     

    Netty4 服務(wù)端準(zhǔn)備工作

    [1] 第一步:下載Netty4

    Netty4的官方網(wǎng)站是:http://netty.io/,直接下最新的Netty 4.1吧,如下圖:


     

    [2] 第二步:找到Netty4的核心庫文件

    直接用這個(gè)all in one的jar包吧,反正用在服務(wù)端,2M大小的東西無所謂,方便管理,見下圖:


     

    [3] 第三步:建好Java工程準(zhǔn)備準(zhǔn)備開擼

    個(gè)人習(xí)慣用Eclipse,你用NetBeans或InteliJ也都沒問題,具體建立過程不熟練的衣自行百度吧,唯一注意的是把Netty4的lib包引用進(jìn)來就行了,我的見下圖:


     

    服務(wù)端代碼

    [1] 服務(wù)端主類 EchoServer.java


     1 public class EchoServer

     2 {

     3         public static void main(String[] args) throws InterruptedException

     4         {

     5                 Bootstrap b = new Bootstrap();

     6                 EventLoopGroup group = new NioEventLoopGroup();

     7                 b.group(group)

     8                         .channel(NioDatagramChannel.class)

     9                         .handler(new EchoSeverHandler());

    10                  

    11                 // 服務(wù)端監(jiān)聽在9999端口

    12                 b.bind(9999).sync().channel().closeFuture().await();

    13         }

    14 }


    如上述代碼所示:不得不說,Netty4的UDP服務(wù)端代碼實(shí)現(xiàn)起來確實(shí)很簡單,一個(gè) Bootstrap、一個(gè) EventLoopGroup、外加一個(gè)SimpleChannelInboundHandler,就這么被Netty4輕松搞定(準(zhǔn)確地說Netty4是在Java NIO上的封裝而已,但最終API對(duì)開發(fā)者來說確實(shí)很友好)。


    吐個(gè)槽:話說Netty4的代碼跟MINA相比,風(fēng)格確實(shí)大不相同,雖說兩者有很深的源源,但經(jīng)過Netty3、Netty4的進(jìn)化,兩者的差異(至少代碼看起來是這樣)還是很明顯的。另外,b.bind(9999).sync().channel().closeFuture().await() 這一大串的連續(xù)方法調(diào)用,看起來很詭異,不小心調(diào)錯(cuò)的話,服務(wù)器會(huì)不會(huì)爆炸?

     

    [2] 服務(wù)端Handler類 EchoSeverHandler.java


     1 public class EchoSeverHandler extends SimpleChannelInboundHandler<DatagramPacket> 

     2 {

     3         @Override

     4         protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet)

     5                         throws Exception

     6         {

     7                 // 讀取收到的數(shù)據(jù)

     8                 ByteBuf buf = (ByteBuf) packet.copy().content();

     9                 byte[] req = new byte[buf.readableBytes()];

    10                 buf.readBytes(req);

    11                 String body = new String(req, CharsetUtil.UTF_8);

    12                 System.out.println("【NOTE】>>>>>> 收到客戶端的數(shù)據(jù):"+body); 

    13                  

    14                 // 回復(fù)一條信息給客戶端

    15                 ctx.writeAndFlush(new DatagramPacket(

    16                 Unpooled.copiedBuffer("Hello,我是Server,我的時(shí)間戳是"+System.currentTimeMillis()

    17                                 , CharsetUtil.UTF_8)

    18                                 , packet.sender())).sync();

    19         }

    20 }


    如上所示,這個(gè)Handler看起來比MINA的類似實(shí)現(xiàn),要簡潔不少。

     

    客戶端代碼

    為了讓客戶端代碼看起來更簡潔,我把Socket管理代碼提煉到 LocalUDPSocketProvider類、把UDP數(shù)據(jù)監(jiān)聽和接收提煉到了 LocalUDPDataReciever類(實(shí)際上這兩個(gè)同名類是簡化自MobileIMSDK工程哦)。

     

    [1] 客戶端主類 EchoClient.java


    public class EchoClient

    {

            public static void main(String[] args) throws Exception

            {

                    // 初始化本地UDP的Socket

                    LocalUDPSocketProvider.getInstance().initSocket();

                    // 啟動(dòng)本地UDP監(jiān)聽(接收數(shù)據(jù)用的)

                    LocalUDPDataReciever.getInstance().startup();

                     

                    // 循環(huán)發(fā)送數(shù)據(jù)給服務(wù)端

                    while(true)

                    {

                            // 要發(fā)送的數(shù)據(jù)

                            String toServer = "Hi,我是客戶端,我的時(shí)間戳"+System.currentTimeMillis();

                            byte[] soServerBytes = toServer.getBytes("UTF-8");

                             

                            // 開始發(fā)送

                            boolean ok = UDPUtils.send(soServerBytes, soServerBytes.length);

                            if(ok)

                                    Log.d("EchoClient", "發(fā)往服務(wù)端的信息已送出.");

                            else

                                    Log.e("EchoClient", "發(fā)往服務(wù)端的信息沒有成功發(fā)出!!!");

                             

                            // 3000秒后進(jìn)入下一次循環(huán)

                            Thread.sleep(3000);

                    }

            }

    }


    補(bǔ)充說明:客戶端代碼沒有使用任何依賴,純Java UDP代碼實(shí)現(xiàn)(如果是Andriod平臺(tái),代碼也幾乎不用改就能用),部分代碼修改自 開源即時(shí)通訊框架 MobileIMSDK(去掉了很多保證健壯性代碼,現(xiàn)在看起來要簡潔的多,便于初學(xué)者學(xué)習(xí))。

     

    [2] Socket操作類 LocalUDPSocketProvider.java


     1 public class LocalUDPSocketProvider

     2 {

     3         private static final String TAG = LocalUDPSocketProvider.class.getSimpleName();

     4         private static LocalUDPSocketProvider instance = null;

     5         private DatagramSocket localUDPSocket = null;

     6 

     7         public static LocalUDPSocketProvider getInstance()

     8         {

     9                 if (instance == null)

    10                         instance = new LocalUDPSocketProvider();

    11                 return instance;

    12         }

    13          

    14         public void initSocket()

    15         {

    16                 try

    17                 {

    18                         // UDP本地監(jiān)聽端口(如果為0將表示由系統(tǒng)分配,否則使用指定端口)

    19                         this.localUDPSocket = new DatagramSocket(ConfigEntity.localUDPPort);

    20                         // 調(diào)用connect之后,每次send時(shí)DatagramPacket就不需要設(shè)計(jì)目標(biāo)主機(jī)的ip和port了

    21                         // * 注意:connect方法一定要在DatagramSocket.receive()方法之前調(diào)用,

    22                         // * 不然整send數(shù)據(jù)將會(huì)被錯(cuò)誤地阻塞。這或許是官方API的bug,也或許是調(diào)

    23                         // * 用規(guī)范就應(yīng)該這樣,但沒有找到官方明確的說明

    24                         this.localUDPSocket.connect(

    25                                         InetAddress.getByName(ConfigEntity.serverIP), ConfigEntity.serverUDPPort);

    26                         this.localUDPSocket.setReuseAddress(true);

    27                         Log.d(TAG, "new DatagramSocket()已成功完成.");

    28                 }

    29                 catch (Exception e)

    30                 {

    31                         Log.w(TAG, "localUDPSocket創(chuàng)建時(shí)出錯(cuò),原因是:" + e.getMessage(), e);

    32                 }

    33         }

    34 

    35         public DatagramSocket getLocalUDPSocket()

    36         {

    37                 return this.localUDPSocket;

    38         }

    39 }


     

    [3] 數(shù)據(jù)接收類 LocalUDPDataReciever.java


     1 public class LocalUDPDataReciever

     2 {

     3         private static final String TAG = LocalUDPDataReciever.class.getSimpleName();

     4         private static LocalUDPDataReciever instance = null;

     5         private Thread thread = null;

     6 

     7         public static LocalUDPDataReciever getInstance()

     8         {

     9                 if (instance == null)

    10                         instance = new LocalUDPDataReciever();

    11                 return instance;

    12         }

    13 

    14         public void startup()

    15         {

    16                 this.thread = new Thread(new Runnable()

    17                 {

    18                         public void run()

    19                         {

    20                                 try

    21                                 {

    22                                         Log.d(LocalUDPDataReciever.TAG, "本地UDP端口偵聽中,端口=" + ConfigEntity.localUDPPort + "...");

    23 

    24                                         //開始偵聽

    25                                         LocalUDPDataReciever.this.udpListeningImpl();

    26                                 }

    27                                 catch (Exception eee)

    28                                 {

    29                                         Log.w(LocalUDPDataReciever.TAG, "本地UDP監(jiān)聽停止了(socket被關(guān)閉了?)," + eee.getMessage(), eee);

    30                                 }

    31                         }

    32                 });

    33                 this.thread.start();

    34         }

    35 

    36         private void udpListeningImpl() throws Exception

    37         {

    38                 while (true)

    39                 {

    40                         byte[] data = new byte[1024];

    41                         // 接收數(shù)據(jù)報(bào)的包

    42                         DatagramPacket packet = new DatagramPacket(data, data.length);

    43 

    44                         DatagramSocket localUDPSocket = LocalUDPSocketProvider.getInstance().getLocalUDPSocket();

    45                         if ((localUDPSocket == null) || (localUDPSocket.isClosed()))

    46                                 continue;

    47                          

    48                         // 阻塞直到收到數(shù)據(jù)

    49                         localUDPSocket.receive(packet);

    50                          

    51                         // 解析服務(wù)端發(fā)過來的數(shù)據(jù)

    52                         String pFromServer = new String(packet.getData(), 0 , packet.getLength(), "UTF-8");

    53                         Log.w(LocalUDPDataReciever.TAG, "【NOTE】>>>>>> 收到服務(wù)端的消息:"+pFromServer);

    54                 }

    55         }

    56 }


     

    運(yùn)行效果

    客戶端運(yùn)行結(jié)果:



    服務(wù)端運(yùn)行結(jié)果:


     

    補(bǔ)充說明

    客戶端代碼沒什么特別,就是標(biāo)準(zhǔn)的Java UDP代碼,看起來跟其它例子不一樣的原因只是我把它提煉了一下,沒本質(zhì)區(qū)別。同樣的代碼改改也可以很好的用在Android端。實(shí)際上,上面的客戶端代碼就是從開源 MobileIMSDK 的Java端扒出來的,有興趣的也可以看看它的Android端Server端iOS端,簡化一下可以用作你自已的各種用途。

     

    更多學(xué)習(xí)資源

    [1] MINA和Netty的源碼在線學(xué)習(xí)和查閱:

    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] 更多有關(guān)NIO編程的資料:

    請(qǐng)進(jìn)入精華資料專輯:http://www.52im.net/forum.php?mod=collection&action=view&ctid=9


    [4] 有關(guān)IM聊天應(yīng)用、消息推送技術(shù)的資料:

    請(qǐng)進(jìn)入精華資料專輯:http://www.52im.net/forum.php?mod=collection&op=all


    [5] 技術(shù)交流和學(xué)習(xí):

    可直接進(jìn)入 即時(shí)通訊開發(fā)者社區(qū) 討論和學(xué)習(xí)網(wǎng)絡(luò)編程、IM聊天應(yīng)用、消息推送應(yīng)用的開發(fā)。

     

    完整源碼工程下載

    不知如何上傳附件,如需完整Eclipse源碼工程請(qǐng)聯(lián)系作者,或者進(jìn)入鏈接 http://www.52im.net/thread-367-1-1.html 自行下載。

    完整源碼工程截圖如下:

    (本文同步發(fā)布于:http://www.52im.net/thread-367-1-1.html)



    作者:Jack Jiang (點(diǎn)擊作者姓名進(jìn)入Github)
    出處:http://www.52im.net/space-uid-1.html
    交流:歡迎加入即時(shí)通訊開發(fā)交流群 215891622
    討論:http://www.52im.net/
    Jack Jiang同時(shí)是【原創(chuàng)Java Swing外觀工程BeautyEye】【輕量級(jí)移動(dòng)端即時(shí)通訊框架MobileIMSDK】的作者,可前往下載交流。
    本博文 歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明出處(也可前往 我的52im.net 找到我)。


    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    Jack Jiang的 Mail: jb2011@163.com, 聯(lián)系QQ: 413980957, 微信: hellojackjiang
    主站蜘蛛池模板: 6080午夜一级毛片免费看6080夜福利| 在线观看国产一区亚洲bd| 亚洲av无码专区国产乱码在线观看 | 亚洲一级毛片免费观看| 亚洲神级电影国语版| 亚洲制服丝袜精品久久| 亚洲冬月枫中文字幕在线看| 亚洲a级在线观看| 在线aⅴ亚洲中文字幕| 亚洲av永久无码天堂网| 亚洲成a人片在线观看天堂无码| 亚洲av日韩av永久无码电影 | 国产亚洲综合网曝门系列| 亚洲va无码手机在线电影| 亚洲国产精品一区二区久久| 亚洲毛片基地日韩毛片基地| 亚洲91精品麻豆国产系列在线| 亚洲一区欧洲一区| 精品久久久久久亚洲综合网| 日本永久免费a∨在线视频 | 国产亚洲sss在线播放| 亚洲精品乱码久久久久蜜桃| 免费毛片毛片网址| 中文字幕乱码免费看电影| 99爱免费观看视频在线| 无遮免费网站在线入口| 国产一区二区三区在线免费| MM131亚洲国产美女久久| 亚洲AV日韩AV鸥美在线观看| 亚洲人成免费电影| 狠狠热精品免费观看| 国产免费阿v精品视频网址| 2021国产精品成人免费视频| 日本免费网站在线观看| 亚洲日产韩国一二三四区| 久久综合亚洲鲁鲁五月天| 亚洲精品国产高清在线观看| 中文在线免费看视频| 7723日本高清完整版免费| 亚洲国产精品综合久久网络| 亚洲视频在线免费播放|