今天只是聊天室实现的初步讲解,并不会真正的实现聊天室功能,具体的聊天室功能将会在下一个文章出现
3.Netty 客户端与服务端之间消息的传递 Hello World
3.1 服务端具体的代码实现
我一般来说会将代码的意思放在注释里面,可以将代码克隆下来 然后看注释即可
package com.demo.netty.secondexample;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.UUID;
/**
* @author 自定义头信息 处理器
*/
public class MyServerHandler extends SimpleChannelInboundHandler<String> {
/**
*
* @param ctx 上下文 核心
* @param msg 请求对象 客户端发送过来的
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
Thread.sleep(2000);
//获取客户端地址 打印 客户端发送的请求带上地址
System.out.println(ctx.channel().remoteAddress() + ", " + msg);
//将客户端发送的消息加上UUID重新发给客户端
ctx.channel().writeAndFlush("你的请求我收到了"+ UUID.randomUUID().toString()+"消息为:"+msg);
}
/**
* 出现异常会调用这个
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
初始化器代码编写
package com.demo.netty.secondexample;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
/**
* 客户端和服务端发送消息 添加消息处理消息头
* @author huangfu
*/
public class MyServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
/**
* 他是基于长度场的帧解码器 以后会做名词讲解
* maxFrameLength:单个包最大的长度,这个值根据实际场景而定。
* lengthFieldOffset:表示数据长度字段开始的偏移量
* lengthFieldLength:数据长度字段的所占的字节数
* lengthAdjustment:lengthAdjustment + 数据长度取值 = 数据长度字段之后剩下包的字节数
* initialBytesToStrip:表示从整个包第一个字节开始,向后忽略的字节数
*/
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
//长度字段预编程器
pipeline.addLast(new LengthFieldPrepender(4));
//解码器
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
//编码器
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
//自定义处理器
pipeline.addLast(new MyServerHandler());
}
}
服务端启动类
package com.demo.netty.secondexample;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LoggingHandler;
/**
* 自定义服务端
* @author huangfu
*/
public class MyServer {
public static void main(String[] args) {
//构造接收请求线程组和处理请求线程组
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try{
//创建启动入口
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
//处理被委托线程组
.childHandler(new MyServerInitializer());
//绑定端口
ChannelFuture sync = bootstrap.bind(8989).sync();
//关闭资源
sync.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭线程组
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
3.2客户端代码编写
客户端 自定义处理器实现
package com.demo.netty.secondexample.client;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.time.LocalDateTime;
import java.util.UUID;
/**
* @author 自定义头信息 处理器
*/
public class MyClientHandler extends SimpleChannelInboundHandler<String> {
/**
*
* @param ctx 上下文 核心
* @param msg 请求对象 客户端发送过来的
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
Thread.sleep(2000);
//获取客户端地址
System.out.println(ctx.channel().remoteAddress() + ", " + msg);
//向服务端发送信息
ctx.writeAndFlush("向你发送:"+ LocalDateTime.now());
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush("来自客户端的问候");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
客户端初始化器实现
package com.demo.netty.secondexample.client;
import com.demo.netty.secondexample.MyServerHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
/**
* 客户端和服务端发送消息
* @author huangfu
*/
public class MyClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(new MyClientHandler());
}
}
客户端 启动类实现
package com.demo.netty.secondexample.client;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
* 自定义客户端
* @author huangfu
*/
public class MyClient {
public static void main(String[] args) {
EventLoopGroup loopGroup = new NioEventLoopGroup();
try{
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(loopGroup)
.channel(NioSocketChannel.class)
//客户端使用handler 他是针对一个Group
.handler(new MyClientInitializer());
//绑定客户端端口
ChannelFuture sync = bootstrap.connect("127.0.0.1",8989).sync();
sync.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
loopGroup.shutdownGracefully();
}
}
}