Netty项目使用经验总结(一)

本文介绍了Netty的基本使用,包括创建Netty服务器和客户端的简单实例,并详细展示了如何添加自定义消息解码器以处理不同格式的消息。在服务器端,通过NioEventLoopGroup和NioServerSocketChannel创建服务器,客户端则通过Bootstrap和NioSocketChannel建立连接。自定义解码器MyCustomMessageDecoder用于解析带有消息头的网络数据,以确保正确解析消息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1.Netty简单实例

(1)Netty服务器

(2)Netty客户端

 2.添加自定义的消息解码器


1.Netty简单实例

关于入门的觉得的这个博主的文章写的就很好,简洁简单,主要是关于Netty的关键类的讲解:

Netty基本使用(一)_cfy137000的博客-CSDN博客_netty基本使用

(1)Netty服务器

import io.netty.bootstrap.ServerBootstrap;

import io.netty.channel.ChannelHandlerContext;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.SimpleChannelInboundHandler;

import io.netty.channel.nio.NioEventLoopGroup;

import io.netty.channel.socket.nio.NioServerSocketChannel;

import io.netty.channel.socket.nio.NioSocketChannel;

import io.netty.handler.codec.string.StringDecoder;



public class NettyServer

{

public static void main(String[]args)

{

System.out.println("启动服务器端口8082");

new ServerBootstrap()

.group(new NioEventLoopGroup())

.channel(NioServerSocketChannel.class)

.childHandler(new ChannelInitializer<NioSocketChannel>() {

protected void initChannel(NioSocketChannel ch) {

ch.pipeline().addLast(new StringDecoder());

ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {

@Override

protected void channelRead0(ChannelHandlerContext ctx, String msg) {

System.out.println("receive client:"+msg);

}

});

}

})

.bind(8082);

}

}

(2)Netty客户端

import io.netty.bootstrap.Bootstrap;

import io.netty.channel.Channel;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.nio.NioEventLoopGroup;

import io.netty.channel.socket.nio.NioSocketChannel;

import io.netty.handler.codec.string.StringEncoder;



public class NettyClient {

public static void main(String[] args) {

try {

new Bootstrap()

.group(new NioEventLoopGroup())

.channel(NioSocketChannel.class)

.handler(new ChannelInitializer<Channel>() {//

@Override

protected void initChannel(Channel ch) {

ch.pipeline().addLast(new StringEncoder());

}

})

.connect("127.0.0.1", 8082)

.sync()

.channel()

.writeAndFlush("hello Chenfy!");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

启动测试,先启动服务器,然后启动客户端,可以看到控制台log,这个就是最简单的实例

 2.添加自定义的消息解码器

由于最最基础的Netty收发消息,每个消息的结束,都需要一个标记符号来定义结束,也就是定义一串字符标记作为每个消息的结尾,但是实际生产活动中,我们不可能直接用 “常量字符串” 作为消息结束符,你没法保证,这个“常量字符串”也有可能出现在正常的消息体内容中的,因此可以定义一个自定义的解码器,用来定义,怎么判定消息结束:

添加自定义的消息解码器,其中 MyCustomMessageDecoder类 就是 具体的解码器实现

package com.ceyee.system3d.netty.core;



import com.ceyee.system3d.netty.net.MyCustomMessageDecoder;

import io.netty.channel.ChannelHandler;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.ChannelPipeline;

import io.netty.channel.socket.SocketChannel;

import io.netty.handler.codec.protobuf.ProtobufDecoder;

import io.netty.handler.codec.protobuf.ProtobufEncoder;

import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;

import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.DependsOn;

import org.springframework.stereotype.Component;



import javax.annotation.PostConstruct;



@Component

@DependsOn("chatHandler")



public class WSServerInitialzer extends ChannelInitializer<SocketChannel> {



private static WSServerInitialzer wsServerInitialzer;

@Autowired

private ChatHandler chatHandler;



@PostConstruct

private void init(){

wsServerInitialzer = this;

wsServerInitialzer.chatHandler = this.chatHandler;

}



@Override

protected void initChannel(SocketChannel ch) throws Exception {

ChannelPipeline pipeline = ch.pipeline();

// delemiter = Unpooled.copiedBuffer(NettyConst.dataEndingFlag);

//maxLength表示当前解码器可以解码的最大长度。

//failFast表示如果超过了这个最大长度是否马上抛出异常

//stripDelimiter表示是否带着换行符一起解析

//discarding表示当前解析器处于丢弃模式

//discardedBytes表示已经丢弃的字节



//自定义的解码器

pipeline.addLast(

new MyCustomMessageDecoder(),

wsServerInitialzer.chatHandler

);



}



}

具体自定义的消息解码器的类实现:我的消息分割定义是,每个消息头塞一个4字节,用来作为消息标识,但是这个解码器 仍然有一个bug,就是健康的条件必须是客户端的包 每个消息都是作为一个包发送,如果存在分片发送就会出现bug

package com.ceyee.system3d.netty.net;



import io.netty.buffer.ByteBuf;

import io.netty.buffer.Unpooled;

import io.netty.channel.ChannelHandlerContext;

import io.netty.handler.codec.ByteToMessageDecoder;

import io.netty.util.ReferenceCountUtil;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;



import java.util.List;



public class MyCustomMessageDecoder extends ByteToMessageDecoder {

// 消息头:发送端写的是一个int,占用4字节。

private final static int HEAD_LENGTH = 4;



protected Logger logger = LoggerFactory.getLogger(getClass());



@Override

protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {

// logger.info("解码器收到 in.readableBytes()="+in.readableBytes()+",capacity="+in.capacity());

if (in.readableBytes() < HEAD_LENGTH) {

return;

}

// 标记一下当前的readIndex的位置

in.markReaderIndex();

// 读取数据长度

int dataLength = in.readInt();

// logger.info("此时 dataLength="+dataLength);//+",capacity="+in.capacity()+"@");

// 我们读到的消息体长度为0,这是不应该出现的情况,这里出现这情况,关闭连接。

if (dataLength < 0) {

ctx.close();

}

//读到的消息体长度如果小于我们传送过来的消息长度,则resetReaderIndex. 这个配合markReaderIndex使用的。把readIndex重置到mark的地方

if (in.readableBytes() < dataLength) {

//yanruTODO 这里 是有bug的,如果 处理正常的前提 是基于收到的客户端的包一定是确定的 包头长度+包体数的格式,万一客户端分片发送,这里就会被丢弃。

//后续看这个文档解决:https://www.cnblogs.com/zjyingchang/p/9970301.html

in.resetReaderIndex();

return;

}

// 将缓冲区的数据读到字节数组

byte[] body = new byte[dataLength];

in.readBytes(body);



ByteBuf resp = Unpooled.copiedBuffer(body);

ReferenceCountUtil.retain(resp);//这行

//2020-5-20 yanrufix 找到了病症,byte数组不能转为字符窜,这个new string会出问题的!

out.add(resp);//body);



}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值