Netty3.5.9源码(一)Server端启动

本文详细介绍如何使用Netty搭建一个简单的回显服务器,包括ServerBootstrap的配置、NioServerSocketChannelFactory的构造、线程池的创建及绑定,以及ChannelPipeline的设置等关键步骤。

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

ServerBootstrap创建

ServerBootstrap创建

1,构造一个NioServerSocketChannelFactory来,初始化ServerBootstrap

2,构造一个ChannelPipelineFactory,给ServerBootstrap设置ChannelPipelineFactory

3, 绑定端口,接受client端连接过来的请求。

 public void run() {
        // Configure the server.
        ServerBootstrap bootstrap = new ServerBootstrap(
                new NioServerSocketChannelFactory(
                        Executors.newCachedThreadPool(),
                        Executors.newCachedThreadPool()));

        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() throws Exception {
                return Channels.pipeline(new EchoServerHandler());
            }
        });

        // Bind and start to accept incoming connections.
        bootstrap.bind(new InetSocketAddress(port));
    }

    public static void main(String[] args) throws Exception {
        ...
        new EchoServer(port).run();
    }

构造NioServerSocketChannelFactory,构造NioWorkerPool线程池,启动work线程

这里采用Reactor模式,bossExecutor用来接收客户端连接,而workerExecutor用来执行IO

的read,write等操作,这里还有个大侠NioServerSocketPipelineSink

    new NioServerSocketChannelFactory(
                            Executors.newCachedThreadPool(),
                            Executors.newCachedThreadPool())

    public NioServerSocketChannelFactory(
            Executor bossExecutor, Executor workerExecutor) {
        this(bossExecutor, workerExecutor, SelectorUtil.DEFAULT_IO_THREADS);
    }
    public NioServerSocketChannelFactory(
            Executor bossExecutor, Executor workerExecutor,
            int workerCount) {
        this(bossExecutor, new NioWorkerPool(workerExecutor, workerCount));
    }
        public NioServerSocketChannelFactory(
            Executor bossExecutor, WorkerPool<NioWorker> workerPool) {
        if (bossExecutor == null) {
            throw new NullPointerException("bossExecutor");
        }
        if (workerPool == null) {
            throw new NullPointerException("workerPool");
        }

        this.bossExecutor = bossExecutor;
        this.workerPool = workerPool;
        sink = new NioServerSocketPipelineSink(workerPool);
    }

将workerPool绑定到NioServerSocketPipelineSink

    NioServerSocketPipelineSink(WorkerPool<NioWorker> workerPool) {
        this.workerPool = workerPool;
    }

NioWorkerPool

    public NioServerSocketChannelFactory(
            Executor bossExecutor, Executor workerExecutor,
            int workerCount) {
        this(bossExecutor, new NioWorkerPool(workerExecutor, workerCount));
    }

NioWorkerPool线程池的构造,创建workerCount个NioWorker,默认为CPU的2倍数量

    public NioWorkerPool(Executor workerExecutor, int workerCount) {
        super(workerExecutor, workerCount);
    }
        AbstractNioWorkerPool(Executor workerExecutor, int workerCount) {
        if (workerExecutor == null) {
            throw new NullPointerException("workerExecutor");
        }
        if (workerCount <= 0) {
            throw new IllegalArgumentException(
                    "workerCount (" + workerCount + ") " +
                    "must be a positive integer.");
        }
        workers = new AbstractNioWorker[workerCount];

        for (int i = 0; i < workers.length; i++) {
            workers[i] = createWorker(workerExecutor);
        }
        this.workerExecutor = workerExecutor;
    }

NioWorker

    protected NioWorker createWorker(Executor executor) {
        return new NioWorker(executor);
    }
        public NioWorker(Executor executor) {
        super(executor);
    }

这个super是AbstractNioWorker,打开Selector

    AbstractNioWorker(Executor executor) {
        this.executor = executor;
        openSelector();
    }

