Netty快速入门系列(二)-HelloWorld
Netty快速入门系列(二)-HelloWorld
在了解一些核心概念前,先直接动手实践一个 HelloWorld 例子。HelloWorld 例子非常的简单,就是一个服务端跟客户端之间互相发送一句 【HelloWorld 】。
1. 环境准备
- JDK8
- Netty 4
- Maven
2. 编写服务端程序
服务端的大概逻辑如下:
- 创建 bossGroup 来负责接受新的连接。
- 创建 workerGroup 来处理已建立好的连接后续的 I/O 操作。
- 创建 ServerBootstrap 实例,指定 channel 类型。
- 创建 ChannelPipeline ,然后添加协议报文的编码、解码、自定义的业务逻辑处理器。
- 调用 bind 方法绑定服务到计算机的指定端口,启动服务。
package com.suny.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class NettyServer {
// 服务端暴露的端口号
private static final int PORT = 8080;
public static void main(String[] args) throws Exception {
// bossGroup 主要是负责接受客户端的连接
final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// workerGroup 主要是负责处理连接的后续IO操作
final EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
final ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
// StringDecoder 将接收到的字节流为字符串
p.addLast(new StringDecoder());
// StringEncoder 将字符串为字节流以发送
p.addLast(new StringEncoder());
// 添加自定义的 ServerHandler 来处理后续的业务逻辑
p.addLast(new ServerHandler());
}
});
// 绑定服务到指定的端口并启动
final ChannelFuture f = b.bind(PORT).sync();
System.out.println("Netty服务暴露在端口 " + PORT);
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
private static class ServerHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
// 处理从客户端发送的消息,打印。 然后将其发送回客户端
System.out.println("服务端收到客户端的消息: " + msg);
ctx.writeAndFlush(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 处理异常情况
cause.printStackTrace();
ctx.close();
}
}
}
3. 编写客户端程序
客户端的逻辑跟服务端的基本差不多,只是客户端只有一个 NioEventLoopGroup 去处理 I/O ,不像服务端使用了两个 NioEventLoopGroup 去分别处理连接跟 I/O。
大致逻辑如下:
- 创建 EventLoopGroup 负责处理所有的I/O操作,包括创建连接和数据传输。
- 创建 Bootstrap 实例,这是 Netty 客户端的启动类实例。同时指定 channel 类型。
- 创建 ChannelPipeline ,然后添加协议报文的编码、解码、自定义的业务逻辑处理器。
- 调用 Bootstrap 的 connect 方法连接到服务端的 IP 和端口。
package com.suny.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class NettyClient {
// 定义了服务端的 IP 和端口
private static final String HOST = "localhost";
private static final int PORT = 8080;
public static void main(String[] args) throws Exception {
// 用于处理I/O操作
final EventLoopGroup group = new NioEventLoopGroup();
try {
// Bootstrap 是 Netty客户端的启动类
final Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
// StringDecoder 将接收到的字节流为字符串
p.addLast(new StringDecoder());
// StringEncoder用于编码字符串为字节流以发送
p.addLast(new StringEncoder());
// 添加自定义的 ClientHandler 来处理后续的业务逻辑
p.addLast(new ClientHandler());
}
});
// 连接到服务器 localhost:8080
final ChannelFuture f = b.connect(HOST, PORT).sync();
// 获取 channel 并向服务端发送消息 HelloWorld, Netty!
final Channel channel = f.channel();
channel.writeAndFlush("HelloWorld, Netty!");
channel.closeFuture().sync();
} finally {
group.shutdownGracefully();
}
System.out.println("Netty 客户端生命周期结束.");
}
private static class ClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
// 处理从服务端接收到的消息,并打印出来
System.out.println("客户端收到服务端的消息: " + msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
}
4. 运行示例程序
先运行服务器端的程序 , 再运行客户端的程序。
- 运行服务端的
NettyServer
,可以看到控制台输出:
Netty服务暴露在端口 8080
- 运行客户端的
NettyClient
,可以看到控制台输出:
客户端收到服务端的消息: HelloWorld, Netty!
- 同时可以观察到服务端输出:
服务端收到客户端的消息: HelloWorld, Netty!
这样就完成了一个简单的 Netty 服务端跟客户端的 HelloWorld ,可以看到的是代码量并不多,都是一些模版代码。再复杂的程序基本也就是在这个模版代码的基础上进行完善。