Spring Boot 整合 Netty 现实TCP通信

第一章、Spring Boot 整合 Netty 现实TCP通信(服务端)

第一章、Spring Boot 整合 Netty 现实TCP通信(客户端)

第二章、Spring Boot 整合 Netty 现实TCP通信实践

第三章、Spring Boot 整合 Netty 现实WebSocket通信

第四章、Spring Boot 整合 Netty 现实WebSocket通信实践

1、POM引入包

    <dependencies>
        <dependency>
            <groupId>com.cdkjframework</groupId>
            <artifactId>cdkj-util</artifactId>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>log4j-to-slf4j</artifactId>
                    <groupId>org.apache.logging.log4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

版本控制及cdkj-util基础包请访问开源工具包

common: 运用cdkj Framework 框架只需要简单,速搭建 Spring Boot + Spring Cloud 微服务项目,也可以单独搭建 Spring Boot 项目。

2、资源目录下新建 META-INF\spring

新建文件 org.springframework.boot.autoconfigure.AutoConfiguration.imports 内容

com.cdkjframework.socket.config.SocketAutoConfiguration

3、启动类

EnableAutoSocket
package com.cdkjframework.socket.annotation;

import com.cdkjframework.socket.config.SocketMarkerConfiguration;
import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

/**
 * @ProjectName: cdkj-framework
 * @Package: com.cdkjframework.swagger.annotation
 * @ClassName: EnableAutoSocket
 * @Description: socket 启动注解
 * @Author: xiaLin
 * @Date: 2023/7/18 9:20
 * @Version: 1.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({SocketMarkerConfiguration.class})
public @interface EnableAutoSocket {
}

4、Socket标记配置

package com.cdkjframework.socket.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @ProjectName: cdkj-framework
 * @Package: com.cdkjframework.swagger.config
 * @ClassName: SwaggerMarkerConfiguration
 * @Description: Socket标记配置
 * @Author: xiaLin
 * @Date: 2023/7/18 9:21
 * @Version: 1.0
 */
@Configuration(proxyBeanMethods = false)
public class SocketMarkerConfiguration {

  @Bean
  public Marker socketMarker() {
    return new Marker();
  }

  public static class Marker {

  }
}

5、配置读取

package com.cdkjframework.socket.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @ProjectName: cdkj-framework
 * @Package: com.cdkjframework.web.socket.config
 * @ClassName: SocketConfig
 * @Description: socket配置
 * @Author: xiaLin
 * @Version: 1.0
 */
@Data
@Component
@Configuration
@ConfigurationProperties(prefix = "spring.custom.socket")
public class SocketConfig {

    /**
     * 端口号
     */
    private List<Integer> port;
}

6、Socket自动配置

package com.cdkjframework.socket.config;

import com.cdkjframework.socket.NettySocketServer;
import com.cdkjframework.socket.listener.SocketListener;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

/**
 * @ProjectName: cdkj-framework
 * @Package: com.cdkjframework.socket.config
 * @ClassName: SocketAutoConfiguration
 * @Description: Socket自动配置
 * @Author: xiaLin
 * @Date: 2023/7/18 9:26
 * @Version: 1.0
 */
@Lazy(false)
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(SocketConfig.class)
@AutoConfigureAfter({WebClientAutoConfiguration.class})
@ConditionalOnBean(SocketMarkerConfiguration.Marker.class)
public class SocketAutoConfiguration {

    /**
     * 读取配置
     */
    private final SocketConfig customConfig;

    /**
     * 网状通道处理器
     */
    private final SocketListener socketListener;

    /**
     * 创建 bean
     *
     * @return 返回结果
     */
    @Bean(initMethod = "start")
    @ConditionalOnMissingBean
    public NettySocketServer nettySocketServer() {
        return new NettySocketServer(customConfig, socketListener);
    }
}

7、Netty服务端初始化

主业务初始化服务端配置及监听端口等

package com.cdkjframework.socket;

