news 2026/7/2 0:46:01

【看完不亏系列】spring boot 使用tcp协议接收客户端发送的xml数据,并处理数据

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【看完不亏系列】spring boot 使用tcp协议接收客户端发送的xml数据,并处理数据

spring boot 使用tcp协议接收客户端发送的xml数据,并处理数据

    • 1.创建tcp 服务器,负责启动ServerSocket,接收连接
    • 2.创建tcp 客户端,负责处理客户端连接请求数据

1.创建tcp 服务器,负责启动ServerSocket,接收连接

importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.ApplicationContext;importorg.springframework.stereotype.Component;importjavax.annotation.PostConstruct;importjavax.annotation.PreDestroy;importjava.net.InetAddress;importjava.net.ServerSocket;importjava.net.Socket;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;/** * tcp 服务器,负责启动ServerSocket,接收连接 */@Slf4j@ComponentpublicclassTcpServerService{/** * 本tcp服务器端口 */privatestaticfinalintPORT=8090;privateServerSocketserverSocket;privatevolatilebooleanrunning=true;/** * 正式环境需要换成自定义线程池,这里为了方便演示 */privatefinalExecutorServicethreadPool=Executors.newCachedThreadPool();/** * spring 允许我们直接注入spring 应用上下文,方便获取Bean */@AutowiredprivateApplicationContextapplicationContext;/** * 项目启动就执行 */@PostConstructpublicvoidstart(){//在独立线程中启动服务器,避免阻塞 Spring启动newThread(()->{try{// 参考我的另一篇文章获取IpUtilInetAddressbindAddress=InetAddress.getByName(IpUtil.getLocalIp());serverSocket=newServerSocket(PORT,50,bindAddress);log.info("tcp服务器:tcp 服务器启动在 {}的{}端口",bindAddress.getHostName(),PORT);while(running){SocketclientSocket=serverSocket.accept();log.info("tcp服务器:新客户端连接请求:{}",clientSocket);//从Spring 容器中获取一个新的 clientHander实例 (原型Bean)ClientHanderclientHander=applicationContext.getBean(ClientHander.class);clientHander.setSocket(clientSocket);threadPool.submit(clientHander);}}catch(Exceptione){if(running){log.error("tcp服务器:tcp服务器异常",e);}}}).start();}@PreDestroypublicvoidstop(){running=false;try{if(serverSocket!=null&&!serverSocket.isClosed()){serverSocket.close();}}catch(Exceptione){log.error("tcp服务器:关闭server socket 异常",e);}}}

2.创建tcp 客户端,负责处理客户端连接请求数据

importio.swagger.annotations.Scope;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;importjava.io.*;importjava.net.Socket;importjava.nio.charset.StandardCharsets;importjava.util.LinkedHashMap;importjava.util.regex.Matcher;importjava.util.regex.Pattern;@Slf4j@Component@Scope(name="prototype",description="原型Bean,处理单条连接,每次连接创建一个新的实例")publicclassClientHanderimplementsRunnable{/** * 由TcpServerService 注入 */privateSocketsocket;privatestaticfinalintBUFFER_SIZE=8192;//设置超时时间60秒privatestaticfinalintTIME_OUT=60_000;publicvoidsetSocket(Socketsocket){this.socket=socket;}/** * 接收客户端发过来的xml数据 * <pre>固定6位长度头如:长度是内容.length * {@code 000099<Message><Message>} * </pre> */@Overridepublicvoidrun(){log.info("ClientHander接收客户端发过来的xml数据");//使用 try-with-resource 自动关闭流和socket//使用转换流将字节流转换成字符流try(BufferedReaderreader=newBufferedReader(newInputStreamReader(socket.getInputStream(),StandardCharsets.UTF_8));PrintWriterwriter=newPrintWriter(newOutputStreamWriter(socket.getOutputStream(),StandardCharsets.UTF_8),true)){//设置超时时间socket.setSoTimeout(TIME_OUT);StringheadStr=readFiexLengthString(reader,6);intcontentLength;try{contentLength=Integer.parseInt(headStr);}catch(NumberFormatExceptione){thrownewIOException("无效的长度格式:"+headStr,e);}if(contentLength<=0||contentLength>1_000_000){thrownewIOException("非法长度:"+contentLength);}//读取指定长度内容StringrequestXml=readFiexLengthString(reader,contentLength);log.info("读取到接收到的数据长度及数据:{}{}",headStr,requestXml);//调用业务处理逻辑Stringresponse=porcessRequest(requestXml);if(response.length()<=0||response.length()>1_000_000){thrownewIOException("非法响应长度,无法用6位长度表示:"+contentLength);}//发送响应writer.write(response);writer.flush();if(writer.checkError()){log.error("响应发送失败,可能客户端已经断开");}else{log.info("响应发送成功,长度:{}",response.length());}//通知客户端响应结束,但不立即关闭socket.shutdownOutput();log.info("发送响应数据:{}",response);}catch(Exceptione){log.error("错误处理client",e);}}/** * 读取指定长度 * 为什么用循环,而不是一次 read? * Reader.read(char[], int, int) 方法不保证一次读完请求的全部字符(哪怕流还没有结束)。它可能因为内部缓冲区大小、系统调用限制等原因,每次只返回一部分数据。 * 因此,需要循环多次调用,并将每次读到的数据拼接到 buffer 的正确位置,直到凑满 length 个字符。 * @param reader * @param length * @return * @throws IOException */privateStringreadFiexLengthString(Readerreader,intlength)throwsIOException{char[]buffer=newchar[length];intoffset=0;while(offset<length){intread=reader.read(buffer,offset,length-offset);//当循环中读取的总字符数达到 length 时,循环结束,不再调用 read,所以不会看到 -1。// 只有当流中数据不足 length 时,最后一次 read 才会返回 -1,从而触发异常。// 所以代码不返回 -1 是因为流中数据足够,没有触发提前结束if(read==-1){thrownewIOException("流提前结束,预期"+length+"字符,实际收到"+offset);}offset+=read;}returnnewString(buffer);}/** * 业务处理:根据请求内容调用不同的Service<br/> * 有新的需求只需要加一个策略就行porcessRequest方法代码一行都不用动 * @param requestXml * @see ServiceCodeEnum * @return */publicStringporcessRequest(StringrequestXml){// todo 你们的业务处理逻辑}/** * 去除前6位数字,如果有的话 * * @param str * @return */privateStringremoveFirstSixNumber(Stringstr){Patternpattern=Pattern.compile("^\\d{6}");Matchermatcher=pattern.matcher(str);if(matcher.find()){//去掉前6位returnstr.substring(6);}returnstr;}}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/27 19:23:11

PR 即时反馈工作流:Claude Code + GitHub CLI 实现智能代码审查闭环

开篇:一个让所有技术Leader失眠的问题 “PR积压越来越多,每个人都忙着赶需求,Code Review变成了走过场——点开PR,看一眼改动,评论个‘LGTM’,merge掉。两周后,某个‘已Review’的PR在生产环境出了问题,排查下来发现是一个并发安全漏洞,根本原因在PR里写得明明白白,…

作者头像 李华
网站建设 2026/6/27 19:22:49

一个 @Async 让循环依赖暴雷:Spring 代理的暗坑

一个 Async 让循环依赖暴雷&#xff1a;Spring 代理的暗坑项目跑了半年没报错&#xff0c;加了 Async 之后启动直接炸&#xff0c;BeanCreationException。循环依赖一直都在&#xff0c;只是 Async 把它从"隐藏"变成了"致命"。一、事故现场 周三上线一个新…

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

基于Chrome140的Threads账号自动化——需求分析环境搭建(一)

引言 随着社交媒体营销的兴起&#xff0c;Threads作为全球最大的社区论坛平台之一&#xff0c;已成为企业和个人进行品牌推广、社区运营的重要阵地。然而&#xff0c;手动管理Threads账号很是麻烦&#xff0c;包括发帖、回复、投票、社区互动等繁琐操作。RPA&#xff08;机器人…

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

性能分析贴身助手:Claude Code 自动化解析火焰图并给出精准优化建议

引言:当火焰图不再是“天书” 周五下午四点,你的服务P99延迟突然飙到2.3秒。你打开性能剖析器,面对一片红黄交织的火焰图——每个函数调用栈都像一团乱麻。你花了两个小时逐层展开,终于定位到一个嵌套循环在处理大批量数据时触发了O(n)复杂度。但这是对的吗?还有没有更深…

作者头像 李华
网站建设 2026/6/27 19:13:36

RDP Wrapper终极指南:让Windows家庭版支持多用户远程桌面

RDP Wrapper终极指南&#xff1a;让Windows家庭版支持多用户远程桌面 【免费下载链接】rdpwrap RDP Wrapper Library 项目地址: https://gitcode.com/gh_mirrors/rd/rdpwrap 你是否在使用Windows家庭版时&#xff0c;渴望像专业版一样享受多用户远程桌面功能&#xff1f…

作者头像 李华
网站建设 2026/6/29 10:34:54

电缆在线监测系统开发指南:从原理到应用

一、产品概述地下电力电缆是电网与工业生产的电能大动脉。沃伦森WRS-CBS11X系统依托自研行波检测与多模态传感融合技术&#xff0c;实现电缆全维度状态实时感知、隐患提前预警、故障精准定位。七大监测维度&#xff1a;1.电压电流暂态行波采集&#xff08;50MHz高频采样&#x…

作者头像 李华