本文主要是刚学netty,搭建一个简单的服务器和客户端的入门demo。参考书籍《Netty in action》
1:开发工具为Idea,使用maven多模块依赖配置。以nettyParent为父parent project,然后在该工程下创建client和server两个module。
项目目录结构和parent中的maven配置如下:
这个项目中有两个模块,Server与Client,它们的依赖库相同,互相之间没有依赖关系
netty-parent
netty
1.0-SNAPSHOT
4.0.0
client
jar
client
org.codehaus.mojo
exec-maven-plugin
run-client
java
client.EchoClient
${echo-server.hostname}
${echo-server.port}
2.1:EchoClientHandler:客户端逻辑处理,其实就是处理返回到客户端的数据。
package client;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
/**
*客户端逻辑
*/
public class EchoClientHandler extends SimpleChannelInboundHandler {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//当前通知Channel是活跃的时候,发送一条信息
ctx.writeAndFlush(Unpooled.copiedBuffer("netty rocks!",CharsetUtil.UTF_8));
}
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
//记录已接受消息的转储
System.out.println("Client received :" +
byteBuf.toString(CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
2.2:EchoClient:客户端引导,启动客户端,发起连接。
package client;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;
/**
* 客户端引导
*/
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host,int port){
this.port = port;
this.host = host;
}
public void start() throws Exception{
EventLoopGroup group = new NioEventLoopGroup();
try{
//创建BootStrap
Bootstrap b = new Bootstrap();
b.group(group) //指定EvenLoopGroup 以处理客户端事件;需要适用于Nio的实现
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host,port))
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel socketChannel) {
socketChannel.pipeline().addLast(
new EchoClientHandler()
);
}
});
ChannelFuture f = b.connect().sync();
f.channel().closeFuture().sync();
}finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception{
if(args.length != 2){
System.out.println("Usage: "+EchoClient.class.getSimpleName()+
"");
}
}
}
2.3:client中的maven配置
netty-parent
netty
1.0-SNAPSHOT
4.0.0
client
jar
client
org.codehaus.mojo
exec-maven-plugin
run-client
java
client.EchoClient
${echo-server.hostname}
${echo-server.port}
3.1:EchoServerHandler:服务端业务逻辑处理。
package server;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
//业务逻辑处理
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
//每个信息入站都会调用
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
//将消息记录到控制台
System.out.println("server received:" + in.toString(CharsetUtil.UTF_8));
ctx.write(in); //将接受到的消息写给发送者
}
//通知处理器最后的channelread是当前批处理中的最后一条信息调用
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//将未决消息冲刷到远程节点,并关闭该Channel
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace(); //打印异常栈追踪
ctx.close(); //关闭该channel
}
}
3.2:EchoServer:服务端引导,启动服务器,绑定到du端口进行监听。
package server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import java.net.InetSocketAddress;
/**
* 引导服务器
* 绑定到服务器,将在其上监听并接受传入连接请求的端口
*/
public class EchoServer {
private final int port;
public EchoServer(int port){
this.port = port;
}
public static void main(String[] args) throws Exception{
if(args.length != 1){
System.err.println("Usage: "+ EchoServer.class.getSimpleName()+"");
return;
}
int port = Integer.parseInt(args[0]);
new EchoServer(port).start();//调用服务器的start方法
}
public void start() throws Exception{
final EchoServerHandler serverHandler = new EchoServerHandler();
//创建Even Loop Group
//配置服务器的NIO线程组
//两个Reactor 一个用于服务器接收客户端的连接 一个用于经行SocketChannel的网络读写
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try{
//创建ServerBootStrap
ServerBootstrap b = new ServerBootstrap();
//指定所使用的NIO传输Channle
b.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel socketChannel) {
//如果EchoServerHandler被注为@Shareable的时候,则可以总是使用同样的实例
socketChannel.pipeline().addLast(serverHandler);
}
});
//异步的绑定服务器,调用sync方法阻塞,直到绑定完成
ChannelFuture f = b.bind().sync();
//获取Channel的CloseFuture,并阻塞当前线程直到它完成
f.channel().closeFuture().sync();
}finally {
//关闭EvenLoopGroup,释放所有资源
bossGroup.shutdownGracefully().sync();
workerGroup.shutdownGracefully().sync();
}
}
}
3.3:Server中的Maven配置。
netty-parent
netty
1.0-SNAPSHOT
4.0.0
server
jar
server
org.codehaus.mojo
exec-maven-plugin
run-server
java
server.EchoServer
${echo-server.port}
4.1:编译项目,首先要配置好Maven环境变量,进入nettyParent目录下
输入 mvn clean package命令对项目进行编译
4.2:然后进入server目录,输入mvn exec:java 启动服务器 (因为parent中的Maven配置好了,所以这样启动即可)。
4.3:同样进入client目录下,输入mvn exec:java 启动客户端。
这样一个简单的netty服务器和客户端就搭建好了,本文参考了《Netty In Action》,
[Netty 源码分析](https://blog.csdn.net/tryingpfq/article/details/104692919)
Demo的GitHUb地址