基于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以及在使用过程中的遇到的一些问题,分享给大家。

相关推荐
程序员飞哥2 小时前
几年没面试,这次真的被打醒了!
java·面试
骑着bug的coder2 小时前
第11讲:主从复制与读写分离架构
后端·mysql
love_summer2 小时前
代码中的“留白”艺术:Python空语句pass的设计哲学与最佳实践
后端
Learner2 小时前
Python异常处理
java·前端·python
tao3556672 小时前
VS Code登录codex,报错(os error 10013)
java·服务器·前端
6***83052 小时前
SpringBoot教程(三十二) SpringBoot集成Skywalking链路跟踪
spring boot·后端·skywalking
信创天地2 小时前
核心系统去 “O” 攻坚:信创数据库迁移的双轨运行与数据一致性保障方案
java·大数据·数据库·金融·架构·政务
mjhcsp2 小时前
C++ AC 自动机:原理、实现与应用全解析
java·开发语言·c++·ac 自动机