首先要引入netty的包,可以在http://netty.io/downloads.html 选择自己需要的版本下载,然后引入它里面的all的jar包。
如果你的项目使用了maven,则可以直接用下面代码引入
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.14.Final</version>
</dependency>
接下来我们先写服务端,新建一个Server类
package netty.test;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class Server {
public static void main(String[] args) throws Exception {
//1 第一个线程组 是用于接收Client端连接的
EventLoopGroup bossGroup = new NioEventLoopGroup();
//2 第二个线程组 是用于实际的业务处理操作的
EventLoopGroup workerGroup = new NioEventLoopGroup();
//3 创建一个辅助类Bootstrap,就是对我们的Server进行一系列的配置
ServerBootstrap b = new ServerBootstrap();
//把俩个工作线程组加入进来
b.group(bossGroup, workerGroup)
//我要指定使用NioServerSocketChannel这种类型的通道
.channel(NioServerSocketChannel.class)
//一定要使用 childHandler 去绑定具体的 事件处理器
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(new ServerHandler()); //ServerHandler为我们自定义的事件处理类
}
})
.option(ChannelOption.SO_SNDBUF, 50*1024) //设置发送缓冲大小
.option(ChannelOption.SO_RCVBUF,50*1024); //是指接受缓冲大小
//绑定指定的端口 进行监听
ChannelFuture f = b.bind(8765).sync();
//Thread.sleep(1000000);
f.channel().closeFuture().sync(); //没有这句话则不会在此阻塞等待客户端的连接,而是直接执行后面代码
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
其中涉及知识点:(此处出于http://ifeve.com/netty5-user-guide/)
- NioEventLoopGroup 是用来处理I/O操作的多线程事件循环器,Netty提供了许多不同的EventLoopGroup的实现用来处理不同传输协议。在这个例子中我们实现了一个服务端的应用,因此会有2个NioEventLoopGroup会被使用。第一个经常被叫做‘boss’,用来接收进来的连接。第二个经常被叫做‘worker’,用来处理已经被接收的连接,一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上。如何知道多少个线程已经被使用,如何映射到已经创建的Channels上都需要依赖于EventLoopGroup的实现,并且可以通过构造函数来配置他们的关系。
- ServerBootstrap 是一个启动NIO服务的辅助启动类。你可以在这个服务中直接使用Channel,但是这会是一个复杂的处理过程,在很多情况下你并不需要这样做。
- 这里我们指定使用NioServerSocketChannel类来举例说明一个新的Channel如何接收进来的连接。
- 这里的事件处理类经常会被用来处理一个最近的已经接收的Channel。ChannelInitializer是一个特殊的处理类,他的目的是帮助使用者配置一个新的Channel。也许你想通过增加一些处理类比如DiscardServerHandle来配置一个新的Channel或者其对应的ChannelPipeline来实现你的网络程序。当你的程序变的复杂时,可能你会增加更多的处理类到pipline上,然后提取这些匿名类到最顶层的类上。
- 你可以设置这里指定的通道实现的配置参数。我们正在写一个TCP/IP的服务端,因此我们被允许设置socket的参数选项比如tcpNoDelay和keepAlive。请参考ChannelOption和详细的ChannelConfig实现的接口文档以此可以对ChannelOptions的有一个大概的认识。
- 你关注过option()和childOption()吗?option()是提供给NioServerSocketChannel用来接收进来的连接。childOption()是提供给由父管道ServerChannel接收到的连接,在这个例子中也是NioServerSocketChannel。
- 我们继续,剩下的就是绑定端口然后启动服务。这里我们在机器上绑定了机器所有网卡上的8080端口。当然现在你可以多次调用bind()方法(基于不同绑定地址)。
接着新建一个类ServerHandler,它实现了ChannelInboundHandlerAdapter并重写了channelRead跟exceptionCaught两个方法。其中channelRead用来监听客户端发送的消息
package netty.test;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
byte[] data = new byte[buf.readableBytes()];
buf.readBytes(data);
String request = new String(data, "utf-8");
System.out.println("Server: " + request);
//写给客户端
String response = "我是反馈的信息:888";
//有write操作则可以不释放msg
ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));//加上.addListener(ChannelFutureListener.CLOSE);则表示写回数据后断开客户端的链接
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
至此服务端已经完成,接下来写一下客户端代码。首先新建一个Client类
package netty.test;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
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;
public class Client {
public static void main(String[] args) throws Exception {
EventLoopGroup workgroup = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(workgroup)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(new ClientHandler());
}
});
ChannelFuture cf1 = b.connect("127.0.0.1", 8765).sync();
//buf
cf1.channel().writeAndFlush(Unpooled.copiedBuffer("我是客户端信息:777".getBytes()));
cf1.channel().closeFuture().sync();
workgroup.shutdownGracefully();
}
}
接着新建一个新建类ClientHandler类,跟ServerHandle相同功能,用来监听处理服务端发来的信息
package netty.test;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
public class ClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
//do something msg
ByteBuf buf = (ByteBuf)msg;
byte[] data = new byte[buf.readableBytes()];
buf.readBytes(data);
String request = new String(data, "utf-8");
System.out.println("Client: " + request);
Thread.sleep(1000);
ctx.writeAndFlush(Unpooled.copiedBuffer("客户端又来咯~".getBytes()));
} finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}