Server:
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
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.channel.socket.nio.NioSocketChannel;
import java.nio.charset.Charset;
import java.util.Base64;
import java.util.Date;
public class Server {
public static void main(String[] args) throws InterruptedException {
ServerBootstrap serverBootstrap = new ServerBootstrap();
EventLoopGroup boss = new NioEventLoopGroup(1);
EventLoopGroup worker = new NioEventLoopGroup(1);
//1、配置监听线程和处理线程
serverBootstrap.group(boss,worker);
//2、为监听线程设置通道类型(监听线程设置为Nio,工作就自动和它匹配了吗?)
serverBootstrap.channel(NioServerSocketChannel.class);
//3、为监听线程的通道设置TCP属性
serverBootstrap.option(ChannelOption.SO_BACKLOG, 2048);//socket接收队列大小(因为accept()也只能一个一个来,其余的缓冲起来)
serverBootstrap.option(ChannelOption.SO_REUSEADDR, true);//避免端口冲突(允许共用该端口,这个在服务器程序中比较常使用)
serverBootstrap.option(ChannelOption.TCP_NODELAY, true); // 关闭TCP的小流合并,保证消息的及时性(禁止使用Nagle算法)
//4、为工作线程的通道设置TCP属性
serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);//该连接长时间没动静就自动关闭
serverBootstrap.childOption(ChannelOption.SO_LINGER,1);//数据发送完成,连接才能关闭(socket.close()会阻塞一下)
//5、为监听线程添加 处理类
serverBootstrap.handler(new ChannelInitializer<NioServerSocketChannel>() {
@Override
protected void initChannel(NioServerSocketChannel nioServerSocketChannel) throws Exception {
ChannelPipeline pipeline=nioServerSocketChannel.pipeline();
pipeline.addLast();//不需要处理什么吧
}
});
//6、为工作线程添加 处理类
serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel channel) throws Exception {
//获取工作线程 通道 的处理类管道(可以在管道里添加各种处理类,进行数据处理)
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new SimpleInHandlerA());
pipeline.addLast(new SimpleInHandlerB());
pipeline.addLast(new SimpleOutHandlerA());
pipeline.addLast(new SimpleOutHandlerB());
pipeline.addLast(new SimpleOutHandlerC());
pipeline.addLast(new SimpleInHandlerC());
}
});
//7、服务器配置完毕,绑定端口,即可启动。bind后面如果.sync()就同步等待绑定成功或失败,但这里用监听器,可以不阻塞在这。
ChannelFuture future = serverBootstrap.bind(8080).addListener(new ChannelFutureListener() {
public void operationComplete (ChannelFuture channelFuture) throws Exception {
if (channelFuture.isSuccess()) {
System.out.println("端口绑定成功");
Channel channel = channelFuture.channel();//获取到的是ServerSocketChannel
//channel.close();//刚绑定成功就关闭通道,哈哈,客户端就无法连接了。
} else {
System.out.println("端口绑定失败");
}
}
});
//8、监听通道服务通道是否关闭,不会阻塞在这。
future.channel().closeFuture().addListener(new ChannelFutureListener() {
public void operationComplete (ChannelFuture channelFuture) throws Exception {
System.out.println("服务通道已经关闭");
}
});
System.out.println("服务器启动完成,已有两个线程处于轮询中,主线程可以结束,或做其它事情");
}
}
class SimpleInHandlerA extends ChannelInboundHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("A被回调");
super.channelRead(ctx,msg);
}
}
class SimpleInHandlerB extends ChannelInboundHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("B被回调");
ctx.fireChannelRead("不给C原始数据");
}
}
class SimpleInHandlerC extends ChannelInboundHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("C被回调");
System.out.println(msg);
ByteBuf buffer = ctx.alloc().buffer();
byte[] bytes = ("我是服务器").getBytes(Charset.forName("utf-8"));
buffer.writeBytes(bytes);
ctx.writeAndFlush(buffer);
super.channelRead(ctx,msg);
}
}
class SimpleOutHandlerA extends ChannelOutboundHandlerAdapter{
@Override
public void write(ChannelHandlerContext ctx,Object msg,ChannelPromise promise) throws Exception {
System.out.println("outA被回调");
ctx.writeAndFlush(msg);//写给客户端
//super.write(ctx,msg,promise);不能在传播了
}
}
class SimpleOutHandlerB extends ChannelOutboundHandlerAdapter{
@Override
public void write(ChannelHandlerContext ctx,Object msg,ChannelPromise promise) throws Exception {
System.out.println("outB被回调");
super.write(ctx,msg,promise);
}
}
class SimpleOutHandlerC extends ChannelOutboundHandlerAdapter{
@Override
public void write(ChannelHandlerContext ctx,Object msg,ChannelPromise promise) throws Exception {
System.out.println("outC被回调");
super.write(ctx,msg,promise);
}
}
Client:
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.nio.charset.Charset;
import java.util.Date;
public class Client {
public static void main(String[] args) {
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap
.group(workerGroup)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new FirstClientHandler());
}
});
// 4.建立连接
ChannelFuture future=bootstrap.connect("127.0.0.1", 8080).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (channelFuture.isSuccess()) {
System.out.println("连接成功!");
} else {
System.err.println("连接失败!");
}
}
});
future.channel().closeFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
workerGroup.shutdownGracefully();//通道关闭了,线程也就没有用了,也关闭。
}
});
}
}
class FirstClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
//channelActive()方法会在客户端与服务器建立连接后自动调用,(就自动调用一次吗?那我如何各种读写)
System.out.println("客户端发送消息...");
ByteBuf buffer = getByteBuf(ctx);
ctx.channel().writeAndFlush(buffer);
}
private ByteBuf getByteBuf(ChannelHandlerContext ctx) {
// 1. 获取二进制抽象 ByteBuf
ByteBuf buffer = ctx.alloc().buffer();
// 2. 准备数据,指定字符串的字符集为 utf-8
byte[] bytes = ("【客户端】:这是客户端发送的消息:"+new Date()).getBytes(Charset.forName("utf-8"));
// 3. 填充数据到 ByteBuf
buffer.writeBytes(bytes);
return buffer;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//服务端发来消息时自动调用
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println(byteBuf.toString(Charset.forName("utf-8")));
ctx.channel().close();//收到回复我就下线
}
}