打开Selector,启动worker thread

    private void openSelector() {
        try {
            selector = Selector.open();
        } catch (Throwable t) {
            throw new ChannelException("Failed to create a selector.", t);
        }

        // Start the worker thread with the new Selector.
        boolean success = false;
        try {
            DeadLockProofWorker.start(executor, new ThreadRenamingRunnable(this, "New I/O  worker #" + id));
            success = true;
        } finally {
            if (!success) {
                // Release the Selector if the execution fails.
                try {
                    selector.close();
                } catch (Throwable t) {
                    logger.warn("Failed to close a selector.", t);
                }
                selector = null;
                // The method will return to the caller at this point.
            }
        }
        assert selector != null && selector.isOpen();
    }

设置ChannelPipelineFactory

       bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() throws Exception {
                return Channels.pipeline(new EchoServerHandler());
            }
        });

绑定端口

 bootstrap.bind(new InetSocketAddress(port));

ServerBootstrap中的bind()方法

    //构造一个UpstreamHandler,Binder
    ChannelHandler binder = new Binder(localAddress, futureQueue);
    ChannelHandler parentHandler = getParentHandler();

    ChannelPipeline bossPipeline = pipeline();
    //将binder注册上bossPipeline
    bossPipeline.addLast("binder", binder);
    if (parentHandler != null) {
        bossPipeline.addLast("userHandler", parentHandler);
    }
    //创建channel
    Channel channel = getFactory().newChannel(bossPipeline);

再来看看ChannelFactory中的newChannel()

    public ServerSocketChannel newChannel(ChannelPipeline pipeline) {
        return new NioServerSocketChannel(this, pipeline, sink);
    }

在NioServerSocketChannel完成pipeline与sink(NioServerSocketPipelineSink)的绑定,

打开一个ServerSocketChannel,并且配置为非阻塞模式,触发ChannelOpen事件