import com.cdkjframework.constant.IntegerConsts;
import com.cdkjframework.socket.config.SocketConfig;
import com.cdkjframework.socket.handler.NettyChannelInitializer;
import com.cdkjframework.socket.listener.SocketListener;
import com.cdkjframework.util.log.LogUtils;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @ProjectName: socket-algorithm
 * @Package: com.lesmarthome.socket.netty
 * @ClassName: NettySocketServer
 * @Author: frank
 * @Version: 1.0
 * @Description: Netty服务端初始化
 */
public class NettySocketServer {

    /**
     * 日志
     */
    private LogUtils logUtils = LogUtils.getLogger(NettySocketServer.class);

    /**
     * 读取配置
     */
    private final SocketConfig customConfig;

    /**
     * 网状通道处理器
     */
    private final SocketListener socketListener;

    /**
     * 构建函数
     */
    public NettySocketServer(SocketConfig customConfig, SocketListener socketListener) {
        this.customConfig = customConfig;
        this.socketListener = socketListener;
    }

    /**
     * 线程启动
     */
    public void start() {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup);
            serverBootstrap.channel(NioServerSocketChannel.class);
            // 保持长连接
            final boolean value = true;
            serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, value);
            serverBootstrap.option(ChannelOption.SO_BACKLOG, IntegerConsts.BYTE_LENGTH);
            serverBootstrap.childHandler(new NettyChannelInitializer(socketListener));
            // 服务器的逻辑
//            serverBootstrap.childHandler(new NettyChannelHandler(socketListener));
            // 监听多个端口
            List<Integer> portList = customConfig.getPort();
            for (Integer port :
                    portList) {
                ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
                // 给cf 注册监听器,监控我们关心的事件
                channelFuture.addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (channelFuture.isSuccess()) {
                            logUtils.info("监听端口[" + port + "]成功!");
                        } else {
                            logUtils.error("监听端口[" + port + "]失败!");
                        }
                    }
                });
                // channelFuture.channel().closeFuture().sync(); 对关闭通道进行监听
            }
        } catch (InterruptedException e) {
            logUtils.error(e);
        } finally {
            // bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully();
        }
    }
}

8、监听接口

SocketListener需要业务端自行实现
package com.cdkjframework.socket.listener;

/**
 * @ProjectName: socket-algorithm
 * @Package: com.lesmarthome.socket.netty.listener
 * @ClassName: SocketListener
 * @Author: frank
 * @Version: 1.0
 * @Description: 监听接口
 */
public interface SocketListener {

	/**
	 * 消息
	 *
	 * @param channelId 通话ID
	 * @param bytes     消息内容字节数据
	 */
	void onMessage(String channelId, byte[] bytes);

	/**
	 * 心跳
	 *
	 * @param channelId 通话ID
	 */
	void heartbeat(String channelId);

	/**
	 * 断开连接
	 *
	 * @param channelId 通道ID
	 */
	void onDisconnect(String channelId);
}

9、Netty通道初始化器

package com.cdkjframework.socket.handler;

import com.cdkjframework.constant.IntegerConsts;
import com.cdkjframework.socket.listener.SocketListener;
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.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @ProjectName: socket-algorithm
 * @Package: com.lesmarthome.socket.netty.handler
 * @ClassName: NettyChannelInitializer
 * @Author: frank
 * @Version: 1.0
 * @Description: Netty通道初始化器
 */
@Component
@ChannelHandler.Sharable
public class NettyChannelInitializer extends ChannelInitializer<SocketChannel> {

    /**
     * 网状通道处理器
     */
    private final SocketListener socketListener;

    /**
     * 构造函数
     */
    public NettyChannelInitializer(SocketListener socketListener) {
        this.socketListener = socketListener;
    }

    /**
     * 初始化通道
     *
     * @param socketChannel 插座通道
     * @throws Exception 异常信息
     */
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();

        // 服务器的逻辑
        pipeline.addLast(new NettyChannelHandler(socketListener));

        // 处理心跳
        pipeline.addLast(new IdleStateHandler(IntegerConsts.FIVE, IntegerConsts.ZERO, IntegerConsts.ZERO, TimeUnit.MINUTES));
        //处理日志
        pipeline.addLast(new LoggingHandler(LogLevel.INFO));

        // 自定义 handler
        pipeline.addLast(new NettyHeartbeatHandler(socketListener));
    }
}

10、Netty通道处理器

