将近快一年时间没有更新Netty的博客。一方面原因是因为项目进度的问题。另外一方面是博主有一段时间去熟悉Unity3D引擎。
本章节主要记录博主自己Netty的UDP协议使用。
1. 构建UDP服务端
首先我们应该清楚UDP协议是一种无连接状态的协议。所以Netty框架区别于一般的有链接协议服务端启动程序(ServerBootstrap)。
Netty开发基于UDP协议的服务端需要使用Bootstrap
1 package dev.tinyz.game;
2
3 import io.netty.bootstrap.Bootstrap;
4 import io.netty.buffer.Unpooled;
5 import io.netty.channel.*;
6 import io.netty.channel.nio.NioEventLoopGroup;
7 import io.netty.channel.socket.DatagramPacket;
8 import io.netty.channel.socket.nio.NioDatagramChannel;
9 import io.netty.handler.codec.MessageToMessageDecoder;
10
11 import java.net.InetSocketAddress;
12 import java.nio.charset.Charset;
13 import java.util.List;
14
15 /**
16 * @author TinyZ on 2015/6/8.
17 */
18 public class GameMain {
19
20 public static void main(String[] args) throws InterruptedException {
21
22 final NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
23
24 Bootstrap bootstrap = new Bootstrap();
25 bootstrap.channel(NioDatagramChannel.class);
26 bootstrap.group(nioEventLoopGroup);
27 bootstrap.handler(new ChannelInitializer<NioDatagramChannel>() {
28
29 @Override
30 public void channelActive(ChannelHandlerContext ctx) throws Exception {
31 super.channelActive(ctx);
32 }
33
34 @Override
35 protected void initChannel(NioDatagramChannel ch) throws Exception {
36 ChannelPipeline cp = ch.pipeline();
37 cp.addLast("framer", new MessageToMessageDecoder<DatagramPacket>() {
38 @Override
39 protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List<Object> out) throws Exception {
40 out.add(msg.content().toString(Charset.forName("UTF-8")));
41 }
42 }).addLast("handler", new UdpHandler());
43 }
44 });
45 // 监听端口
46 ChannelFuture sync = bootstrap.bind(9009).sync();
47 Channel udpChannel = sync.channel();
48
49 // String data = "我是大好人啊";
50 // udpChannel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(data.getBytes(Charset.forName("UTF-8"))), new InetSocketAddress("192.168.2.29", 9008)));
51
52 Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
53 @Override
54 public void run() {
55 nioEventLoopGroup.shutdownGracefully();
56 }
57 }));
58 }
59 }
View Code
于Tcp协议的客户端启动程序基本一样。唯一区别就在于,UDP服务器使用的是bind方法,来监听端口
在Netty的Bootstrap类中的注释,发现有如下注释内容:
大意就是:bind()用于UDP, TCP连接使用connect()。
上面的源码监听的是端口9009,那么所有使用UDP协议的数据,发送到端口9009,就会被我们的Netty接收到了。
为了输出方便,博主在上面的代码中增加一个MessageToMessageDecoder将接收到的Datagram,排除其他信息,仅将字符串传递下去。并在UDPHandler中打印出来。
2. 构建UDP客户端
UDP协议来说,其实没有客户端和服务端的区别啦。只是为了贴近TCP协议做的一点文字描述上面的区分。
简单来讲,上面的那段逻辑其实就可以作为UDP客户端来使用。注释掉的那行逻辑其实就是发送“我是大好人啊”这个字符串到ip地址为192.168.2.29的服务端的9008端口。代码如下:
1 package dev.tinyz.game;
2
3 import io.netty.bootstrap.Bootstrap;
4 import io.netty.buffer.Unpooled;
5 import io.netty.channel.*;
6 import io.netty.channel.nio.NioEventLoopGroup;
7 import io.netty.channel.socket.DatagramPacket;
8 import io.netty.channel.socket.nio.NioDatagramChannel;
9 import io.netty.handler.codec.MessageToMessageDecoder;
10
11 import java.net.InetSocketAddress;
12 import java.nio.charset.Charset;
13 import java.util.List;
14
15 /**
16 * @author TinyZ on 2015/6/8.
17 */
18 public class GameMain {
19
20 public static void main(String[] args) throws InterruptedException {
21
22 final NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
23
24 Bootstrap bootstrap = new Bootstrap();
25 bootstrap.channel(NioDatagramChannel.class);
26 bootstrap.group(nioEventLoopGroup);
27 bootstrap.handler(new ChannelInitializer<NioDatagramChannel>() {
28
29 @Override
30 public void channelActive(ChannelHandlerContext ctx) throws Exception {
31 super.channelActive(ctx);
32 }
33
34 @Override
35 protected void initChannel(NioDatagramChannel ch) throws Exception {
36 ChannelPipeline cp = ch.pipeline();
37 cp.addLast("framer", new MessageToMessageDecoder<DatagramPacket>() {
38 @Override
39 protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List<Object> out) throws Exception {
40 out.add(msg.content().toString(Charset.forName("UTF-8")));
41 }
42 }).addLast("handler", new UdpHandler());
43 }
44 });
45 // 监听端口
46 ChannelFuture sync = bootstrap.bind(0).sync();
47 Channel udpChannel = sync.channel();
48
49 String data = "我是大好人啊";
50 udpChannel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(data.getBytes(Charset.forName("UTF-8"))), new InetSocketAddress("192.168.2.29", 9008)));
51
52 Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
53 @Override
54 public void run() {
55 nioEventLoopGroup.shutdownGracefully();
56 }
57 }));
58 }
59 }
View Code
和上面的“服务端”代码最大的差别就是,监听的端口号修改成0.为
使用Netty的Channel发送DatagramPacket。写好目标地址,然后运行起来就可以自己测试一下了。
3. JAVA原生UDP
有朋友这个时候就会问:为什么不是有JAVA原生的UDP呢?
其实很简单。说白了Netty使用的也是Java底层的代码。只是做了一层封装,以便于使用。服务端使用Netty框架构建高性能,高扩展的UDP服务器。
客户端则使用JAVA或者任意其他的语言的API(遵循UDP协议即可)。
下面上一段博主使用的的JAVA
1 package dev.tinyz.game;
2
3 import java.io.IOException;
4 import java.net.DatagramPacket;
5 import java.net.DatagramSocket;
6 import java.net.InetSocketAddress;
7 import java.nio.charset.Charset;
8
9 /**
10 * @author TinyZ on 2015/6/10.
11 */
12 public class UdpTest {
13
14 public static void main(String[] args) throws IOException {
15 final String data = "博主邮箱:zou90512@126.com";
16 byte[] bytes = data.getBytes(Charset.forName("UTF-8"));
17 InetSocketAddress targetHost = new InetSocketAddress("192.168.2.29", 9009);
18
19 // 发送udp内容
20 DatagramSocket socket = new DatagramSocket();
21 socket.send(new DatagramPacket(bytes, 0, bytes.length, targetHost));
22 }
23 }
View Code
..
ps.UDP协议最大特点就是效率高,速度快。用于某些场合可以极大改善系统的性能。
博主在这里引入这个Netty实现UDP的服务端,主要目的。嘻嘻。就是想开源拙作:eyeOfSauron日志系统。