首页 > 其他分享 >用springBoot、netty写TCP客户端/服务端,并用TCP工具测试

用springBoot、netty写TCP客户端/服务端,并用TCP工具测试

时间:2023-10-27 14:55:06浏览次数:26  
标签:netty springBoot ctx TCP io import 服务端 客户端

1.启动客户端和连接服务端

package com.pkx.cloud.test.netty;

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 lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;


/**
 * 客户端
 */
@Component
@Slf4j
//@RequiredArgsConstructor
public class client implements InitializingBean {
    public void connect(int port, String host) throws Exception{

        /**
         * 客户端的NIO线程组
         *
         */
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            /**
             * Bootstrap 是一个启动NIO服务的辅助启动类 客户端的
             */
            Bootstrap bootstrap = new Bootstrap();
            /**
             * 设置group
             */
            bootstrap = bootstrap.group(group);
            /**
             * 关联客户端通道
             */
            bootstrap = bootstrap.channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true);
            /**
             * 设置 I/O处理类,主要用于网络I/O事件,记录日志,编码、解码消息
             */
            bootstrap = bootstrap.handler(new ServerHandlerInit());

            System.out.println("netty client start success!");

            /**
             * 连接服务端
             */
            ChannelFuture f = bootstrap.connect(host, port).sync();
            //通常需要写不断重连服务端
            f.addListener((ChannelFutureListener) future -> {
                if (!future.isSuccess()) {
                   //重连交给后端线程执行
                    future.channel().eventLoop().schedule(() -> {
                        log.info("重连服务端...");
                        try {
                            connect();
                        } catch (Exception e) {
    //                        e.printStackTrace();
                            log.error("连接失败。。。");
                        }
                    }, 3000, TimeUnit.MILLISECONDS);
                } else {
                  log.info("服务端连接成功...");
              }
            });
            /**
             * 等待连接端口关闭
             */
            f.channel().closeFuture().sync();

        } finally {
            /**
             * 退出,释放资源
             */
            group.shutdownGracefully();
        }
    }

    /**
     * 通道初始化
     */
    static class ServerHandlerInit extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();
            // 解码出具体的数据类型
            //该篇对解码器和编码器进行了详细说明,推荐阅读https://blog.csdn.net/tang_huan_11/article/details/133853786
            pipeline.addLast("decoder", new StringDecoder());//解码和编码可以自己定义
            pipeline.addLast(new handler());//handler类是自己写的处理类
            pipeline.addLast("encoder", new StringEncoder());
        }
    }

    @Override
    @Async //异步多线程注解
    //这个方法在服务启动时就会执行,即服务启动后则客户端就启动了
    public void afterPropertiesSet() throws Exception {
        connect(8888,"127.0.0.1");//TCP工具做服务端进行测试的时候的端口和ip
    }
}

2.自定义处理类

package com.pkx.cloud.test.netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.net.InetSocketAddress;

/**
 * 处理类
 */
