申明:本文由作者基于日常实践整理,希望对初次接触mina、netty的人有所启发。如需与作者交流,见文签名,互相学习。
学习交流
- 更多学习资料: 推荐
- 移动端即时通讯交流: 215891622 推荐
nio框架的流行,使得开发大并发、高性能的互联网服务端成为可能。这其中最流行的无非就是mina和netty了,mina目前的主要版本是、而netty的主要版本是和(netty5已经被取消开发了:)。
本文将演示的是一个基于netty4的udp服务端和一个标准udp客户端(java实现)双向通信的完整例子。实际上,netty4的udp例子非常难找(恕我愚钝,找遍全网也没有有价值的代码,有也是netty3,而netty3和netty4的风格差的不是一点点,参考意义不大),官方的代码演示里只有一个简单的udp广播例子,不足以用于演示netty4的udp通信最佳实践。
[1] netty4的udp例子太难找:
netty4的完整双向udp通信例子很难找(官方没有),本文就是要用代码来演示这个;
[2] 本例中客户端udp实现无需第3方依赖:
通常mina或netty的例子里,客户端也都是用的mina或netty的客户端lib,本文将直接使用标准udp代码(便于跨平台实现,比如ios平台),不依赖于第3方包;
[3] 完整可执行源码、方便学习:
完整的demo源码,适合新手直接运行,便于学习和研究。
社区里经常在争论到底该用mina还是netty,比如这几篇文章:《》、《》、《》,哈哈这确实挺难抉择的。不过,个人观点是,对那个熟悉就用哪个吧,没什么好纠结的,必竟二者并比本质区别,也都是出自同一作者之手。
言归正传,本文要演示的demo包含两部分,java udp客户端和netty4 udp服务端,客户端将每隔3秒向服务端发送一条消息,而服务端在收到消息后马上回复一条消息给客户端。
也就是说,服务端和客户端都要实现消息的发送和接收,这也就实现了双向通信。如果有心的话,稍加改造,也就很容易实现一个简陋的聊天程序了。下节将将给出真正的实现代码。
[1] 第一步:下载netty4
netty4的官方网站是:,直接下最新的netty 4.1吧,如下图:
[2] 第二步:找到netty4的核心库文件
直接用这个all in one的jar包吧,反正用在服务端,2m大小的东西无所谓,方便管理,见下图:
[3] 第三步:建好java工程准备准备开撸
个人习惯用eclipse,你用netbeans或intelij也都没问题,具体建立过程不熟练的衣自行百度吧,唯一注意的是把netty4的lib包引用进来就行了,我的见下图:
[1] 服务端主类 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 // 服务端监听在9999端口
12 b.bind(9999).sync().channel().closefuture().await();
13 }
14 }
如上述代码所示:不得不说,netty4的udp服务端代码实现起来确实很简单,一个 、一个 、外加一个,就这么被netty4轻松搞定(准确地说netty4是在java nio上的封装而已,但最终api对开发者来说确实很友好)。
吐个槽:话说netty4的代码跟mina相比,风格确实大不相同,虽说两者有很深的源源,但经过netty3、netty4的进化,两者的差异(至少代码看起来是这样)还是很明显的。另外,b.bind(9999).sync().channel().closefuture().await() 这一大串的连续方法调用,看起来很诡异,不小心调错的话,服务器会不会爆炸?
[2] 服务端handler类 echoseverhandler.java
1 public class echoseverhandler extends simplechannelinboundhandler
2 {
3 @override
4 protected void channelread0(channelhandlercontext ctx, datagrampacket packet)
5 throws exception
6 {
7 // 读取收到的数据
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】>>>>>> 收到客户端的数据:" body);
13
14 // 回复一条信息给客户端
15 ctx.writeandflush(new datagrampacket(
16 unpooled.copiedbuffer("hello,我是server,我的时间戳是" system.currenttimemillis()
17 , charsetutil.utf_8)
18 , packet.sender())).sync();
19 }
20 }
如上所示,这个handler看起来比mina的类似实现,要简洁不少。
为了让客户端代码看起来更简洁,我把socket管理代码提炼到 localudpsocketprovider类、把udp数据监听和接收提炼到了 localudpdatareciever类(实际上这两个同名类是简化自工程哦)。
[1] 客户端主类 echoclient.java
public class echoclient
{
public static void main(string[] args) throws exception
{
// 初始化本地udp的socket
localudpsocketprovider.getinstance().initsocket();
// 启动本地udp监听(接收数据用的)
localudpdatareciever.getinstance().startup();
// 循环发送数据给服务端
while(true)
{
// 要发送的数据
string toserver = "hi,我是客户端,我的时间戳" system.currenttimemillis();
byte[] soserverbytes = toserver.getbytes("utf-8");
// 开始发送
boolean ok = udputils.send(soserverbytes, soserverbytes.length);
if(ok)
log.d("echoclient", "发往服务端的信息已送出.");
else
log.e("echoclient", "发往服务端的信息没有成功发出!!!");
// 3000秒后进入下一次循环
thread.sleep(3000);
}
}
}
补充说明:客户端代码没有使用任何依赖,纯java udp代码实现(如果是andriod平台,代码也几乎不用改就能用),部分代码修改自 (去掉了很多保证健壮性代码,现在看起来要简洁的多,便于初学者学习)。
[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本地监听端口(如果为0将表示由系统分配,否则使用指定端口)
19 this.localudpsocket = new datagramsocket(configentity.localudpport);
20 // 调用connect之后,每次send时datagrampacket就不需要设计目标主机的ip和port了
21 // * 注意:connect方法一定要在datagramsocket.receive()方法之前调用,
22 // * 不然整send数据将会被错误地阻塞。这或许是官方api的bug,也或许是调
23 // * 用规范就应该这样,但没有找到官方明确的说明
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创建时出错,原因是:" e.getmessage(), e);
32 }
33 }
34
35 public datagramsocket getlocaludpsocket()
36 {
37 return this.localudpsocket;
38 }
39 }
[3] 数据接收类 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监听停止了(socket被关闭了?)," 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 // 接收数据报的包
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 // 阻塞直到收到数据
49 localudpsocket.receive(packet);
50
51 // 解析服务端发过来的数据
52 string pfromserver = new string(packet.getdata(), 0 , packet.getlength(), "utf-8");
53 log.w(localudpdatareciever.tag, "【note】>>>>>> 收到服务端的消息:" pfromserver);
54 }
55 }
56 }
客户端运行结果:
服务端运行结果:
客户端代码没什么特别,就是标准的java udp代码,看起来跟其它例子不一样的原因只是我把它提炼了一下,没本质区别。同样的代码改改也可以很好的用在android端。实际上,上面的客户端代码就是从 的java端扒出来的,有兴趣的也可以看看它的、、,简化一下可以用作你自已的各种用途。
[1] mina和netty的源码在线学习和查阅:
mina-2.x地址是:
mina-1.x地址是:
netty-4.x地址是:
netty-3.x地址是:
[2] mina和netty的api文档在线查阅:
mina-2.x api文档(在线版):
mina-1.x api文档(在线版):
netty-4.x api文档(在线版):
netty-3.x api文档(在线版):
[3] 更多有关nio编程的资料:
请进入精华资料专辑:
[4] 有关im聊天应用、消息推送技术的资料:
请进入精华资料专辑:
[5] 技术交流和学习:
可直接进入 讨论和学习网络编程、im聊天应用、消息推送应用的开发。
不知如何上传附件,如需完整eclipse源码工程请联系作者,或者进入链接 自行下载。
完整源码工程截图如下:
(本文同步发布于:)
作者: (点击作者姓名进入github)
出处:
交流:欢迎加入即时通讯开发交流群
讨论:
jack jiang同时是和的作者,可前往下载交流。
本博文
欢迎转载,转载请注明出处(也可前往 找到我)。