news 2026/6/22 19:52:52

基于Netty的TCP协议的Socket服务端

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Netty的TCP协议的Socket服务端

01 引言

上一节分享Websocket独立部署的一个设计思路,我们今天接着聊一下基于Netty的TCP协议的Socket服务端如何搭建。这个对于熟悉的人可能很简单,但是对于新手或者不常用的开发者来说,可能一头雾水。

小编在初次使用Socket的时候,都是度娘一大堆,然后抄抄抄,完成自己的任务。至于为什么这么做,完全不知道。这一节将自己的理解分享并记录下来,以备不时之需。

02 服务端案例

2.1 代码展示

publicvoidstart(){// 创建线程组EventLoopGroupbossGroup=newNioEventLoopGroup(1);EventLoopGroupworkGroup=newNioEventLoopGroup(5);try{// 服务端类ServerBootstrapserverBootstrap=newServerBootstrap();// 添加组serverBootstrap.group(bossGroup,workGroup);// 设置NioServerSocketChannel通道serverBootstrap.channel(NioServerSocketChannel.class);// 连接队列大小serverBootstrap.option(ChannelOption.SO_BACKLOG,1024)// 保持连接serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE,true);serverBootstrap.childHandler(newChannelInitializer<SocketChannel>(){@OverrideprotectedvoidinitChannel(SocketChannelsocketChannel)throwsException{// 设置处理器链,依次执行ChannelPipelinepipeline=socketChannel.pipeline();pipeline.addLast(newDelimiterBasedFrameDecoder(2048,Unpooled.copiedBuffer("_".getBytes())));pipeline.addLast(newStringDecoder(StandardCharsets.UTF_8));pipeline.addLast(newStringEncoder(StandardCharsets.UTF_8));// 自定义的handler,处理业务逻辑pipeline.addLast(newBusinessHandler<>());}});// 配置完成,开始绑定server,通过调用sync同步方法阻塞直到绑定成功ChannelFuturechannelFuture=serverBootstrap.bind(9091).sync();log.info("Server started and listen on:{}",channelFuture.channel().localAddress());// 对关闭通道进行监听channelFuture.channel().closeFuture().sync();}catch(Exceptione){log.error("信息异常:",e);}finally{bossGroup.close();workGroup.close();}}

2.2 创建线程组

// 创建线程组EventLoopGroupbossGroup=newNioEventLoopGroup(1);EventLoopGroupworkGroup=newNioEventLoopGroup(5);

EventLoopGroup就是一个线程池。在NettySocket中需要两个不同的线程池,分别处理不同的任务。其中bossGroup用来接收客户端连接,通常设置1个线程,而workGroup用来处理I/O操作和业务逻辑,可以根据CPU的核心数指定。

2.3 创建服务端

// 服务端类ServerBootstrapbootstrap=newServerBootstrap();// 添加组serverBootstrap.group(bossGroup,workGroup);// 设置NioServerSocketChannel通道serverBootstrap.channel(NioServerSocketChannel.class);// 连接队列大小serverBootstrap.option(ChannelOption.SO_BACKLOG,1024)// 保持连接serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE,true);

服务端引导类创建完成之后,需要设置参数:

  • group():添加线程池组,第一个参数为bossGroup,第二个是workGroup
  • channel():设置NioServerSocketChannel通道
  • option():配置服务端监听的ServerSocketChannel
  • childOption():配置客户端连接的SocketChannel

所以这里的ChannelOption.SO_BACKLOG只能设置在option中,用来指定服务端接收的任务最大队列。ChannelOption.SO_KEEPALIVE用来TCP保活机制,检测死连接,是针对客户端的连接,所以需要设置在childOption上。

ChannelOption.SO_KEEPALIVE也可以不同设置,采用心跳机制来保活。其使用有一定的局限性,通常都会通过心跳机制来代替。

2.4 设置处理链

serverBootstrap.childHandler(newChannelInitializer<SocketChannel>(){@OverrideprotectedvoidinitChannel(SocketChannelsocketChannel)throwsException{// 设置处理器链,依次执行ChannelPipelinepipeline=socketChannel.pipeline();pipeline.addLast("...");pipeline.addLast("...");}});

采用责任链模式,每个处理器处理特定任务,依次执行。里面具体的Handler单独说明。

2.5 绑定端口,同步阻塞

// 配置完成,开始绑定server,通过调用sync同步方法阻塞直到绑定成功ChannelFuturechannelFuture=serverBootstrap.bind(9091).sync();// 对关闭通道进行监听channelFuture.channel().closeFuture().sync();

当前服务端绑定一个端口,客户端就可以通过当前端口连接,并同步阻塞,等待端口绑定成功。最后同步阻塞等待通过关闭。

03 消息处理

消息的处理是接受和推送消息重要部分。Netty框架提供了丰富的处理器,我们可以选择适合自己的处理器。处理器都是实现io.netty.channel.ChannelInboundHandler接口

3.1 框架自带编解码器

Netty框架下的Socket数据传输,默认都是ByteBuf(字节缓冲)。我们使用的时候自然想通过常用的字符串传输,而Netty自然帮我们提供了字符串相关的编解码处理器。

  • io.netty.handler.codec.string.StringDecoder
  • io.netty.handler.codec.string.StringEncoder

通过源码我们可以看到注释:

StringDecoder是将ByteBuf转成字符串的解码器,但是在处理之前必须使用ByteToMessageDecoder先解码,子类包括:

  • io.netty.handler.codec.DelimiterBasedFrameDecoder:分隔符分割
  • io.netty.handler.codec.FixedLengthFrameDecoder:固定长度分割
  • io.netty.handler.codec.LengthFieldBasedFrameDecoder:按照字段长度分割
  • io.netty.handler.codec.LineBasedFrameDecoder:按行分割

这几种方式都是有效防止拆包、粘包的方法。

按照注释的案例,我们就可以配置。而StringEncoder是用来发送消息的解码器,用来将字符串转成ByteBuf

我们这里采用分隔符的方式分割:

pipeline.addLast(newDelimiterBasedFrameDecoder(2048,Unpooled.copiedBuffer("_".getBytes())));pipeline.addLast(newStringDecoder(StandardCharsets.UTF_8));pipeline.addLast(newStringEncoder(StandardCharsets.UTF_8));

而其中DelimiterBasedFrameDecodermaxFrameLength参数用来控制接收消息的最大字节大小,超过就会异常。

3.2 自定义业务处理器

自定义业务处理器是用来处理客户端连接以及消息的。

@Slf4jpublicclassBusinessHandlerextendsSimpleChannelInboundHandler{@OverridepublicvoidhandlerAdded(ChannelHandlerContextctx)throwsException{// 建立客户端Channelchannel=ctx.channel();log.info("Socket客户端建立连接:channelId={}",channel.id());}@OverridepublicvoidhandlerRemoved(ChannelHandlerContextctx)throwsException{// 断开链接Channelchannel=ctx.channel();log.info("Socket客户端断开连接:channelId={}",channel.id());}@OverrideprotectedvoidchannelRead0(ChannelHandlerContextctx,Objectmsg)throwsException{// 接受消息Channelchannel=ctx.channel();log.info("Socket收到来自通道channelId[{}]发送的消息:{}",channel.id(),msg);// 通过WebSocket将方法发送给客户端channel.writeAndFlush(msg+"789_000");}@OverridepublicvoidexceptionCaught(ChannelHandlerContextctx,Throwablecause)throwsException{log.info("异常:",cause);}}

handlerAdded()

客户端建立连接之后会触发该方法。可以通过ctx.channel()获取来连接的通道(客户端)。连接的通常可以通过io.netty.channel.group.ChannelGroup收集。

ChannelGroupchannelGroup=newDefaultChannelGroup(GlobalEventExecutor.INSTANCE)channelGroup.add(channel);

向客户端发送消息时,可以通过channelGroup.writeAndFlush()统一给客户端发送消息。

handlerRemoved()

客户端断开连接的时触发,可以通过channelGroup.remove(channel)移除已经关闭的客户端通道

channelRead0()

接收客户端消息的重要方法,通过channel.writeAndFlush()可以直接向客户端发送消息

exceptionCaught()

处理异常的方法

3.3 客户端测试

从图上可以看出,介绍的方法都被触发了。

从图可以看出客户端的也接收到服务端的消息了。

注意

客户端发送的消息:foo test…_

服务端发送的消息:foo test…789_000

客户端接受的消息:foo test…789

客户端和服务端接收到的消息都通过_截断的

04 小结

简单的服务端搭建就已经好了,但是实际应用的时候,还需要考虑心跳机制、以及无效客户端的清理等。TCP协议服务端的介绍就到这里,客户端我们下一期介绍。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/19 3:37:07

3分钟搞定专业年会抽奖:log-lottery 3D球体抽奖系统完全指南

3分钟搞定专业年会抽奖&#xff1a;log-lottery 3D球体抽奖系统完全指南 【免费下载链接】log-lottery &#x1f388;&#x1f388;&#x1f388;&#x1f388;年会抽奖程序&#xff0c;threejsvue3 3D球体动态抽奖应用。 项目地址: https://gitcode.com/gh_mirrors/lo/log-l…

作者头像 李华
网站建设 2026/6/15 21:08:47

如何用EventSource实现AI实时数据推送?

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个使用EventSource实现AI实时数据推送的Web应用。前端使用HTMLJavaScript监听EventSource事件&#xff0c;后端使用Node.js提供SSE接口&#xff0c;集成AI模型对输入数据进行…

作者头像 李华
网站建设 2026/6/19 9:43:20

AI微调民主化:预配置镜像如何降低Llama Factory门槛

AI微调民主化&#xff1a;预配置镜像如何降低Llama Factory门槛 想尝试用大模型做点有趣的事情&#xff0c;却被复杂的安装配置劝退&#xff1f;作为技术布道师&#xff0c;我深知环境搭建是阻碍AI技术普及的第一道门槛。本文将介绍如何通过预配置镜像快速上手Llama Factory&a…

作者头像 李华
网站建设 2026/6/11 3:09:28

RtAudio跨平台音频库终极指南:快速上手与最佳配置方案

RtAudio跨平台音频库终极指南&#xff1a;快速上手与最佳配置方案 【免费下载链接】rtaudio A set of C classes that provide a common API for realtime audio input/output across Linux (native ALSA, JACK, PulseAudio and OSS), Macintosh OS X (CoreAudio and JACK), an…

作者头像 李华
网站建设 2026/6/20 20:26:48

libgo协程库终极指南:C++高性能并发编程快速上手

libgo协程库终极指南&#xff1a;C高性能并发编程快速上手 【免费下载链接】libgo Go-style concurrency in C11 项目地址: https://gitcode.com/gh_mirrors/li/libgo 在当今高并发应用场景下&#xff0c;libgo协程库作为一款专为C11设计的stackful协程解决方案&#xf…

作者头像 李华
网站建设 2026/6/9 19:39:46

懒人必备:10分钟用Llama Factory和云端GPU搞定模型微调

懒人必备&#xff1a;10分钟用Llama Factory和云端GPU搞定模型微调 作为一名独立开发者&#xff0c;想要尝试微调Llama模型却苦于环境配置的繁琐&#xff1f;本文将带你快速上手使用Llama Factory工具&#xff0c;在云端GPU环境下10分钟内完成模型微调&#xff0c;无需操心复杂…

作者头像 李华