1.一个netty echo服务的例子。client向server发消息,server向client返回同样的消息。程序分两部分,server项目和client项目。这里包含了netty的核心要素。更多的细节请参考前文提到的参考资料即可。
2.server项目
2.1 目录结构
├── build.gradle
├── settings.gradle
└── src
├── main
├── java
├── EchoServerHandler.java
└── Main.java
2.2 build.gradle文件内容
group 'com.brian.demo.netty'
version '1.0-SNAPSHOT'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile group: 'io.netty', name: 'netty-all', version: '4.1.12.Final'
testCompile group: 'junit', name: 'junit', version: '4.12'
}
2.3 Main.java文件内容
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;
//主要参考资料:
// 《netty in action》
// https://github.com/waylau/essential-netty-in-action
public class Main {
private final int port;
public Main(int port) {
this.port = port;
}
public static void main(String args[]) throws Exception {
new Main(8008).start();
}
public void start() throws Exception {
//NioEventLoopGroup是一个线程池,一个线程可以处理多个channel,一个channel只对应一个线程
EventLoopGroup group = new NioEventLoopGroup();
//echo服务handler
final EchoServerHandler echoServerHandler = new EchoServerHandler();
//创建 引导服务器
ServerBootstrap sbs = new ServerBootstrap();
try {
//设置线程池
sbs.group(group)
//指定NIO传输的channel
.channel(NioServerSocketChannel.class)
//本地端口
.localAddress(new InetSocketAddress(port))
//配置handler pipeline。每个channel有一个pipline,
// 包含多个handler,事件从pipline逐个经过handler进行处理。
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(echoServerHandler);
}
});
//绑定服务器,然后同步等待服务器关闭。
ChannelFuture future = sbs.bind().sync();
System.out.println(Main.class.getName() +
" started and listening for connections on " + future.channel().localAddress());
//关闭channel,同步等待
future.channel().closeFuture().sync();
} finally {
//释放线程池
group.shutdownGracefully().sync();
}
}
}
2.4 EchoServerHandler.java文件内容
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
//Sharable注解,声明这个Handler的实例可以被多个channel共享使用
@ChannelHandler.Sharable
//ChannelInboundHandler接口,处理入站事件
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
//每个信息入站都会调用channelRead
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg){
//收到的消息是Object msg,强转撑ByteBuf
ByteBuf inBuf = (ByteBuf)msg;
//在控制台输出接收到的消息
System.out.println("Server received:" + inBuf.toString(CharsetUtil.UTF_8));
//再把消息不做修改重新写给客户端。注意,此时数据还没有flush,仍然在服务端。
ctx.write(inBuf);
}
//通知处理器,当下的channelread()读取消息是本批处理的最后一条消息的时候,调用本函数
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//把所有数据冲刷flush到客户端,关闭通道。至此操作完成。Listern以future方式通知操作完成。
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
//读操作遇到异常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
3. Client项目
3.1 文件目录结构
.
├── build.gradle
├── settings.gradle
└── src
├── main
├── java
├── EchoClientHandler.java
└── Main.java
3.2 build.gradle文件内容
group 'com.brian.demo.netty'
version '1.0-SNAPSHOT'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile group: 'io.netty', name: 'netty-all', version: '4.1.12.Final'
testCompile group: 'junit', name: 'junit', version: '4.12'
}
3.3 Main.java文件内容
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 Main {
private final String host;
private final int port;
public Main(){
this.host="127.0.0.1";
this.port=8008;
}
public void start() throws Exception{
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host,port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture future = b.connect().sync();
future.channel().closeFuture().sync();
}finally {
group.shutdownGracefully();
}
}
public static void main(String args[])throws Exception{
new Main().start();
}
}
3.4 EchoClientHandler.java文件内容
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
System.out.println("client received:" + msg.toString(CharsetUtil.UTF_8));
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("hello,world", CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
4. 如果遇到gradle不能导入nettty包的情况,关闭项目,删除~/.gradle目录所有文件,然后重新在idea做import项目即可。