package com.cdkjframework.socket.handler;

import com.cdkjframework.constant.IntegerConsts;
import com.cdkjframework.socket.NettySocketUtils;
import com.cdkjframework.socket.listener.SocketListener;
import com.cdkjframework.util.log.LogUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.springframework.stereotype.Component;

/**
 * @ProjectName: socket-algorithm
 * @Package: com.lesmarthome.socket.netty.handler
 * @ClassName: NettyChannelHandler
 * @Author: frank
 * @Version: 1.0
 * @Description: Netty通道处理器
 */
@Component
@ChannelHandler.Sharable
public class NettyChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {

    /**
     * 日志
     */
    private static final LogUtils LOG_UTILS = LogUtils.getLogger(NettyChannelHandler.class);

    /**
		 * socket 监听
		 */
		private final SocketListener socketListener;

    /**
		 * 构造函数
		 *
		 * @param socketListener 监听接口
		 */
		public NettyChannelHandler(SocketListener socketListener) {
			this.socketListener = socketListener;
		}

	/**
     * 连接成功
     *
     * @param ctx 通道处理程序上下文
     * @throws Exception 异常信息
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        LOG_UTILS.info("RemoteAddress : " + channel.remoteAddress().toString() + " add !");
        NettySocketUtils.getClients().add(channel);
        NettySocketUtils.onlineChannelsHeart.put(ctx.channel().id().asLongText(), IntegerConsts.ONE);
    }

    /**
     * 断开成功
     *
     * @param ctx 通道处理程序上下文
     * @throws Exception 异常信息
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
			Channel channel = ctx.channel();
			NettySocketUtils.getClients().remove(channel);
			String channelId = channel.id().asLongText();
			NettySocketUtils.onlineChannelsHeart.remove(channelId);
			if (socketListener != null) {
				socketListener.onDisconnect(channelId);
			}
		}

	/**
	 * 有客户端终止连接服务器会触发此函数
	 *
	 * @param ctx 通道进程
	 * @throws Exception 异常信息
	 */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
			Channel channel = ctx.channel();
			String channelId = channel.id().asLongText();
			LOG_UTILS.info("RemoteAddress : " + channel.remoteAddress().toString() + " remove !");
			NettySocketUtils.getClients().remove(channel);
			NettySocketUtils.onlineChannelsHeart.remove(channelId);
			if (socketListener != null) {
				socketListener.onDisconnect(channelId);
			}
		}

	/**
	 * 读取数据
	 *
	 * @param ctx 通道处理程序上下文
	 * @param buf 消息
	 * @throws Exception 异常信息
	 */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
        String channelId = ctx.channel().id().asLongText();
        if (socketListener == null) {
					return;
				}
			byte[] bytes = new byte[buf.readableBytes()];
        buf.getBytes(buf.readerIndex(), bytes);
			socketListener.onMessage(channelId, bytes);
		}

    /**
     * 异常处理
     *
     * @param ctx   通道处理程序上下文
     * @param cause 异常信息
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
			Channel channel = ctx.channel();
			String channelId = channel.id().asLongText();
			LOG_UTILS.error("异常处理 - 通道ID:" + channelId + cause.getMessage());
			if (socketListener != null) {
				socketListener.onDisconnect(channelId);
			}
			if (channel.isActive()) {
				ctx.close();
				NettySocketUtils.getClients().remove(channel);
				NettySocketUtils.onlineChannelsHeart.remove(channelId);
			}
		}
}

11、Netty心跳处理器

package com.cdkjframework.socket.handler;

import com.cdkjframework.constant.IntegerConsts;
import com.cdkjframework.socket.NettySocketUtils;
import com.cdkjframework.socket.listener.SocketListener;
import com.cdkjframework.util.log.LogUtils;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;

/**
 * @ProjectName: com.lesmarthome.iot
 * @Package: com.lesmarthome.iot.netty.handler
 * @ClassName: TcpHeartbeatHandler
 * @Description: Netty心跳处理器
 * @Author: xiaLin
 * @Version: 1.0
 */
public class NettyHeartbeatHandler extends ChannelInboundHandlerAdapter {

	/**
	 * 监听数据
	 */
	private final SocketListener listener;

