基于Netty的TCP协议的Socket客户端

关注我的公众号:【编程朝花夕拾】,可获取首发内容。

01引言

之前分享了关于Socket服务端的代码,这一节我们继续Socket客户端代码的分享。有了之前的代码基础,分享起来客户端端就非常简单了。因为客户端和服务端几位相似,甚至可以直接拷贝,但是有一些细节需要注意。

02 Socket客户端

2.1 代码预览

java 复制代码
@Slf4j
public class MockClient {

    @Getter
    private SocketChannel socketChannel;

    public void connect() throws InterruptedException {
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup(1);
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup);
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
        
        bootstrap.handler(new ChannelInitializer() {
            @Override
            protected void initChannel(Channel channel) throws Exception {
                ChannelPipeline pipeline = channel.pipeline();
                pipeline.addLast(new DelimiterBasedFrameDecoder(2048, Unpooled.copiedBuffer("_".getBytes())));
                pipeline.addLast(new StringDecoder(StandardCharsets.UTF_8));
                pipeline.addLast(new StringEncoder(StandardCharsets.UTF_8));
                pipeline.addLast(new SimpleChannelInboundHandler<String>(){
                    @Override
                    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
                        log.info("client receive: {}", msg);
                    }
                });
            }
        });

        ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9091).sync();
        this.socketChannel = (SocketChannel) channelFuture.channel();
    }
}

同样使用EventLoopGroup线程池,不过这里的线程组只需要一个就可以了,直接用来处理业务。

2.2 引导类

java 复制代码
EventLoopGroup eventLoopGroup = new NioEventLoopGroup(1);
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup);

io.netty.bootstrap.Bootstrap为客户端的引导类和服务端是不一样的。

  • io.netty.bootstrap.ServerBootstrap :服务端的引导类
  • io.netty.bootstrap.Bootstrap:客户端的引导类

但是引导类对应的方法非常类似。

2.3 配置

java 复制代码
// 绑定通道
bootstrap.channel(NioSocketChannel.class);
// 保活连接
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);

绑定的通道区别于服务端的通道:

  • io.netty.channel.socket.nio.NioServerSocketChannel:服务端的通道
  • io.netty.channel.socket.nio.NioSocketChannel:客户端的通道

类似带Server关键字的都属于服务端的属性或方法。

2.4 编解码器

编解码器要和服务端保持一致。

java 复制代码
bootstrap.handler(new ChannelInitializer() {
    @Override
    protected void initChannel(Channel channel) throws Exception {
        ChannelPipeline pipeline = channel.pipeline();
        pipeline.addLast(new DelimiterBasedFrameDecoder(2048, Unpooled.copiedBuffer("_".getBytes())));
        pipeline.addLast(new StringDecoder(StandardCharsets.UTF_8));
        pipeline.addLast(new StringEncoder(StandardCharsets.UTF_8));
        pipeline.addLast(new SimpleChannelInboundHandler<String>(){
            @Override
            protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
                log.info("client receive: {}", msg);
            }
        });
    }
});

这里要说明的是,自定义的业务处理器需要我们根据需要处理对应的方法。客户端主要用来发送消息。接不接收消息需要根据具体情况处理,案例为了验证客户端和服务端的相互通信,这里选择打印接收的消息。

2.5 连接服务端

java 复制代码
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9091).sync();
this.socketChannel = (SocketChannel) channelFuture.channel();

注意这里的连接服务端的方式和服务端暴漏接口的方式的区别:

bootstrap.connect(ip,port)用来连接服务端的IP端口,而服务端通过serverBootstrap.bind(9091)来绑定端口。

同时,都需要通过同步的等待端口绑定成功或连接服务端的IP和接口成功。

值得注意的是:

客户端的socketChannel.closeFuture().sync();要不要等待通道关闭?客户端主要用来发布消息,可以完全不同处理。如果还要等待接收服务端的消息,并且会随着方法执行完毕而结束,则需要该代码阻塞。

而实际应用都是嵌套在业务应用中的,本身就不会关闭,更加无需阻塞。

03 SprintBoot配置

前面介绍了服务端,我们结合客户端和服务端集成到SpringBoot中,形成完整的链路。

3.1 服务端初始化

java 复制代码
@Slf4j
@Component
public class StartConfig {
    
    @Autowired
    private SockerServer socketServer;


    @PostConstruct
    public void init() {
        socketServer.start();
    }
}

项目启动的时候可以通过@PostConstruct注解,完成项目启动后服务端的初始化。

3.2 客户端调用

java 复制代码
@Test
void contextLoads() throws Exception {
    MockClient mockClient = new MockClient();
    mockClient.connect();
    SocketChannel socketChannel = mockClient.getSocketChannel();

    // 发送消息
    socketChannel.writeAndFlush("foo test..._");
}

04 小结

客户端的代码到这里基本就结束了。后面就会介绍WebSocket以及在使用过程中的遇到的一些问题,分享给大家。

相关推荐
小马爱打代码1 分钟前
MyBatis:缓存体系设计与避坑大全
java·缓存·mybatis
时艰.7 分钟前
Java 并发编程:Callable、Future 与 CompletableFuture
java·网络
码云数智-园园8 分钟前
深入理解与正确实现 .NET 中的 BackgroundService
java·开发语言
好好研究10 分钟前
SpringBoot整合SpringMVC
xml·java·spring boot·后端·mvc
千寻技术帮12 分钟前
10386_基于SpringBoot的外卖点餐管理系统
java·spring boot·vue·外卖点餐
曹轲恒14 分钟前
SpringBoot整合SpringMVC(末)
java·spring boot·后端
_周游14 分钟前
Java8 API 文档搜索引擎_2.索引模块(程序)
java·搜索引擎·intellij-idea
小马爱打代码16 分钟前
Spring Boot:邮件发送生产可落地方案
java·spring boot·后端
BD_Marathon21 分钟前
设计模式——接口隔离原则
java·设计模式·接口隔离原则
空空kkk26 分钟前
SSM项目练习——hami音乐(二)
java