NioServerSocketChannel(
            ChannelFactory factory,
            ChannelPipeline pipeline,
            ChannelSink sink) {
    super(factory, pipeline, sink);
    socket = ServerSocketChannel.open();
    socket.configureBlocking(false);
    config = new DefaultServerSocketChannelConfig(socket.socket());
    //触发ChannelOpen事件
    fireChannelOpen(this);

NioServerSocketChannel-->super()-->AbstractChannel- 绑定pipeline与sink

    protected AbstractChannel(
            Channel parent, ChannelFactory factory,
            ChannelPipeline pipeline, ChannelSink sink) {

        this.parent = parent;
        this.factory = factory;
        this.pipeline = pipeline;
        id = allocateId(this);
        //pipeline与sink的绑定
        pipeline.attach(this, sink);
    }

fireChannelOpen(this);触发ChannelOpen事件

    channel.getPipeline().sendUpstream(
            new UpstreamChannelStateEvent(
                    channel, ChannelState.OPEN, Boolean.TRUE));

这里是默认的DefaultChannelPipeline来处理sendUpstream()事件,

DefaultChannelHandlerContext head = getActualUpstreamContext(this.head);
sendUpstream(head, e);

这个head的handler是ServerBootstrap$Binder

 void sendUpstream(DefaultChannelHandlerContext ctx, ChannelEvent e) {
        try {
            ((ChannelUpstreamHandler) ctx.getHandler()).handleUpstream(ctx, e);
        } catch (Throwable t) {
            notifyHandlerException(e, t);
        }
    }

ctx.getHandler()获取到的handler是Binder,而这个handleUpstream在其父类SimpleChannelUpstreamHandler中

public void handleUpstream(
            ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
    if (e instanceof MessageEvent) {
        messageReceived(ctx, (MessageEvent) e);
    } else if (e instanceof WriteCompletionEvent) {
        WriteCompletionEvent evt = (WriteCompletionEvent) e;
        writeComplete(ctx, evt);
    } else if (e instanceof ChildChannelStateEvent) {
        ChildChannelStateEvent evt = (ChildChannelStateEvent) e;
        if (evt.getChildChannel().isOpen()) {
            childChannelOpen(ctx, evt);
        } else {
            childChannelClosed(ctx, evt);
        }
    } else if (e instanceof ChannelStateEvent) {
        ChannelStateEvent evt = (ChannelStateEvent) e;
        switch (evt.getState()) {
        case OPEN:
            if (Boolean.TRUE.equals(evt.getValue())) {
                channelOpen(ctx, evt);
            } else {
                channelClosed(ctx, evt);
            }
            break;
        case BOUND:
            if (evt.getValue() != null) {
                channelBound(ctx, evt);
            } else {
                channelUnbound(ctx, evt);
            }
            break;
        case CONNECTED:
            if (evt.getValue() != null) {
                channelConnected(ctx, evt);
            } else {
                channelDisconnected(ctx, evt);
            }
            break;
        case INTEREST_OPS:
            channelInterestChanged(ctx, evt);
            break;
        default:
            ctx.sendUpstream(e);
        }
    } else if (e instanceof ExceptionEvent) {
        exceptionCaught(ctx, (ExceptionEvent) e);
    } else {
        ctx.sendUpstream(e);
    }

这里的ChannelEvent是ChannelStateEvent,OPEN,进入channelOpen(),这个channelOpen是在Binder中实现的

    public void channelOpen(
            ChannelHandlerContext ctx,
            ChannelStateEvent evt) {

        try {
            evt.getChannel().getConfig().setPipelineFactory(getPipelineFactory());

            // Split options into two categories: parent and child.
            Map<String, Object> allOptions = getOptions();
            Map<String, Object> parentOptions = new HashMap<String, Object>();
            for (Entry<String, Object> e: allOptions.entrySet()) {
                if (e.getKey().startsWith("child.")) {
                    childOptions.put(
                            e.getKey().substring(6),
                            e.getValue());
                } else if (!e.getKey().equals("pipelineFactory")) {
                    parentOptions.put(e.getKey(), e.getValue());
                }
            }

            // Apply parent options.
            evt.getChannel().getConfig().setOptions(parentOptions);
        } finally {
            ctx.sendUpstream(evt);
        }

        boolean finished = futureQueue.offer(evt.getChannel().bind(localAddress));
        assert finished;
    }

关联主Channel与getPipelineFactory(pipelineFactory==EchoServer$1?)

 evt.getChannel().getConfig().setPipelineFactory(getPipelineFactory());

将parentOptions与主主Channel关联

evt.getChannel().getConfig().setOptions(parentOptions);

绑定之前打开的NioServerSocketChannel

evt.getChannel().bind(localAddress)

bind()

    public ChannelFuture bind(SocketAddress localAddress) {
        return Channels.bind(this, localAddress);
    }
    //sendDownstream事件
    public static ChannelFuture bind(Channel channel, SocketAddress localAddress) {
    if (localAddress == null) {
        throw new NullPointerException("localAddress");
    }
    ChannelFuture future = future(channel);
    channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
            channel, future, ChannelState.BOUND, localAddress));
    return future;
}

sendDownstream()事件

    public void sendDownstream(ChannelEvent e) {
        DefaultChannelHandlerContext tail = getActualDownstreamContext(this.tail);
        if (tail == null) {
            try {
                //终于看到sink了
                getSink().eventSunk(this, e);
                return;
            } catch (Throwable t) {
                notifyHandlerException(e, t);
                return;
            }
        }

        sendDownstream(tail, e);
    }

绕了一大圈,终于看到sink了

    public void eventSunk(
            ChannelPipeline pipeline, ChannelEvent e) throws Exception {
        Channel channel = e.getChannel();
        if (channel instanceof NioServerSocketChannel) {
            handleServerSocket(e);
        } else if (channel instanceof NioSocketChannel) {
            handleAcceptedSocket(e);
        }
    }

在这里ChannelEvent是BIND,channel是NioServerSocketChannel,state是BOUND

