客户端代码:
public class Server {
private int port;
public Server(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // 创建线程组监听端口
EventLoopGroup workerGroup = new NioEventLoopGroup();//创建线程组来处理客户端发来的消息
try {
ServerBootstrap b = new ServerBootstrap(); // 是一个帮助类 用来开启服务
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
//每次有新的连接进来 都会创建新的ChannelPipeline 所以ServerHandler 不存在线程问题
System.out.println("初始化了");
ChannelPipeline pipeline = ch.pipeline();
//加心跳机制
pipeline.addLast("ping", new IdleStateHandler(3, 0, 0,TimeUnit.SECONDS));
//加解码器
pipeline.addLast("decode",new DecodeMessgae());
//加处理器
pipeline.addLast("handler",new ServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync(); // (7) //同步
// Wait until the server socket is closed.
// In this example, this does not happen, but you can do that to gracefully
// shut down your server.
f.channel().closeFuture().sync();//同步 如果不关闭,线程会一直阻塞
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 2000;
}
new Server(port).run();
}
}
解码器:
public class DecodeMessgae extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
int length = in.readableBytes();
//如果不输出 那么会一直调用decode方法 继续去读 直到 直到输出结果才会把缓冲的数据丢弃到并且会自动释放ByteBuf内存
if(length < 6){
System.out.println("当前length:"+length+"b不够");
return ;
}else{
byte[] body = new byte[in.readableBytes()];
in.readBytes(body);
String s = new String(body);
out.add(body);
}
}
}
处理消息:
public class ServerHandler extends ChannelInboundHandlerAdapter {
private int counter = 0;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(this.hashCode() + "有客户端连接");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try{
System.out.println("开始读取进来了");
}finally {
//ReferenceCountUtil.release(msg); 如果不用解码器 一定要手动释放 写数据是 netty会帮我们释放ByteBuf
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("--- Client is inactive ---");
}
//根据心跳机制前面设置的时间 会执行下面的代码
//IdleState.READER_IDLE 读空闲
//IdleState.WRITER_IDLE 写空闲
//IdleState.ALL_IDLE 读写空闲
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
super.userEventTriggered(ctx, evt);
if (evt instanceof IdleStateEvent) {
counter++;
}
if(counter > 4){
ctx.channel().close();
System.out.println("关闭当前连接");
}
}
/**
* 处理心跳包
*
* @param ctx
* @param packet
*/
private void handleHeartbreat(ChannelHandlerContext ctx, Object msg) {
// 将心跳丢失计数器置为0
counter = 0;
System.out.println("收到心跳包");
}
/**
* 处理数据包
*
* @param ctx
* @param packet
*/
private void handleData(ChannelHandlerContext ctx, Object msg) {
// 将心跳丢失计数器置为0
counter = 0;
}
}
@Override
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
System.out.println("有新的客户端连接");
final ByteBuf time = ctx.alloc().buffer(4);
time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));
final ChannelFuture f = ctx.writeAndFlush(time);
System.out.println("状态1:"+f.isDone());
//当ChannelFuture完成后 才执行 是异步执行的 如果ChannelFuture没完成 这个监听事件的代码
//不会执行
f.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
// TODO Auto-generated method stub
System.out.println("状态2:"+future.isDone());
System.out.println("关闭客户端");
assert f == future;
ctx.close();
}
});
}
解决粘包和拆包的解码器代码:
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
public class DecodeMessgae extends ByteToMessageDecoder {
//数据格式 前缀(4) + 长度 (4)+数据
private int baseLength = 4 + 4;
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
int length = in.readableBytes();
//如果不输出 那么会一直调用decode方法 继续去读 直到 直到输出结果才会把缓冲的数据丢弃到并且会自动释放ByteBuf内存
if(length < baseLength){
return ;
}
//如果出现大数据直接过滤掉 根据需要填写字段
if(length > 30){
in.skipBytes(length);
return;
}
//读取包头位 只有包头正确 继续读取数据
int beginReader = 0;
while(true){
beginReader = in.readerIndex();
in.markReaderIndex();
int pre = in.readInt();
System.out.println(pre);
if(pre == 2){
break;
}
in.resetReaderIndex();
//每次只前进一位
in.readByte();
//读取之后长度变的不满足
if(in.readableBytes() < baseLength){
return ;
}
}
//读取数据大小
int dataLength = in.readInt();
if(in.readableBytes() < dataLength){
//还原读指针
in.readerIndex(beginReader);
return ;
}
byte[] bs = new byte[dataLength];
in.readBytes(bs);
String s = new String(bs);
out.add(s);
}
}
测试代码:
public class Client2 {
public static void main(String[] args) throws Exception {
Socket s = new Socket("127.0.0.1", 2000);
Scanner in = new Scanner(System.in);
while (in.hasNext()) {
String data = in.nextLine();
ByteBuffer byteBuffer = ByteBuffer.allocate(4 + 4 + 4 + data.length());
byteBuffer.putInt(55);
byteBuffer.putInt(2);
byteBuffer.putInt(data.length());
byteBuffer.put(data.getBytes());
byte[] array = byteBuffer.array();
s.getOutputStream().write(array);
s.getOutputStream().flush();
}
in.close();
s.close();
}
}