	/**
	 * 日志
	 */
	private LogUtils logUtils = LogUtils.getLogger(NettyHeartbeatHandler.class);

	/**
	 * 构造函数
	 */
	public NettyHeartbeatHandler(SocketListener listener) {
		this.listener = listener;
	}

	/**
	 * 用户事件已触发
	 *
	 * @param ctx 通道处理程序上下文
	 * @param evt 事件
	 * @throws Exception 异常信息
	 */
	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		if (evt instanceof IdleStateEvent) {
			Channel channel = ctx.channel();
			String channelId = channel.id().asLongText();

			// 该通道非法
			if (!NettySocketUtils.onlineChannelsHeart.containsKey(channelId)) {
				channel.close().sync();
				return;
			}
			IdleStateEvent event = (IdleStateEvent) evt;

			switch (event.state()) {
                // 进入读写空闲
                case ALL_IDLE:
									// 空闲60s之后触发 (心跳包丢失)
									Integer counter = NettySocketUtils.onlineChannelsHeart.get(channelId) + IntegerConsts.ONE;
									// 重置心跳丢失次数
									NettySocketUtils.onlineChannelsHeart.replace(channelId, counter);
									logUtils.info("通道【" + channelId + "】丢失了第 " + counter + " 个心跳包");
									if (counter < IntegerConsts.THREE) {
										return;
									}
									// 通道关闭
									logUtils.info("已与通道【%s】断开连接,地址:%s", channelId, channel.remoteAddress());
									if (listener == null) {
										// 连续丢失3个心跳包 (断开连接)
										channel.close().sync();
									} else {
										listener.heartbeat(channelId);
									}
									break;
				// 进入读空闲...
				case READER_IDLE:
					logUtils.info("通道【%s】进入读空闲!", channelId);
					break;
				// 进入写空闲...
				case WRITER_IDLE:
					logUtils.info("通道【%s】进入写空闲!", channelId);
					break;
			}
        }
    }
}

12、工具类

package com.cdkjframework.socket;

import com.cdkjframework.constant.IntegerConsts;
import com.cdkjframework.util.log.LogUtils;
import com.cdkjframework.util.tool.HexUtils;
import com.cdkjframework.util.tool.StringUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.util.HashMap;
import java.util.Optional;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @ProjectName: socket-algorithm
 * @Package: com.lesmarthome.socket.netty
 * @ClassName: NettySocketUtils
 * @Author: frank
 * @Version: 1.0
 * @Description: 工具类
 */
public class NettySocketUtils {

    /**
     * 日志
     */
    private static final LogUtils logUtils = LogUtils.getLogger(NettySocketUtils.class);

    /**
     * 锁
     */
    private static final ReentrantLock Lock = new ReentrantLock();

    /**
     * 客户端集合
     */
    private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 记录每一个channel的心跳包丢失次数
     */
    public static HashMap<String, Integer> onlineChannelsHeart = new HashMap<>();

    public static ChannelGroup getClients() {
        return clients;
    }

    public static void setClients(ChannelGroup clients) {
        NettySocketUtils.clients = clients;
    }

