SpringBoot搭建Netty+Socket+Tcp服务端和客户端

这篇具有很好参考价值的文章主要介绍了SpringBoot搭建Netty+Socket+Tcp服务端和客户端。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一: 服务端 

1: 启动类

package com.idc.config.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;

/**
 * @description: netty服务启动类
 **/

@Slf4j
@Component
public class NettyServer {

    public void start(InetSocketAddress address) {
        //配置服务端的NIO线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap()
                    .group(bossGroup, workerGroup)  // 绑定线程池
                    .channel(NioServerSocketChannel.class)
                    .localAddress(address)
                    .childHandler(new NettyServerChannelInitializer())//编码解码
                    .option(ChannelOption.SO_BACKLOG, 128);  //服务端接受连接的队列长度,如果队列已满,客户端连接将被拒绝
//                    .childOption(ChannelOption.SO_KEEPALIVE, true);  //保持长连接,2小时无数据激活心跳机制

            // 绑定端口,开始接收进来的连接
            ChannelFuture future = bootstrap.bind(address).sync();
            log.info("ODF-Socket------netty服务器开始监听端口:" + address.getPort());
            //关闭channel和块,直到它被关闭
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}

2: 处理程序

package com.idc.config.netty;

import com.idc.config.udpsocket.UdpServerHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

/**
 * @description: 服务端初始化,客户端与服务器端连接一旦创建,这个类中方法就会被回调,设置出站编码器和入站解码器
 **/

public class NettyServerChannelInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel channel) throws Exception {

        channel.pipeline().addLast("decoder",new StringDecoder(CharsetUtil.UTF_8));
        channel.pipeline().addLast("encoder",new StringEncoder(CharsetUtil.UTF_8));
        channel.pipeline().addLast(new NettyServerHandler());
    }
}
package com.idc.config.netty;

import com.idc.common.exception.CommonRuntimeException;
import com.idc.entity.odf.dto.LightingStatus;
import com.idc.mapper.OdfAlarmMapper;
import com.idc.mapper.OdfMapper;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.net.InetSocketAddress;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author wcybaonier
 * @description: netty服务端处理类
 **/

