注:源代码来自享学课堂,学习之后所做笔记,方便回顾,也给大家一个参考
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;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import java.net.InetSocketAddress;
public class EchoClient {
private final int port;
private final String host;
public EchoClient(int port, String host) {
this.port = port;
this.host = host;
}
//netty的标准程序基本都是下面这个流程
public void start() throws InterruptedException {
/*线程组*/
EventLoopGroup group = new NioEventLoopGroup();
try {
/*客户端启动必备,服务端使用ServerBootstrap*/
Bootstrap b = new Bootstrap();
b.group(group)
/*指明使用NIO进行网络通讯*/
.channel(NioSocketChannel.class)
/*配置远程服务器的地址,服务器端就是配置LocalAdderss*/
.remoteAddress(new InetSocketAddress(host, port))
//这里配置我们自定义的handler,netty主要就是写各种各样的handler
.handler(new ChannelInitializer() {
//解决粘包和半包问题
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//以下三种任意使用一种
//1、通过添加DelimiterBasedFrameDecoder使用自定义字符串分隔符
socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.copiedBuffer("---".getBytes())));
//2、消息定长,把消息的长度作为固定长度
//socketChannel.pipeline().addLast(new FixedLengthFrameDecoder(EchoClientHandler.MSG.length()));
//3、加分割符:系统回车符
/**
* 这个需要在EchoClientHandler每次发送消息,在消息上加上换行,代码如下
* @Override
* public void channelActive(ChannelHandlerContext ctx) throws Exception {
* ByteBuf msg = null;
* String request = "Hello,Netty + System.getProperty("line.separator");
* for(int i=0;i<100;i++){
* msg = Unpooled.buffer(request.length());
* msg.writeBytes(request.getBytes());
* ctx.writeAndFlush(msg);
* }
* }
*/
//socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024*100000));
socketChannel.pipeline().addLast(new EchoClientHandler());
}
});
/*连接到远程节点,阻塞等待直到连接完成*/
ChannelFuture f = b.connect().sync();
/*阻塞,直到channel关闭*/
f.channel().closeFuture().sync();
} finally {
//阻塞关闭线程组
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws InterruptedException {
new EchoClient(8080, "127.0.0.1").start();
}
}
在channelActive建立连接之后,循环100次数据,本来应该是server接受100次的,但是看最后的结果只接受了两次
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;
//SimpleChannelInboundHandler帮我们封装好了很多东西,客户端和服务端都是使用ByteBuf通信的
public class EchoClientHandler extends SimpleChannelInboundHandler {
/*客户端读取到服务器发送过来的数据了,在这里做处理*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg)
throws Exception {
System.out.println("Client accept:" + msg.toString(CharsetUtil.UTF_8));
}
/*客户端被通知channel活跃以后,做事*/
//这里循环发送数据
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for (int i = 0; i < 100; i++) {
ctx.writeAndFlush(Unpooled.copiedBuffer("Hello,Netty ",
CharsetUtil.UTF_8));
}
}
//客户端读取数据完毕之后做事
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelReadComplete");
}
//发生异常的处理
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}
4、Server端主函数
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
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.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import java.net.InetSocketAddress;
public class EchoServer {
private Integer port;
public EchoServer(Integer port) {
this.port = port;
}
public void start() {
EventLoopGroup group = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
try {
bootstrap.group(group)
.localAddress(new InetSocketAddress(port))
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(Channel channel) throws Exception {
//以下三种任意使用一种
//1、通过添加DelimiterBasedFrameDecoder使用自定义字符串分隔符
channel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.copiedBuffer("---".getBytes())));
//2、消息定长,把消息的长度作为固定长度
//channel.pipeline().addLast(new FixedLengthFrameDecoder(EchoClientHandler.MSG.length()));
//3、加分割符:系统回车符
/**
* 这个需要在EchoClientHandler每次发送消息,在消息上加上换行,代码如下
* @Override
* public void channelActive(ChannelHandlerContext ctx) throws Exception {
* ByteBuf msg = null;
* String request = "Hello,Netty + System.getProperty("line.separator");
* for(int i=0;i<100;i++){
* msg = Unpooled.buffer(request.length());
* msg.writeBytes(request.getBytes());
* ctx.writeAndFlush(msg);
* }
* }
*/
//channel.pipeline().addLast(new LineBasedFrameDecoder(1024*100000));
channel.pipeline().addLast(new EchoServerHandler());
}
});
ChannelFuture future = bootstrap.bind().sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
try {
group.shutdownGracefully().sync();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
public static void main(String[] args) {
new EchoServer(8089).start();
}
}
5、server端的Handler
counter.incrementAndGet()用来计算客户端发送数据的次数,客户端循环了100次,但是服务端只接受了
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;
import java.util.concurrent.atomic.AtomicInteger;
/*指明我这个handler可以在多个channel之间共享,意味这个实现必须线程安全的。*/
@ChannelHandler.Sharable//如果不加这个注解,主函数在.childHandler(new EchoServerHandler()),确保每个链接都单独的handler
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
private AtomicInteger counter = new AtomicInteger();
/*** 服务端读取到网络数据后的处理*/
//计算接受数据次数,按道理客户端发送了100次,也应该接受100次,结果只有两次
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
ByteBuf in = (ByteBuf) msg;/*netty实现的缓冲区*/
System.out.println("Server Accept:" + in.toString(CharsetUtil.UTF_8) + counter.incrementAndGet());
ctx.write(in);
}
/*** 服务端读取完成网络数据后的处理*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx)
throws Exception {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)/*flush掉所有的数据*/
.addListener(ChannelFutureListener.CLOSE);/*当flush完成后,关闭连接*/
}
/*** 发生异常后的处理*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}
6、运行结果
server
服务器即将启动
Server Accept[hello server,I`m client:1] and the counter is:1
Server Accept[hello server,I`m client:2] and the counter is:2
Server Accept[hello server,I`m client:3] and the counter is:3
Server Accept[hello server,I`m client:4] and the counter is:4
Server Accept[hello server,I`m client:5] and the counter is:5
Server Accept[hello server,I`m client:6] and the counter is:6
Server Accept[hello server,I`m client:7] and the counter is:7
Server Accept[hello server,I`m client:8] and the counter is:8
Server Accept[hello server,I`m client:9] and the counter is:9
Server Accept[hello server,I`m client:10] and the counter is:10
这里能够正确输出10次,代表粘包和半包解决
client
Hello,hello server,I`m client:1. Welcome to Netty World!
Hello,hello server,I`m client:2. Welcome to Netty World!
Hello,hello server,I`m client:3. Welcome to Netty World!
Hello,hello server,I`m client:4. Welcome to Netty World!
Hello,hello server,I`m client:5. Welcome to Netty World!
Hello,hello server,I`m client:6. Welcome to Netty World!
Hello,hello server,I`m client:7. Welcome to Netty World!
Hello,hello server,I`m client:8. Welcome to Netty World!
Hello,hello server,I`m client:9. Welcome to Netty World!
Hello,hello server,I`m client:10. Welcome to Netty World!
下一篇介绍使用netty编写自己的服务器