    /**
     * 发送消息
     *
     * @param message 消息内容
     */
    public static void sendRunnableMessage(String channelId, String message) {
        Lock.lock();
        try {
            Channel channel = findChannel(channelId);
            if (StringUtils.isNullAndSpaceOrEmpty(message) || channel == null) {
                return;
            }
            if (channel.isOpen()) {
                NettySocketUtils.onlineChannelsHeart.replace(channelId, IntegerConsts.ZERO);
                logUtils.info("channelId:" + channelId);
                message = message + System.lineSeparator();
                final ByteBuf buf = Unpooled.copiedBuffer(message, CharsetUtil.UTF_8);
                channel.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        channel.writeAndFlush(buf);
                    }
                });
            }
        } finally {
            Lock.unlock();
        }
    }

    /**
     * 发送消息
     *
     * @param message 消息内容
     */
    public static void sendMessageString(String channelId, String message) {
        Lock.lock();
        try {
            Channel channel = findChannel(channelId);
            if (StringUtils.isNullAndSpaceOrEmpty(message) || channel == null) {
                return;
            }
            if (channel.isOpen()) {
                NettySocketUtils.onlineChannelsHeart.replace(channelId, IntegerConsts.ZERO);
                ChannelFuture future = channel.writeAndFlush(Unpooled.wrappedBuffer(HexUtils.hexToByteArray(message)));
                future.addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        //写操作完成,并没有错误发生
                        if (future.isSuccess()) {
                            logUtils.info("发送成功!");
                        } else {
                            //记录错误
                            logUtils.error(future.cause(), "发送失败!");
                        }
                    }
                });
            }
        } finally {
            Lock.unlock();
        }
    }

    /**
     * 发送消息
     *
     * @param message 消息内容
     */
    public static void sendMessage(String channelId, String message) {
        Lock.lock();
        try {
            Channel channel = findChannel(channelId);
            if (StringUtils.isNullAndSpaceOrEmpty(message) || channel == null) {
                return;
            }
            if (channel.isOpen()) {
                NettySocketUtils.onlineChannelsHeart.replace(channelId, IntegerConsts.ZERO);
                logUtils.info("channelId:" + channelId);
                final ByteBuf buf = Unpooled.copiedBuffer(message, CharsetUtil.UTF_8);
                channel.writeAndFlush(buf);
            }
        } finally {
            Lock.unlock();
        }
    }

    /**
     * 发送消息
     *
     * @param message 消息内容
     */
    public static void sendFourGBraceletByteMessage(String channelId, byte[] message) {
        Lock.lock();
        try {
            Channel channel = findChannel(channelId);
            if (StringUtils.isNullAndSpaceOrEmpty(message) || channel == null) {
                return;
            }
            if (channel.isOpen()) {
                logUtils.info("channelId:" + channelId);
                final ByteBuf buf = Unpooled.copiedBuffer(message, IntegerConsts.ZERO, message.length);
                channel.writeAndFlush(buf);
            }
        } finally {
            Lock.unlock();
        }
    }

    /**
     * 发送消息
     *
     * @param message 消息内容
     */
    public static void sendMessage(String channelId, byte [] message) {
        Lock.lock();
        try {
            Channel channel = findChannel(channelId);
            if (StringUtils.isNullAndSpaceOrEmpty(message) || channel == null) {
                return;
            }
            if (channel.isOpen()) {
                logUtils.info("channelId:" + channelId);
                final ByteBuf buf = Unpooled.copiedBuffer(message);
                channel.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        channel.writeAndFlush(buf);
                    }
                });
            }
        } finally {
            Lock.unlock();
        }
    }


    /**
     * 是否打开
     *
     * @param channelId 通道ID
     * @return 返回布尔
     */
    public static boolean isOpen(String channelId) {
        Channel channel = findChannel(channelId);
        return channel != null;
    }

    /**
     * 获取通道
     *
     * @param channelId 通道ID
     * @return 返回通道
     */
    public synchronized static Channel findChannel(String channelId) {
        Optional<Channel> optional = clients.stream()
                .filter(f -> f.id().asLongText().equals(channelId))
                .findFirst();

        // 验证是否查询到结果
        if (!optional.isPresent()) {
            return null;
        }
        // 验证连接是否存在
        Channel channel = optional.get();
        if (!channel.isOpen()) {
            clients.remove(channel);
            return null;
        }
        // 返回结果
        return channel;
    }
}

13、总结

以前内容为Spring Boot整合Netty实现TCP通信封装,在后面文章会详细说明如果去实践完成和硬件通信