@Slf4j
@Component
public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    @Resource
    private OdfMapper odfMapper;

    /**
     * 管理一个全局map,保存连接进服务端的通道数量
     */
    private static final ConcurrentHashMap<ChannelId, ChannelHandlerContext> CHANNEL_MAP = new ConcurrentHashMap<>();



    /**
     * @param ctx
     * @DESCRIPTION: 有客户端连接服务器会触发此函数
     * @return: void
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {

        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();

        String clientIp = insocket.getAddress().getHostAddress();
        int clientPort = insocket.getPort();

        //获取连接通道唯一标识
        ChannelId channelId = ctx.channel().id();

        System.out.println();
        //如果map中不包含此连接,就保存连接
        if (CHANNEL_MAP.containsKey(channelId)) {
            log.info("ODF-Socket------客户端【" + channelId + "】是连接状态,连接通道数量: " + CHANNEL_MAP.size());
        } else {
            //保存连接
            CHANNEL_MAP.put(channelId, ctx);

            log.info("ODF-Socket------客户端【" + channelId + "】连接netty服务器[IP:" + clientIp + "--->PORT:" + clientPort + "]");
            log.info("ODF-Socket------连接通道数量: " + CHANNEL_MAP.size());
        }
    }

    /**
     * @param ctx
     * @DESCRIPTION: 有客户端终止连接服务器会触发此函数
     * @return: void
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {

        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();

        String clientIp = insocket.getAddress().getHostAddress();

        ChannelId channelId = ctx.channel().id();

        //包含此客户端才去删除
        if (CHANNEL_MAP.containsKey(channelId)) {
            //删除连接
            CHANNEL_MAP.remove(channelId);
            System.out.println();
            log.info("ODF-Socket------客户端【" + channelId + "】退出netty服务器[IP:" + clientIp + "--->PORT:" + insocket.getPort() + "]");
            log.info("ODF-Socket------连接通道数量: " + CHANNEL_MAP.size());
        }
    }

    /**
     * @param ctx
     * @DESCRIPTION: 有客户端发消息会触发此函数
     * @return: void
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg == null){
            throw new CommonRuntimeException("ODF-Socket------加载客户端报文为空,请联系厂商!");
        }
        log.info("ODF-Socket------加载客户端报文......【" + ctx.channel().id() + "】" + " :" + msg);


        /**
         *  下面可以解析数据,保存数据,生成返回报文,将需要返回报文写入write函数
         *   在这里可以设置异步执行 提交任务到该channel的taskQueue 中
         */
        ctx.channel().eventLoop().execute(() -> {
            String msgStr = String.valueOf(msg);
            // 如果不包含逗号, 那么格式不对 约定格式为 : 序列号,deviceid,shelfNo,moduleNo,termNo,state
            if (!msgStr.contains(",")){
                throw new CommonRuntimeException("ODF-Socket------加载客户端报文格式不正确,请联系厂商!");
            }
            try {
                String[] split = msgStr.split(",");
                if (split.length != 6){
                    throw new CommonRuntimeException("ODF-Socket------加载客户端报文长度不正确,请联系厂商!");
                }
                //开始修改 admin
                LightingStatus lightingStatus = new LightingStatus();
                lightingStatus.setSerialNumber(split[0]);
                lightingStatus.setDeviceId(split[1]);
                lightingStatus.setShelfNo(split[2]);
                lightingStatus.setModuleNo(split[3]);
                lightingStatus.setTermNo(split[4]);
                lightingStatus.setState(split[5]);
                int i = odfMapper.updateTermStatus(lightingStatus);
                log.info("ODF-Socket------亮灯状态更新条数......【" + i + "】" );
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        /**
         * 可以设置多个异步任务
         * 但是这个会在上面异步任务执行完之后才执行
         */
        /*ctx.channel().eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(10*1000);
                    log.info(">>>>>>>>>休眠二十秒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });*/

        //响应客户端
        log.info("ODF-Socket------服务端端返回报文......【" + ctx.channel().id() + "】" + " :" + msg);
        this.channelWrite(ctx.channel().id(), msg);
    }

    /**
     * @param msg        需要发送的消息内容
     * @param channelId 连接通道唯一id
     * @DESCRIPTION: 服务端给客户端发送消息
     * @return: void
     */
    public void channelWrite(ChannelId channelId, Object msg) throws Exception {

        ChannelHandlerContext ctx = CHANNEL_MAP.get(channelId);

        if (ctx == null) {
            log.info("ODF-Socket------通道【" + channelId + "】不存在");
            return;
        }

        if (msg == null || msg == "") {
            log.info("ODF-Socket------服务端响应空的消息");
            return;
        }

        //将客户端的信息直接返回写入ctx
        ctx.write(msg);
        //刷新缓存区
        ctx.flush();
    }

    public static void main(String[] args) {
        System.out.println("序列号,deviceid,shelfNo,moduleNo,termNo,state".split(",").length);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {

        String socketString = ctx.channel().remoteAddress().toString();

        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                log.info("ODF-Socket------Client: " + socketString + " READER_IDLE 读超时");
                ctx.disconnect();
            } else if (event.state() == IdleState.WRITER_IDLE) {
                log.info("ODF-Socket------Client: " + socketString + " WRITER_IDLE 写超时");
                ctx.disconnect();
            } else if (event.state() == IdleState.ALL_IDLE) {
                log.info("ODF-Socket------Client: " + socketString + " ALL_IDLE 总超时");
                ctx.disconnect();
            }
        }
    }

    /**
     * @param ctx
     * @DESCRIPTION: 发生异常会触发此函数
     * @return: void
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

        System.out.println();
        ctx.close();
        log.info("ODF-Socket------"+ctx.channel().id() + " 发生了错误,此连接被关闭" + "此时连通数量: " + CHANNEL_MAP.size());
        //cause.printStackTrace();
    }
}

3: 项目启动类

package com.idc;

import com.idc.config.udpsocket.UdpServer;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

import javax.annotation.Resource;

/**
 * @author wcybaonier
 */
@MapperScan("com.idc.mapper")
@SpringBootApplication
@Slf4j
@EnableScheduling
public class IdcPduApplication implements CommandLineRunner {

    @Value("${netty.host}")
    private String host;

    @Value("${netty.port}")
    private Integer port;

    @Resource
    private NettyServer nettyServer;

    public static void main(String[] args) {
        SpringApplication.run(IdcPduApplication.class, args);
        log.info("IdcPduApplication 启动成功!");
    }

    /**
     * netty服务启动
     * @param args
     * @throws Exception
     */
    @Override
    public void run(String... args) throws Exception {
        //tcp实现 
        InetSocketAddress address = new InetSocketAddress(host,port);
        log.info("neety服务器启动地址: "+host+":"+ port);
        nettyServer.start(address);
    }
}

yml配置: 

# 配置Netty通信IP和端口
netty:
  port: 7101
  host: 127.0.0.1

 

完成,启动项目即可自动监听对应端口

二: 客户端

1: 启动类

这里为了测试,写了Main方法,可以参考服务端,配置启动类 ,实现跟随项目启动

package com.ws.aa;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Springboot整合Netty,实现Socket信息交互(本项目用于与C语言程序进行交互)
 * 
 * 核心文件(与服务端进行数据交互)
 * 
 * 客户端
 *
 * @author 小辰哥哥
 */
public class SocketClient {
    // 服务端IP
    static final String HOST = System.getProperty("host", "134.95.3.134");

    // 服务端开放端口
    static final int PORT = Integer.parseInt(System.getProperty("port", "7101"));

    // 数据包大小
    static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));

    // 日志打印
    private static final Logger LOGGER = LoggerFactory.getLogger(SocketClient.class);

    // 主函数启动
    public static void main(String[] args) throws InterruptedException {
        sendMessage("1,deviceid123,shelfNo123,moduleNo123,termNo123,state123");
    }

    /**
     * 核心方法(处理:服务端向客户端发送的数据、客户端向服务端发送的数据)
     *
     * @param content
     * @throws InterruptedException
     * @author 小辰哥哥
     */
    public static void sendMessage(String content) throws InterruptedException {
        // Configure the client.
        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 {
                            ChannelPipeline p = ch.pipeline();
                            p.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
                            p.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
                            p.addLast(new SocketHandler() {
                                @Override
                                public void channelRead(ChannelHandlerContext ctx, Object msg) {
                                    LOGGER.debug("####接收服务端发送过来的消息####");
                                    LOGGER.debug("服务端发送过来的数据:" + msg);

                                    // 主动与服务端断开连接(客户端触发)
                                    //ctx.channel().close();
                                }
                            });
                        }
                    });

            ChannelFuture future = b.connect(HOST, PORT).sync();
            future.channel().writeAndFlush(content);

            // 程序阻塞
            future.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }

}