@Service
public class handler extends ChannelInboundHandlerAdapter {
    /**
     * 从服务端收到新的数据时,这个方法会在收到消息时被调用
     * 这里写收到服务端的数据之后要做的处理,通常有数据类型转换,数据解析
     * @param ctx
     * @param msg
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception, IOException
    {
        System.out.println("channelRead:read msg:"+msg.toString());
        //回应服务端
        ctx.write("接收到了数据----------------------!");
    }

    /**
     * 从服务端收到新的数据、读取完成时调用
     *
     * @param ctx
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws IOException
    {
        System.out.println("channelReadComplete");
        //回应服务端
        ctx.write("接收到了新数据----------------------!");
        ctx.flush();
    }

    /**
     * 当出现 Throwable 对象才会被调用,即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时
     *
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws IOException
    {
        System.out.println("exceptionCaught");
        cause.printStackTrace();
        ctx.close();//抛出异常,断开与客户端的连接
    }

    /**
     * 客户端与服务端第一次建立连接时 执行
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception, IOException
    {
        super.channelActive(ctx);
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = insocket.getAddress().getHostAddress();
        System.out.println("channelActive:"+clientIp+ctx.name());
        ByteBuf message = null;
        byte[] req = ("I am client once").getBytes();
        for(int i = 0; i < 5; i++) {
            message = Unpooled.buffer(req.length);
            message.writeBytes(req);
            Thread.sleep(5000);
            ctx.writeAndFlush(message);
        }

    }

    /**
     * 客户端与服务端 断连时 执行
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception, IOException
    {
        super.channelInactive(ctx);
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = insocket.getAddress().getHostAddress();
        ctx.close(); //断开连接时,必须关闭,否则造成资源浪费
        System.out.println("channelInactive:"+clientIp);
    }
}

推荐一篇文章对客户端和服务端写法上的区别说明:https://juejin.cn/post/7290740945073668131

3.工具测试

  • 首先协议类型选择TCP服务端,表示该工具做服务端(因为我们写的是客户端,如果写的是服务端则这里选择 TCP client),ip和端口可以自定义,和我们上面代码里写的连接服务端的ip和端口保持一致就行,然后点击打开,可以看到当前连接对象数量为0

  • 然后我们启动我们的客户端服务

  • 可以看到已经有一个可连接的对象,并且已经自动连接上

  • 向客户端发送数据,可以看到客户端已经收到并作出了反应

至此,一个TCP客户端如何编写与测试就结束了,具体的代码解释已经标注

标签:netty,springBoot,ctx,TCP,io,import,服务端,客户端
From: https://www.cnblogs.com/lal520/p/17792372.html

相关文章

  • TCP/IP协议族面面观
    TCP/IP协议族介绍及在C#中的使用什么是TCP/IP协议族?TCP/IP协议族是互联网最基本的网络协议族,它定义了互联网上设备之间进行通信的规范。TCP/IP协议族由多个协议组成,每个协议都有不同的功能和用途,下面我们将介绍其中一些常用的协议。TCP/IP协议族中的常用协议1.IP协议Interne......
  • CentOS7系统放行TCP/UDP端口教程
    在使用CentOS7操作系统时,您需要放行某些端口,以便应用程序能够正常运行。下面是如何放行TCP/UDP端口的步骤。步骤1:SSH连接服务器使用SSH方式连接服务器,如果您不知道如何SSH连接服务器,可以查看该教程:SSH远程连接Linux服务器教程步骤2:确定要放行的端口在放行端口之前,您需要确定要......
  • 基于SpringBoot的菜谱交流系统-计算机毕业设计源码+LW文档
    开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:Maven3.3.9浏览器:谷歌浏览器本选题致力于开发一个菜谱交流系统,旨在帮助越来越多的人可以与他人分享自己做菜的经验和做法,并......
  • 传输层协议 TCP
    TCP(TransportControlProtocol)是一个传输层协议,提供Host-To-Host数据的可靠传输,支持全双工,是一个连接导向的协议。TCP提供的是Host-To-Host传输,一台主机通过TCP发送数据给另一台主机。 TCP协议往上是应用到应用(Application-To-Application)的协议。什么是应用到应用......
  • SpringBoot环境配置
    使用Intellij2023版本。1.新建空项目;2.在file中找到projectstructures,设置languagelevel为17。(否则后续通过springinitializr新建模块时会报错)3.右键项目图标,新建module。4.使用maven,sdk为17版本,springboot使用3.15版本,dependencies为:DeveloperTools:Gr......
  • TCP Socket性能优化秘籍
    一、引言1.1、TCPSocket在网络通信中的重要性TCPSocket在网络通信中的重要性体现在其提供了可靠的数据传输、连接性、多路复用等特性,是实现各种网络应用的基础,同时具有广泛的兼容性。它的存在使得网络通信更加可靠、高效和方便。其重要性如下:可靠性:TCP(传输控制协议)是一种可......
  • SpringBoot 缓存Ehcache的使用说明
    pring缓存(cache)是在Spring3.1开始引入的,但是其本身只提供了缓存接口,不提供具体缓存的实现,其实现需要第三方缓存实现(Generic、EhCache、Redis等)。EhCache、Redis比较常用,使用Redis的时候需要先安装Redis服务器。为什么引入缓存提升服务性能例如在项目开发完成以后,随着时间推移,各种......
  • EPOLLOUT只是表示tcp stream是可写入的?
    tcp_poll方法https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp.c#L553......
  • SpringBoot 实现大文件断点续传
    最近在工作中有涉及到文件上传功能,需求方要求文件最大上限为1G,如果直接将文件在前端做上传,会出现超长时间等待,如果服务端内存不够,会直接内存溢出,此时我们可以通过断点续传方式解决,前端我们通过WebUploader实现文件分割和上传,语言是React,后端我们通过SpringBoot实现文件接收和组装......
  • 02. TCP编程
    一、什么是TCP协议  TCP协议则是建立在IP协议之上的。TCP协议负责在两台计算机之间建立可靠连接,保证数据包按顺序达到。TCP协议会通过3次握手建立可靠连接。然后需要对每个IP包进行编号,确保对方按顺序收到,如果包丢了,就自动重发。一个TCP报文来了以后,到底是交给那个......