1.创建服务器端:
public class ServerMain {
public static void run() {
EventLoopGroup pGroup = new NioEventLoopGroup(); //线程组:用来处理网络事件处理(接受客户端连接)
EventLoopGroup cGroup = new NioEventLoopGroup(); //线程组:用来进行网络通讯读写
try {
ServerBootstrap b = new ServerBootstrap();
b.group(pGroup, cGroup)
//注册服务端channel
.channel(NioServerSocketChannel.class)
//队列形式,阻塞式处理队列里的客户端请求
// .option(ChannelOption.SO_BACKLOG, 1024)
//长连接,每两小时检测心跳
.option(ChannelOption.SO_KEEPALIVE, true)
// .handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline()
//添加解码ByteBuf为字符串
.addLast(new StringDecoder())
//编码为字符串,这样发送消息直接使用String即可
//.addLast(new StringEncoder())
.addLast(new ServerHandlerOne()); //服务端业务处理类
}
});
ChannelFuture cf = null;
cf = b.bind(8080).sync();
cf.channel().closeFuture().sync();
} catch (Exception e) {
pGroup.shutdownGracefully();
cGroup.shutdownGracefully();
e.printStackTrace();
}
}
public static void main(String[] args) {
new Thread(
() -> {
run();
}
).start();
}
}
服务端处理类:
public class ServerHandlerOne extends SimpleChannelInboundHandler<String> {
private static int count = 0;
//SimpleChannelInboundHandler带泛型的抽象处理类
protected void channelRead0(ChannelHandlerContext ctx, String o) throws Exception {
//避免粘包
String[] split = o.split("&");
Arrays.stream(split).forEach((a) -> {
System.out.println(a);
});
//只读的话需要手动释放ByteBuf计数器.
ReferenceCountUtil.release(o);
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端注册" + count++);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("读取客户端信息完成" + count++);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端与服务器连接成功");
super.channelActive(ctx);
ByteBuf b = Unpooled.copiedBuffer("我是服务器".getBytes());
ctx.writeAndFlush(b);
}
}
2.客户端;
public class ClientMain {
public static void run() {
EventLoopGroup cGroup = new NioEventLoopGroup(); //线程组:用来进行网络通讯读写
try {
Bootstrap b = new Bootstrap();
b.group(cGroup)
.channel(NioSocketChannel.class) //注册服务端channel
.option(ChannelOption.SO_KEEPALIVE, true)
// .handler(new LoggingHandler(LogLevel.INFO))
.handler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel sc) throws Exception {
//5s没有交互,就会关闭channel
sc.pipeline()
.addLast(new StringDecoder())
.addLast(new ClientHandler())
.addLast(new ClientHandler2());
}
});
ChannelFuture localhost = b.connect("localhost", 8080);
// localhost.addListener(ChannelFutureListener.CLOSE);
localhost.channel().closeFuture().sync();
System.out.println("aaaa");
} catch (Exception e) {
cGroup.shutdownGracefully();
e.printStackTrace();
}
}
public static void main(String[] args) {
run();
}
}
两个客户端处理类:
public class ClientHandler extends SimpleChannelInboundHandler<String> {
protected void channelRead0(ChannelHandlerContext ctx, String) throws Exception {
System.out.println(o);
//此处使该方法中管道和信息往下传播到下一个处理类
ctx.fireChannelRead(o);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(1);
//此处使该方法中管道和信息往下传播到下一个处理类
ctx.fireChannelActive();
new Thread(() -> {
String s = "客户端&";
int i = 0;
do {
ByteBuf b = Unpooled.buffer(s.length());
try {
b.writeBytes(s.getBytes("utf-8"));
//注释掉间隔时间,则服务器产生粘包现象
// Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
ctx.writeAndFlush(b);
i++;
} while (i != 10);
}).start();
}
}
public class ClientHandler2 extends SimpleChannelInboundHandler {
protected void channelRead0(ChannelHandlerContext ctx, Object o) throws Exception {
System.out.println(o.toString() + 2);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(2);
}
}
注: 短连接则每次客户端发送完信息,得到服务器响应后即可关闭管道资源;长连接则可以做心跳机制检测连接情况;