2: 处理程序

package com.ws.aa;

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.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

import java.util.logging.SocketHandler;

/**
 * Springboot整合Netty,实现Socket信息交互(本项目用于与C语言程序进行交互)
 *
 * 设置出站和入站的编码器和解码器(该方法在SocketClientConfig.java中被重写)
 *
 * 客户端
 *
 * @author wcybaonier
 */

public class SocketChannelInitializer extends ChannelInitializer<SocketChannel> {
    protected void initChannel(SocketChannel channel) throws Exception {
        ChannelPipeline p = channel.pipeline();
        p.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
        p.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
        p.addLast((ChannelHandler) new SocketHandler());
    }
}
package com.ws.aa;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Springboot整合Netty,实现Socket信息交互(本项目用于与C语言程序进行交互)
 *
 * 初始化操作、接受服务端发送过来的消息(该方法在SocketClient.java中被重写)
 *
 * 客户端
 *
 * @author wcybaonier
 */

public class SocketHandler extends ChannelInboundHandlerAdapter {

    // 日志打印
    private static final Logger LOGGER = LoggerFactory.getLogger(SocketHandler.class);

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        LOGGER.debug("SocketHandler Active(客户端)");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        LOGGER.debug("####接收服务端发送过来的消息####");
        LOGGER.debug("SocketHandler read Message:" + msg);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        LOGGER.debug("####客户端断开连接####");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

 

3: 项目启动类

......想写就参考服务端......

三: 测试,完成

有测试的,,,但是忘记截图了................文章来源地址https://www.toymoban.com/news/detail-606057.html

到了这里,关于SpringBoot搭建Netty+Socket+Tcp服务端和客户端的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 服务端和客户端通信-TCP(含完整源代码)