private void handleServerSocket(ChannelEvent e) {
    if (!(e instanceof ChannelStateEvent)) {
        return;
    }

    ChannelStateEvent event = (ChannelStateEvent) e;
    NioServerSocketChannel channel =
        (NioServerSocketChannel) event.getChannel();
    ChannelFuture future = event.getFuture();
    ChannelState state = event.getState();
    Object value = event.getValue();

    switch (state) {
    case OPEN:
        if (Boolean.FALSE.equals(value)) {
            close(channel, future);
        }
        break;
    case BOUND:
        if (value != null) {
            bind(channel, future, (SocketAddress) value);
        } else {
            close(channel, future);
        }
        break;
    default:
        break;
    }
}

直接进入看看bind

private void bind(
            NioServerSocketChannel channel, ChannelFuture future,
            SocketAddress localAddress) {

        boolean bound = false;
        boolean bossStarted = false;
        try {
            channel.socket.socket().bind(localAddress, channel.getConfig().getBacklog());
            bound = true;

            future.setSuccess();
            fireChannelBound(channel, channel.getLocalAddress());

            Executor bossExecutor =
                ((NioServerSocketChannelFactory) channel.getFactory()).bossExecutor;
            DeadLockProofWorker.start(bossExecutor,
                    new ThreadRenamingRunnable(new Boss(channel),
                            "New I/O server boss #" + id + " (" + channel + ')'));
            bossStarted = true;
        } catch (Throwable t) {
            future.setFailure(t);
            fireExceptionCaught(channel, t);
        } finally {
            if (!bossStarted && bound) {
                close(channel, future);
            }
        }
    }

从channel中拿到socket进行bind

channel.socket.socket().bind(localAddress, channel.getConfig().getBacklog());

启动boss线程

 Executor bossExecutor =
                ((NioServerSocketChannelFactory) channel.getFactory()).bossExecutor;
            DeadLockProofWorker.start(bossExecutor,
                    new ThreadRenamingRunnable(new Boss(channel),
                            "New I/O server boss #" + id + " (" + channel + ')'));

在boss线程中,打开一个Selector,

 Boss(NioServerSocketChannel channel) throws IOException {
        this.channel = channel;
        selector = Selector.open();
        boolean registered = false;
        try {
            channel.socket.register(selector, SelectionKey.OP_ACCEPT);
            registered = true;
        } finally {
            if (!registered) {
                closeSelector();
            }
        }

        channel.selector = selector;
    }

将ServerSocketChannel的注册OP_ACCEPT到selector

Boss负责接受连接请求,当有心情求到来时,将对应的channel指派一个worker线程来处理

Worker 负责对channel的读写操作

