news 2026/3/27 21:18:19

基于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/3/27 8:25:02

为什么你的OCR识别不准?试试这个集成图像预处理的CRNN镜像

为什么你的OCR识别不准&#xff1f;试试这个集成图像预处理的CRNN镜像 &#x1f4d6; 项目简介&#xff1a;高精度通用 OCR 文字识别服务&#xff08;CRNN版&#xff09; 在数字化转型加速的今天&#xff0c;OCR&#xff08;光学字符识别&#xff09;技术已成为信息提取的核心工…

作者头像 李华
网站建设 2026/3/26 16:56:27

吐血推荐8个AI论文写作软件,专科生搞定毕业论文!

吐血推荐8个AI论文写作软件&#xff0c;专科生搞定毕业论文&#xff01; 专科生的毕业论文救星&#xff0c;AI工具如何改变写作方式 对于很多专科生来说&#xff0c;毕业论文是一道难以逾越的难关。从选题到撰写&#xff0c;再到查重降重&#xff0c;每一步都充满了挑战。而随着…

作者头像 李华
网站建设 2026/3/17 1:48:31

THREE.JS开发效率翻倍:AI工具vs传统开发对比

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个THREE.JS开发效率对比工具&#xff0c;功能包括&#xff1a;1. 传统方式开发3D场景的步骤模拟 2. AI辅助开发的流程演示 3. 耗时统计和对比图表 4. 代码质量分析 5. 性能测…

作者头像 李华
网站建设 2026/3/21 7:17:12

CNPM一键配置工具开发实录

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 构建一个CNPM一键配置原型工具&#xff0c;功能包括&#xff1a;1.图形化界面选择配置项2.自动生成安装脚本3.环境检测4.错误自动修复。要求使用Electron框架&#xff0c;界面简洁…

作者头像 李华
网站建设 2026/3/27 14:01:00

Webots机器人仿真平台:构建智能系统的完整解决方案

Webots机器人仿真平台&#xff1a;构建智能系统的完整解决方案 【免费下载链接】webots Webots Robot Simulator 项目地址: https://gitcode.com/gh_mirrors/web/webots Webots是一款开源的专业级机器人仿真平台&#xff0c;为机器人技术、自动驾驶和人工智能研究提供全…

作者头像 李华
网站建设 2026/3/27 13:49:31

IDM序列号管理工具:提升团队协作效率

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个IDM序列号管理工具&#xff0c;支持团队协作功能。工具应允许管理员分配序列号给团队成员&#xff0c;跟踪使用情况&#xff0c;并在序列号即将到期时发送提醒。界面友好&…

作者头像 李华