netty hello world | 张扎瓦的博客

netty hello world

Java NIO 框架netty的简单介绍以及hello world程序示例


Netty简介

  Netty是一款NIO客户端服务器框架,可以快速轻松地开发协议服务器和客户端等网络应用程序。 它极大地简化并简化了TCP和UDP套接字服务器等网络编程。

  ’快速和简单’并不意味着由此产生的应用程序将受到可维护性或性能问题的困扰。 Netty的设计经验非常丰富,包括FTP,SMTP,HTTP以及各种基于二进制和基于文本的传统协议等。 因此,Netty成功地找到了一种方法来实现轻松的开发,性能,稳定性和灵活性,而无需妥协。

  以上内容摘自Netty官网

不建议使用Java原生NIO编程的原因

  1. NIO的类库和API繁杂,使用麻烦,需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。
  2. 需要具备其他额外的技能做铺垫,例如熟悉Java的多线程编程。这是因为NIO涉及到Reactor模式,你必须对多线程和网络编程非常熟悉,才能编写出高质量的NIO程序。
  3. 可靠性能力补齐,工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等问题,NIO编程的特点是功能开发相对容易,但是可靠性能力补齐的工作量和难度都非常大。
  4. JDK NIO的bug,例如臭名昭著的epoll bug,他会导致Selector空轮询,最终导致CPU100%。官方声称在JDK1.6版本的update18修复了该问题,但是直到JDK1.7版本改问题依旧存在,只不过是发生的概率低了一些,并没有得到根本性的解决。

为什么选择Netty

  Netty是业界最流行的NIO框架之一,它的健壮性、功能、性能、可定制性和可扩展性在同类框架中都是首屈一指的,他已经得到成百上千的商用项目验证,例如Haoop的RPC框架Avro就使用了Netty作为底层通信框架,其他还有业界主流的PRC框架,也使用Netty来构建高性能的异步通信能力。

Netty的优点如下:

  1. API使用简单,开发门槛低。
  2. 功能强大,预置了多种编解码功能,支持多种主流协议。
  3. 定制能力强,可以通过ChannelHandler对通信框架进行灵活的扩展。
  4. 性能高,通过与其他业界主流的NIO框架对比,Netty的综合性能最优。
  5. 成熟、稳定,Netty修复了已经发现的所有JDK NIO bug,业务开发人员不需要在位NIO的bug烦恼。
  6. 社区活跃,版本迭代周期短,发现的bug可以被及时修复,同时,更多的新功能会加入。
  7. 经历了大规模的商业应用考验,质量得到验证。Netty在互联网、大数据、网络游戏、企业应用、电信软件等众多行业已经取得了成功商用,证明它已经完全能够满足不同行业的商业应用了。

Netty hello world Demo

服务端

自定义逻辑处理类 HelloServerHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class HelloServerHandler extends SimpleChannelInboundHandler<String> {

/**
* 获取客户端发送的消息
*
* @param channelHandlerContext
* @param s
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
// 收到消息直接打出
System.out.println(channelHandlerContext.channel().remoteAddress()+"说:" + s);

// 向客户端发送消息
channelHandlerContext.writeAndFlush("服务器已收到消息,内容是:" + s + "\n");
}

/**
* 建立连接后调用
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(ctx.channel().remoteAddress()+"连接!");

// 向客户端发送消息
ctx.writeAndFlush("欢迎登录"+InetAddress.getLocalHost().getHostName()+"\n");
}

}

IO事件初始化类 HelloServerInitializer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class HelloServerInitializer extends ChannelInitializer<SocketChannel> {

@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();

// 以("\n")为结尾分割的 解码器
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192,
Delimiters.lineDelimiter()));

// 字符串解码 和 编码
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());

// 自己的逻辑Handler
pipeline.addLast("handler", new HelloServerHandler());
}

}

服务端启动类 Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Server {

/**
* 监听端口
*/
private static final int PORT = 8888;

public static void main(String[] args) {
// 用于接受客户端连接的线程组
EventLoopGroup bossGroup = new NioEventLoopGroup();
// 用于进行SocketChannel读写的线程组
EventLoopGroup workerGroup = new NioEventLoopGroup();

// netty启动类
ServerBootstrap server = new ServerBootstrap();
// 将线程组配置到启动类中
server.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // 创建ServerSocketChannel
.childHandler(new HelloServerInitializer()); // 绑定IO事件处理类

try {
System.out.println("服务端已启动,等待客户端连接...");
// 服务器绑定端口
ChannelFuture future = server.bind(PORT).sync();
// 等待服务端监听端口关闭后,main函数才退出
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放线程组资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}

}

客户端

自定义业务逻辑处理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class HelloClientHandler extends SimpleChannelInboundHandler<String> {

private Thread thread = null;

/**
* 获取服务器发送的消息
*
* @param channelHandlerContext
* @param s
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
System.out.println("服务器说:"+s);
}

/**
* 建立连接后调用
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
if (thread == null) {
thread = new Thread(() -> {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String msg = scanner.next();
ctx.writeAndFlush(msg + "\n");
}
});

thread.start();
}
}

/**
* 与服务器断开连接后调用
*
* @param ctx
* @throws Exception
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端关闭");
super.channelInactive(ctx);
}

}

IO事件初始化类 HelloClientInititlizer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class HelloClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();

/*
* 这个地方的 必须和服务端对应上。否则无法正常解码和编码
*
*/
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192,
Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());

// 客户端的逻辑
pipeline.addLast("handler", new HelloClientHandler());
}
}

客户端启动类Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Client {

/** 服务器IP */
private static final String HOST = "127.0.0.1";
/** 服务器端口号 */
private static final int PORT = 8888;


public static void main(String[] args) {
// 创建NIO线程组
EventLoopGroup group = new NioEventLoopGroup();
// 创建NIO客户端
Bootstrap client = new Bootstrap();
// 将线程组配置到启动类中,并创建SocketChannel,设置处理类
client.group(group).channel(NioSocketChannel.class)
.handler(new HelloClientInitializer());

try {
// 发起异步连接操作
ChannelFuture future = client.connect(HOST, PORT).sync();
// 关闭连接后,主函数退出。
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放线程组资源
group.shutdownGracefully();
}
}
}

分别启动Server 和 Client 后,控制台输出

Server控制台输出
1
2
3
4
服务端已启动,等待客户端连接...
/127.0.0.1:53521连接!
/127.0.0.1:53521说:hello
/127.0.0.1:53521说:world
Client控制台输出
1
2
3
4
服务器说:欢迎登录zhangxu
hello world
服务器说:服务器已收到消息,内容是:hello
服务器说:服务器已收到消息,内容是:world
如果我的文章对您有所帮助,不妨打赏一杯豆浆以资鼓励(○` 3′○)