### 回答1: Spring Boot可以很方便地整合Netty,实现TCP协议的通信。具体实现步骤如下: 1. 引入Netty依赖 在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.25.Final</version> </dependency> ``` 2. 编写Netty服务端 编写一个Netty服务端,监听指定端口,接收客户端的请求,并返回响应。具体实现可以参考Netty官方文档。 3. 配置Spring BootSpring Boot的配置文件中,配置Netty服务端的端口号和其他相关参数。 4. 启动Spring Boot应用程序 启动Spring Boot应用程序,Netty服务端会自动启动并监听指定端口。 5. 编写客户端程序 编写一个客户端程序,连接Netty服务端,并发送请求。具体实现可以参考Netty官方文档。 通过以上步骤,就可以实现Spring Boot整合Netty,实现TCP协议的通信。 ### 回答2: Spring Boot是一个非常流行的Java开源框架,它提供了一种简单且快捷的方式来构建可扩展的Web应用程序。而Netty是一个基于NIO的客户端/服务器框架,它可以轻松处理高负载的网络通信。 因此通过Spring BootNetty整合,可以实现高效,快速,可扩展的TCP通信,在需要高性能可扩展的应用程序中是有很大的优势的。 实现过程如下: 1. 通过Spring Boot创建一个Maven项目,引入Netty依赖。 2. 创建Netty服务端和客户端,用于实现TCP通讯。服务端可以监听端口,客户端则可以连接服务端。 3. 将Netty的ChannelHandler封装成Spring Bean,并在Spring Boot中进行注入。 4. 通过使用Spring Boot的自动配置功能,将服务端和客户端的配置信息进行注入,从而使整个过程的配置更加简单。 5. 为了更好地支持多个客户端并发操作,可以使用Netty的线程池功能来提高性能和稳定性。 6. 配置Spring Boot,使其运行在指定的端口,并且注册Netty ChannelHandler,使其能够接收和处理来自客户端的请求消息。 7. 编写客户端代码,建立与服务端的连接并发送数据。 8. 客户端与服务端完成通信后,可以将数据响应给客户端,并断开连接。 通过以上步骤,就可以使用Spring BootNetty实现高效,快速,可扩展的TCP通信。这种架构有很多优点,例如高并发,高性能,易于维护,容易扩展等。对于需要实现实时数据传输和高性能的应用程序而言,这是一种非常好的解决方案。 ### 回答3: Springboot是一款非常流行的Java开发框架,它提供了很多便捷的工具和库,帮助开发者更快地搭建高效的应用程序。Netty则是一款基于NIO的高性能网络通信框架,非常适合开发高并发、高性能的网络应用。 利用Springboot整合Netty实现TCP通信可以方便地实现异步、非阻塞IO,而不需要开发者手动处理Java NIO的细节。下面简要介绍如何利用Springboot整合Netty实现TCP通信。 1. 引入Netty的依赖 在pom.xml文件中引入Netty的依赖,例如: ``` <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.25.Final</version> </dependency> ``` 2. 实现Netty服务端 创建一个NettyServer类,继承自ChannelInboundHandlerAdapter,并实现以下方法: ``` public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {} public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {} public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {} ``` 在NettyServer类的构造方法中启动Netty服务端,示例代码如下: ``` public class NettyServer extends ChannelInboundHandlerAdapter { public NettyServer() { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new NettyServer()); } }); // Bind and start to accept incoming connections. ChannelFuture f = b.bind(PORT).sync(); // Wait until the server socket is closed. f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // 处理读事件 } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { // 读事件完成处理 } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // 处理异常 } // 启动Netty服务端 public static void main(String[] args) { new NettyServer(); } } ``` 3. 实现Netty客户端 创建一个NettyClient类,继承自SimpleChannelInboundHandler,并实现以下方法: ``` public void channelActive(ChannelHandlerContext ctx) throws Exception {} protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {} public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {} ``` 在NettyClient类的构造方法中启动Netty客户端,示例如下: ``` public class NettyClient extends SimpleChannelInboundHandler<String> { private final String host; private final int port; public NettyClient(String host, int port) { this.host = host; this.port = port; EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.copiedBuffer("$_".getBytes()))); ch.pipeline().addLast(new StringEncoder()); ch.pipeline().addLast(new StringDecoder()); ch.pipeline().addLast(new NettyClient(host, port)); } }); // Start the client. ChannelFuture f = b.connect(host, port).sync(); // Wait until the connection is closed. f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { // 发送消息 } @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { // 处理读事件 } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // 处理异常 } // 启动Netty客户端 public static void main(String[] args) { String host = "127.0.0.1"; int port = 8080; new NettyClient(host, port); } } ``` 以上是利用Springboot整合Netty实现TCP通信的大致步骤。实际开发过程中还需要根据应用程序的具体需求进一步优化和调整。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

维基框架

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值