    目录 简单TCP通信实验 分析 1、套接字类型 2、socket编程步骤 3、socket编程实现具体思路 实验结果截图 程序代码 实验设备:     目标系统:windows 软件工具:vs2022/VC6/dev 实验要求: 完成TCP服务端和客户端的程序编写; 实现简单字符串的收发功能。 需附上代码及运行结果截图

    2024年02月07日
    浏览(76)
  • 【Qt专栏】Qt实现TCP服务端和客户端通信

    网络通信是程序员必须会的一项生存技能,这里简单的实现了服务端和客户端通信的两个小示例,代码可以直接拿来用,开发环境是Qt5.9.6。 1.项目架构 2.tcpserver.h文件 3.tcpserver.cpp文件 4.测试效果 1.项目架构 2.tcpserver.h文件 3.tcpserver.cpp文件 4.测试效果 好了,两个小程序写完并

    2024年02月12日
    浏览(37)
  • linux搭建http源【服务端和客户端详细说明】

    inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0 [root@master ~]# 关闭防火墙和selinux firewall的话,直接 systemctl stop firewalld 即可 如果使用的是iptables,则吧firewalld替换成iptables [root@master ~]# systemctl is-active firewalld active [root@master ~]# [root@master ~]# systemctl stop firewalld [root@master ~]# [root@m

    2024年04月13日
    浏览(40)
  • netty-发起tcp长连接(包含客户端和服务端)

    Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持。 Netty是对JDK自带的NIO的API进行封装,具有高并发,高性能等优点。 项目中经常用到netty实现服务器与设备的通信,先写服务端代码: 服务端处理类代码: 接下来 模拟 客户端: 客户端处理类代

    2024年02月12日
    浏览(43)
  • linux搭建http源【服务端和客户端详细说明(1)

    我下面的HTTP配置使用的包就是iso镜像里面的包【同理,我们只要会这种方式以后,使用什么包都一样,可以自己在网上下载自己需要的包和依赖,然后通过这种方式配置成http源,然后就可以直接使用yum安装了,这样的好处是解决软件在安装的时候会有许多依赖包这个繁琐的

    2024年04月11日
    浏览(41)
  • 使用Netty构建TCP和UDP服务器和客户端

    Netty是一个基于Java NIO实现的网络通信框架,提供了高性能、低延迟的网络通信能力。使用Netty构建TCP和UDP服务器和客户端非常简单,下面是一个简单的示例代码: 构建TCP服务器 构建TCP客户端 构建UDP服务器 构建UDP客户端   上述示例代码中,分别定义了一个TCP服务器、TCP客户

    2024年02月16日
    浏览(49)
  • 《TCP/IP网络编程》阅读笔记--基于Windows实现Hello Word服务器端和客户端

    目录 1--Hello Word服务器端 2--客户端 3--编译运行 3-1--编译服务器端 3-2--编译客户端 3-3--运行 运行结果:

    2024年02月10日
    浏览(66)
  • SpringBoot+CAS整合服务端和客户端实现SSO单点登录与登出快速入门上手

    教学讲解视频地址:视频地址 因为CAS支持HTTP请求访问,而我们是快速入门上手视频,所以这期教程就不教大家如何配置HTTPS了,如果需要使用HTTPS,可以参考其他博客去云服务器申请证书或者使用JDK自行生成一个证书。 下载CAS Server(直接下载压缩包就可以) 这里我们用的是

    2024年02月02日
    浏览(68)
  • Linux网络编程:socket、客户端服务器端使用socket通信(TCP)

    socket(套接字),用于网络中不同主机间进程的通信。 socket是一个伪文件,包含读缓冲区、写缓冲区。 socket必须成对出现。 socket可以建立主机进程间的通信,但需要协议(IPV4、IPV6等)、port端口、IP地址。          (1)创建流式socket套接字。                 a)此s

    2024年02月11日
    浏览(65)
  • netty学习(3):SpringBoot整合netty实现多个客户端与服务器通信

    创建一个SpringBoot工程,然后创建三个子模块 整体工程目录:一个server服务(netty服务器),两个client服务(netty客户端) pom文件引入netty依赖,springboot依赖 NettySpringBootApplication NettyServiceHandler SocketInitializer NettyServer NettyStartListener application.yml Client1 NettyClientHandler SocketInitializ

    2024年02月11日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包