在run()中处理链接请求

    try {
        // Just do a blocking select without any timeout
        // as this thread does not execute anything else.
        selector.select();
        // There was something selected if we reach this point, so clear
        // the selected keys
        //如果有新链接,清除key
        selector.selectedKeys().clear();

        // accept connections in a for loop until no new connection is ready
        //循环请求队列,处理链接
        for (;;) {
            SocketChannel acceptedSocket = channel.socket.accept();
            if (acceptedSocket == null) {
                break;
            }
            registerAcceptedChannel(acceptedSocket, currentThread);

        }

worker

 private void registerAcceptedChannel(SocketChannel acceptedSocket, Thread currentThread) {
            try {
            //获取pipeline
                ChannelPipeline pipeline =
                    channel.getConfig().getPipelineFactory().getPipeline();
                //从workerPool中获取worker
                NioWorker worker = nextWorker();
                //注册
                worker.register(new NioAcceptedSocketChannel(
                        channel.getFactory(), pipeline, channel,
                        NioServerSocketPipelineSink.this, acceptedSocket,
                        worker, currentThread), null);
            } catch (Exception e) {

获取worker

   @SuppressWarnings("unchecked")
    public E nextWorker() {
        return (E) workers[Math.abs(workerIndex.getAndIncrement() % workers.length)];
    }

<think>我们有个明确的目标:使用Spring Boot整合Netty和Protostuff实现客户与服务通信。根据引用内容,我们可以构建如下步骤: 1. 项目结构:创建两个独立的模块(或项目),个用于服务个用于客户。 2. 依赖管理:在Spring Boot项目中添加Netty和Protostuff的依赖。 3. 序列化/反序列化:使用Protostuff作为序列化工具,我们需要定义消息结构,并编写编解码器。 4. 服务实现:包括启动类、初始化类、处理类。 5. 客户实现:包括启动类、初始化类、处理类。 6. 测试:启动服务和客户,进行通信测试。 下面我们逐步展开: ### 步骤1: 创建项目 使用Spring Initializr创建两个Spring Boot项目(或个多模块项目),分别命名为`netty-server`和`netty-client`。 ### 步骤2: 添加依赖 在`pom.xml`中添加依赖(两个项目都需要): ```xml <!-- Netty --> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.68.Final</version> <!-- 使用最新稳定版本 --> </dependency> <!-- Protostuff --> <dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-core</artifactId> <version>1.7.2</version> </dependency> <dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-runtime</artifactId> <version>1.7.2</version> </dependency> ``` ### 步骤3: 定义消息结构 我们需要定义个消息类,用于客户和服务之间的通信。例如: ```java public class Message { private int id; private String content; // 省略getter和setter } ``` ### 步骤4: 编写Protostuff编解码器 我们需要实现Netty的`MessageToMessageDecoder`和`MessageToMessageEncoder`来处理Protostuff序列化和反序列化。 首先,创建个工具类`ProtostuffUtil`: ```java import io.protostuff.LinkedBuffer; import io.protostuff.ProtostuffIOUtil; import io.protostuff.Schema; import io.protostuff.runtime.RuntimeSchema; public class ProtostuffUtil { private static final Schema<Message> schema = RuntimeSchema.getSchema(Message.class); public static byte[] serialize(Message message) { LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); try { return ProtostuffIOUtil.toByteArray(message, schema, buffer); } finally { buffer.clear(); } } public static Message deserialize(byte[] bytes) { Message message = schema.newMessage(); ProtostuffIOUtil.mergeFrom(bytes, message, schema); return message; } } ``` 然后,编写编码器和解码器: ```java // 编码器 public class ProtostuffEncoder extends MessageToByteEncoder<Message> { @Override protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception { byte[] bytes = ProtostuffUtil.serialize(msg); out.writeInt(bytes.length); // 写入消息长度 out.writeBytes(bytes); // 写入消息体 } } // 解码器 public class ProtostuffDecoder extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { if (in.readableBytes() < 4) { return; // 长度字段不够 } in.markReaderIndex(); int length = in.readInt(); if (in.readableBytes() < length) { in.resetReaderIndex(); return; } byte[] bytes = new byte[length]; in.readBytes(bytes); Message message = ProtostuffUtil.deserialize(bytes); out.add(message); } } ``` ### 步骤5: 服务实现 #### 服务启动类(在Spring Boot启动启动Netty服务) ```java import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; @Component public class NettyServer implements CommandLineRunner { @Value("${netty.port:8888}") private int port; @Override public void run(String... args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new NettyServerInitializer()); // 初始化类 ChannelFuture future = bootstrap.bind(port).sync(); future.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } ``` #### 服务初始化类 ```java import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; public class NettyServerInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline() // 解决粘包拆包问题:长度字段为4个字节,在消息头 .addLast(new LengthFieldBasedFrameDecoder(1024*1024, 0, 4, 0, 4)) .addLast(new LengthFieldPrepender(4)) // 编解码器 .addLast(new ProtostuffDecoder()) .addLast(new ProtostuffEncoder()) // 业务处理器 .addLast(new NettyServerHandler()); } } ``` #### 服务处理器 ```java import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; public class NettyServerHandler extends SimpleChannelInboundHandler<Message> { @Override protected void channelRead0(ChannelHandlerContext ctx, Message msg) throws Exception { System.out.println("Server received: " + msg); // 处理消息,并回复 Message response = new Message(); response.setId(msg.getId()); response.setContent("Server response for: " + msg.getContent()); ctx.writeAndFlush(response); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } ``` ### 步骤6: 客户实现 #### 客户启动类(模拟在Spring Boot中启动客户) ```java import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; @Component public class NettyClient implements CommandLineRunner { @Value("${netty.server.host:localhost}") private String host; @Value("${netty.server.port:8888}") private int port; @Override public void run(String... args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new NettyClientInitializer()); Channel channel = bootstrap.connect(host, port).sync().channel(); // 发送消息 Message message = new Message(); message.setId(1); message.setContent("Hello from client"); channel.writeAndFlush(message); // 等待连接关闭 channel.closeFuture().sync(); } finally { group.shutdownGracefully(); } } } ``` #### 客户初始化类 ```java import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; public class NettyClientInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline() .addLast(new LengthFieldBasedFrameDecoder(1024*1024, 0, 4, 0, 4)) .addLast(new LengthFieldPrepender(4)) .addLast(new ProtostuffDecoder()) .addLast(new ProtostuffEncoder()) .addLast(new NettyClientHandler()); } } ``` #### 客户处理器 ```java import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; public class NettyClientHandler extends SimpleChannelInboundHandler<Message> { @Override protected void channelRead0(ChannelHandlerContext ctx, Message msg) throws Exception { System.out.println("Client received: " + msg); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } ``` ### 步骤7: 配置和测试 在服务和客户的`application.properties`中配置口和主机(客户需要配置服务的地址和口)。 启动服务项目,然后启动客户项目。观察控制台输出,应该能看到客户发送消息,服务接收并回复,客户再接收回复。 ### 注意事项 1. 我们在编解码器前添加了`LengthFieldBasedFrameDecoder`和`LengthFieldPrepender`,用于处理粘包拆包问题。 2. 序列化工具类`ProtostuffUtil`是针对`Message`类的,如果需要序列化其他对象,可以扩展。 3. 在实际项目中,可以将消息类型设计得更通用,例如包含消息类型字段,以便处理多种消息。 以上就是个基于Spring Boot整合Netty和Protostuff实现客户与服务通信的简单示例。 ### 相关问题 1. 如何解决Netty中的粘包和拆包问题? 2. Protostuff序列化相比其他序列化方式(如JSON、Kryo)有什么优势? 3.Netty中如何实现心跳机制来保持长连接?</think>### 基于Spring Boot整合Netty和Protostuff实现通信 以下是完整的实现步骤,包含服务和客户代码: #### 1. 添加依赖(pom.xml) ```xml <dependencies> <!-- Netty --> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.68.Final</version> </dependency> <!-- Protostuff --> <dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-core</artifactId> <version>1.7.2</version> </dependency> <dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-runtime</artifactId> <version>1.7.2</version> </dependency> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> </dependencies> ``` #### 2. 定义消息实体类 ```java public class Message { private int id; private String content; private long timestamp; // 省略getter/setter } ``` #### 3. Protostuff序列化工具类 ```java import io.protostuff.LinkedBuffer; import io.protostuff.ProtostuffIOUtil; import io.protostuff.Schema; import io.protostuff.runtime.RuntimeSchema; public class ProtostuffSerializer { private static final Schema<Message> schema = RuntimeSchema.getSchema(Message.class); public static byte[] serialize(Message message) { LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); try { return ProtostuffIOUtil.toByteArray(message, schema, buffer); } finally { buffer.clear(); } } public static Message deserialize(byte[] bytes) { Message message = schema.newMessage(); ProtostuffIOUtil.mergeFrom(bytes, message, schema); return message; } } ``` #### 4. Netty服务实现 **启动(NettyServerStarter.java)** ```java import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; @Component public class NettyServerStarter implements CommandLineRunner { private static final int PORT = 8888; @Override public void run(String... args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new NettyServerInitializer()); ChannelFuture future = bootstrap.bind(PORT).sync(); System.out.println("Netty server started on port: " + PORT); future.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } ``` **初始化类 (NettyServerInitializer.java)** ```java import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; public class NettyServerInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) { ch.pipeline() // 处理粘包/拆包 .addLast(new LengthFieldBasedFrameDecoder(1024 * 1024, 0, 4, 0, 4)) .addLast(new LengthFieldPrepender(4)) // 序列化处理器 .addLast(new ProtostuffDecoder()) .addLast(new ProtostuffEncoder()) // 业务处理器 .addLast(new NettyServerHandler()); } } ``` **处理器 (NettyServerHandler.java)** ```java import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; public class NettyServerHandler extends SimpleChannelInboundHandler<Message> { @Override protected void channelRead0(ChannelHandlerContext ctx, Message msg) { System.out.println("Server received: " + msg.getContent()); // 构建响应 Message response = new Message(); response.setId(msg.getId()); response.setContent("Echo: " + msg.getContent()); response.setTimestamp(System.currentTimeMillis()); ctx.writeAndFlush(response); } } ``` #### 5. Netty客户实现 **启动(NettyClientStarter.java)** ```java @Component public class NettyClientStarter implements CommandLineRunner { private static final String HOST = "localhost"; private static final int PORT = 8888; @Override public void run(String... args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new NettyClientInitializer()); Channel channel = bootstrap.connect(HOST, PORT).sync().channel(); System.out.println("Connected to server"); // 发送测试消息 Message message = new Message(); message.setId(1); message.setContent("Hello from client"); message.setTimestamp(System.currentTimeMillis()); channel.writeAndFlush(message); channel.closeFuture().sync(); } finally { group.shutdownGracefully(); } } } ``` **初始化类 (NettyClientInitializer.java)** ```java public class NettyClientInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) { ch.pipeline() .addLast(new LengthFieldBasedFrameDecoder(1024 * 1024, 0, 4, 0, 4)) .addLast(new LengthFieldPrepender(4)) .addLast(new ProtostuffDecoder()) .addLast(new ProtostuffEncoder()) .addLast(new NettyClientHandler()); } } ``` **处理器 (NettyClientHandler.java)** ```java public class NettyClientHandler extends SimpleChannelInboundHandler<Message> { @Override protected void channelRead0(ChannelHandlerContext ctx, Message msg) { System.out.println("Client received response: " + msg.getContent()); } } ``` #### 6. Protostuff编解码器 ```java // 编码器 public class ProtostuffEncoder extends MessageToByteEncoder<Message> { @Override protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) { byte[] bytes = ProtostuffSerializer.serialize(msg); out.writeBytes(bytes); } } // 解码器 public class ProtostuffDecoder extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) { byte[] bytes = new byte[in.readableBytes()]; in.readBytes(bytes); out.add(ProtostuffSerializer.deserialize(bytes)); } } ``` ### 关键点说明 1. **粘包/拆包处理**:使用`LengthFieldBasedFrameDecoder`和`LengthFieldPrepender`解决TCP粘包问题 2. **序列化流程**: - 对象 → Protostuff序列化 → 字节数组 → Netty发送 - 接收字节数组 → Protostuff反序列化 → 对象 3. **通信流程**: ```mermaid sequenceDiagram Client->>Server: Protostuff编码消息 Server->>Client: Protostuff编码响应 ``` ### 测试方法 1. 启动服务应用 2. 启动客户应用 3. 观察控制台输出: ``` Server received: Hello from client Client received response: Echo: Hello from client ``` ### 相关问题 1. 如何处理Netty中的TCP粘包和拆包问题? 2. Protostuff相比JSON/Protobuf有哪些性能优势? 3. 如何在Netty中实现心跳机制保持长连接? 4. 如何设计支持多种消息类型的Netty通信协议? 5. 如何监控Netty服务的性能指标和连接状态?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值