简单的springboot 编写Socket服务接口

news2024/11/17 3:36:26

简单的springboot 编写Socket服务接口

1.需求

我们项目中有部分老接口为票据接口,其中实现为java socket形式进行实现,但是其中大部分信息都是原始公司封装的包进行实现的,想要修改非常费劲,所以此处简单了解了一下socket,自己简单的 编写了两个测试接口,方便以后如果需要自己添加socket接口,可以快速编写。

2. 简单实现

编写的接口为测试接口,整体结构相对简单,主要就是客户端发起一个请求,请求信息前6位为请求串长度,其余为请求的请求体,发送信息到服务端后,服务端使用线程池异步处理信息,最终返回处理之后的响应信息,客户端则接收响应信息,同样的步骤处理响应信息,前6位为响应信息长度,然后解析响应信息即可,因为为简单案例,所以没有进行数据通信加密。

2.1 客户端实现

客户端代码相对简单,直接写入到controller当中了,具体实现代码如下:

package cn.git.controller;

import cn.git.entity.Product;
import cn.git.socket.SocketUtil;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.math.BigDecimal;
import java.net.Socket;

/**
 * @description: Socket测试controller
 * @program: bank-credit-sy
 * @author: lixuchun
 * @create: 2023-03-20
 */
@RestController
@RequestMapping("/socket")
public class SocketController {

	/**
	 * 异步发送200个请求,模拟多用户
	 */
    @GetMapping("/client")
    public String client() {
    	// 异步发送200个请求,模拟多用户
        for (int i = 0; i < 200; i++) {
            int finalI = i;
            new Thread(()-> {
                try {
                    // 创建Socket对象
                    Socket socket = new Socket("localhost", 7777);
                    // 设置超时时间
                    socket.setSoTimeout(60000);

                    // 测试产品
                    Product product = new Product();
                    product.setAmount(new BigDecimal(finalI));
                    product.setCycle(12);
                    product.setEndTime("2018-08-08");
                    product.setName("test");
                    product.setRate(new BigDecimal(1));
                    product.setRaised(new BigDecimal(0));

                    // 拼接请求报文
                    String message = JSONObject.toJSONString(product);
                    String reqLengthStr = SocketUtil.leftFixedZero(6, message.length());

                    // 发送请求报文
                    PrintStream out = new PrintStream(socket.getOutputStream());
                    out.println(reqLengthStr.concat(message));

                    // 获取服务端返回的消息长度信息
                    BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    char[] lengthByte = new char[6];
                    in.read(lengthByte);
                    String rspLengthStr = new String(lengthByte);
                    int responseLength = Integer.parseInt(rspLengthStr);

                    // 获取服务端返回的消息体信息
                    char[] responseByte = new char[responseLength];
                    in.read(responseByte);
                    String responseBody = new String(responseByte);

                    // 打印返回结果
                    System.out.println("返回结果为 : ".concat(responseBody));
                    socket.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }

        return "处理成功啦";
    }

}

2.2 服务端代码

服务端代码相对复杂一些,主要有socket服务初始化,公共线程池,工具类以及接口处理handle类。具体实现如下:

  • socket初始化类

    package cn.git.socket;
    
    import cn.git.mapper.ProductMapper;
    import cn.git.socket.handler.SocketHandler;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.PostConstruct;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * @description: socket接口入口信息
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2023-03-20
     */
    @Slf4j
    @Component
    public class CustomSocketServer {
    
        @Autowired
        private ProductMapper productMapper;
    	
    	/**
    	 * 初始化调用接口
    	 * 
    	 * 异步启动socket监听服务,端口 7777
    	 */
        @PostConstruct
        public void socketServerInit() throws IOException {
            new Thread(() -> {
                try {
                    // 监听7777端口
                    log.info("开始启动socket服务信息,端口监听 7777");
                    ServerSocket serverSocket = new ServerSocket(7777);
    
                    // 循环监听
                    while (true) {
                        log.info("等待客户端连接...");
                        Socket clientSocket = serverSocket.accept();
                        ThreadPoolUtil.THREAD_POOL.execute(
                            // 构建handler
                            SocketHandler.builder().clientSocket(clientSocket).productMapper(productMapper).build()
                        );
                        log.info("客户端连接成功,当前连接数:{}", ThreadPoolUtil.THREAD_POOL.getActiveCount());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
    
    
  • 通用线程池相关类
    自定义线程池工厂实现如下

    package cn.git.socket;
    
    import cn.hutool.core.util.StrUtil;
    
    import java.util.concurrent.ThreadFactory;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * 自定义线程池工厂
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2021-12-25
     */
    public class OnlineThreadFactory implements ThreadFactory {
    
        /**
         * 自增线程序号
         */
        private final AtomicInteger threadNumber = new AtomicInteger(1);
    
        /**
         * 线程名称前缀
         */
        private final String threadNamePrefix;
    
        /**
         * 构造方法
         * @param threadNamePrefix 方法前缀
         */
        public OnlineThreadFactory(String threadNamePrefix) {
            this.threadNamePrefix = threadNamePrefix.concat(StrUtil.DASHED);
        }
    
        /**
         * Constructs a new {@code Thread}.  Implementations may also initialize
         * priority, name, daemon status, {@code ThreadGroup}, etc.
         * @param runnable a runnable to be executed by new thread instance
         * @return constructed thread, or {@code null} if the request to
         * create a thread is rejected
         */
        @Override
        public Thread newThread(Runnable runnable) {
            // 设置线程池名称
            Thread thread = new Thread(runnable , threadNamePrefix.concat(StrUtil.toString(threadNumber.getAndIncrement())));
    
            // 设置守护线程
            if (thread.isDaemon()) {
                thread.setDaemon(false);
            }
    
            // 同意设置程默认优先级 5
            if (thread.getPriority() != Thread.NORM_PRIORITY) {
                thread.setPriority(Thread.NORM_PRIORITY);
            }
            return thread;
        }
    }
    
    

    线程池工具类

    package cn.git.socket;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @description: 线程池工具类
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2022-08-16 10:58:07
     */
    public class ThreadPoolUtil {
    
        /**
         * 线程池线程名称
         */
        private static final String DICS_THREAD_POOL_PREFIX = "DICS-SOCKET";
    
        /**
         * 超时时间 单位毫秒
         */
        private static final int REQ_TIME_OUT = 10 * 1000;
    
        /**
         * 阻塞队列大小
         */
        private static final int QUEUE_SIZE = 200;
    
        /**
         * 核心线程池数量
         */
        private static final int CORE_THREAD_NUM = 5;
    
        /**
         * 最大线程池数量
         */
        private static final int MAX_THREAD_NUM = 20;
    
        /**
         * 线程池构造参数
         */
        public static ThreadPoolExecutor THREAD_POOL = new ThreadPoolExecutor(CORE_THREAD_NUM,
                MAX_THREAD_NUM,
                REQ_TIME_OUT,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(QUEUE_SIZE),
                new OnlineThreadFactory(DICS_THREAD_POOL_PREFIX));
    }
    
    
  • 业务处理handle类

    package cn.git.socket.handler;
    
    import cn.git.entity.Product;
    import cn.git.mapper.ProductMapper;
    import cn.git.socket.SocketUtil;
    import cn.hutool.core.util.IdUtil;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import lombok.*;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.Socket;
    
    /**
     * @description: socket请求处理类
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2023-03-20
     */
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class SocketHandler implements Runnable {
    
        /**
         * 订单信息mapper
         */
        private ProductMapper productMapper;
    
        /**
         * 客户端socket
         */
        private Socket clientSocket;
    
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
        @SneakyThrows
        @Override
        public void run() {
            // 获取请求数据信息
            System.out.println("接收数据开始处理!");
            BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
    
            // 读取数据前6位,获取请求数据长度
            char[] bodyBytes = new char[6];
            in.read(bodyBytes);
            String dataLengthStr = new String(bodyBytes);
    
            // 获取请求数据信息
            Integer dataLength = Integer.parseInt(dataLengthStr);
            System.out.println("请求数据长度:" + dataLength);
    
            bodyBytes = new char[dataLength];
            in.read(bodyBytes);
            String requestBodyInfo = new String(bodyBytes);
            System.out.println("请求数据:" + requestBodyInfo);
    
            // 请求数据转换为Person对象
            Product product = JSON.parseObject(requestBodyInfo, Product.class);
            product.setId(IdUtil.simpleUUID());
            productMapper.insert(product);
    
            // 响应数据
            String rspJSONInfo = JSONObject.toJSONString(product);
    
            // 响应数据长度标识位 eg: 000667
            String prefixLength = SocketUtil.leftFixedZero(6, rspJSONInfo.length());
    
            // 最终响应数据
            String finalRspInfo = prefixLength.concat(rspJSONInfo);
            System.out.println("响应数据:" + finalRspInfo);
            out.println(finalRspInfo);
        }
    }
    
    
  • socket工具类

    package cn.git.socket;
    
    /**
     * @description: socket工具类
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2023-03-20
     */
    public class SocketUtil {
    
        /**
         * 左补0
         * eg: length = 6, num = 123, return 000123
         *
         * @param length 长度
         * @param num    数字
         * @return
         */
        public static String leftFixedZero(int length, int num) {
            return String.format("%0" + length + "d", num);
        }
    
    }
    
    

3.测试

启动服务,观察socket监听端口 7777 是否正常启动监听,观察如下,socket服务端正常启动监听端口
在这里插入图片描述
开始模拟多客户端调用,请求 http://localhost:8088/socket/client 接口,循环异步发起 200 socket 请求。
在这里插入图片描述
观察后台信息
在这里插入图片描述
观察数据库,发现数据已经正确导入了, 成功插入了 200 条数据信息
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2172104.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

卫星导航定位原理学习(三)

GNSS信号体制及其性能分析 GNSS信号体制直接影响卫星导航系统的性能&#xff0c;是卫星导航系统设计的重要内容。卫星导航信号体制主要包括信号频率、信号结构、导航电文3部分。其中信号结构又包括调制波形、频率带宽、扩频码码长、码速率、码结构、信号功率等内容。导航电文设…

25 Vue3之如何开发移动端并适配

开发移动端最主要的就是适配各种手机 vw vh是相对viewport 视口的单位&#xff0c;配合meta标签可以直接使用&#xff0c;无需计算 1vw1/100视口宽度 1vh1/100视口高度 当前屏幕视口是375像素&#xff0c;1vw就是3.75px postCss 提供了 把Css 转换AST的能力&#xff0c;类…

LeetCode --- 416周赛

题目列表 3295. 举报垃圾信息 3296. 移山所需的最少秒数 3297. 统计重新排列后包含另一个字符串的子字符串数目 I 3298. 统计重新排列后包含另一个字符串的子字符串数目 II 一、举报垃圾信息 直接用哈希表统计bannedWords中的单词&#xff0c;遍历message中出现的垃圾信息…

WiFi无线连接管理安卓设备工具:WiFiADB

介绍 WiFi ADB 使您能够通过 WiFi TCP/IP 连接直接在设备上轻松调试和测试 Android 应用&#xff0c;无需使用 USB 数据线。在启用 WiFi 上的 ADB 后&#xff0c;打开控制台将电脑连接到设备。 手机和电脑在同一个WiFi然后电脑上运行adb connect x.x.x.x:x命令即可 下载 谷…

Go语言开发后台框架不能只有CRUD还需有算法集成基础功能-GoFly框架集成了自然语言处理(NLP)分词、关键词提取和情感分析

前言 Go语言开发框架&#xff0c;我们要把Go的优势体现在框架中&#xff0c;不仅CRUD常规操作&#xff0c;还要把常用即有算力自己集成到框架中&#xff0c;而不是去购买第三方提供服务接口。作为开发者可以拓宽自己代码面&#xff0c;获取更多成就感&#xff0c;同时也提供自…

戴尔PowerEdge R840服务器亮黄灯 不开机

最近接修到一台东莞用户的DELL PowerEdge R840 服务器因为意外断电后&#xff0c;无法正常开机的问题&#xff0c; 大概故障现象是 插上电源线 按卡机按钮无响应&#xff0c;无法开机&#xff0c;无显示输出&#xff0c;工程师到现场检修&#xff0c;经过idrac中日志分析&#…

商标是什么?为何对企业至关重要?

商标作为企业的核心标识&#xff0c;不仅是区分商品与服务的关键&#xff0c;更是企业品牌塑造、市场区分和消费者信任建立的基石。那么&#xff0c;商标究竟是什么&#xff1f;它又为何对企业如此重要呢&#xff1f; 商标的定义及类型 商标&#xff08;Trademark&#xff09;…

Python获取百度翻译的两种方法

一、引言 百度是我们常用的搜索工具&#xff0c;其翻译是与爱词霸合作&#xff0c;总体看其反应速度较快&#xff0c;可以作为项目中重要的翻译工具。根据大家的需要&#xff0c;现提供两种Python获取百度翻译的两种办法&#xff1a; 二、requests法 我们引用requests模块&a…

构建5G-TSN测试平台:架构与挑战

论文标题&#xff1a;Building a 5G-TSN Testbed: Architecture and Challenges 作者信息&#xff1a; Anna Agust-Torra, Marc Ferr-Mancebo, David Rincn-Rivera, Cristina Cervell Pastor, Sebasti Sallent-Ribes&#xff0c;来自西班牙巴塞罗那的加泰罗尼亚理工大学&…

裁剪视频如何让画质不变?一文教会你

当我们想要从一段视频中提取精华&#xff0c;裁剪视频就成了必不可少的技能。 但是&#xff0c;如何做到在裁剪过程中不损害画质&#xff0c;保持视频原有的清晰度和流畅度呢&#xff1f; 这不仅需要技巧&#xff0c;还需要对视频编辑有一定的了解。 本文将为你介绍四种裁剪…

Redis篇(数据类型)

目录 讲解一&#xff1a;简介 讲解二&#xff1a;常用 一、String类型 1. 简介 2. 常见命令 3. Key结构 4. 操作String 5. 实例 二、Hash类型 1. 简介 2. 常见命令 3. 3操作hash 4. 实例 三、List类型 1. 简介 2. 特征 3. 应用场景 4. 常见命令 5. 操作list …

13.安卓逆向-frida基础-编写hook脚本1

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要盲目相信。 工…

外国电影演员识别系统源码分享

外国电影演员识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

AI产品经理必知:核心人工智能技术概览

第一章&#xff1a;AI产品经理是否需要懂技术及其程度 在当今AI行业快速发展的背景下&#xff0c;作为一位AI产品经理&#xff0c;理解并掌握一定的AI技术知识不仅是锦上添花&#xff0c;更是不可或缺的素质。那么&#xff0c;AI产品经理究竟需要懂到何种程度的技术呢&#xf…

国内车市销量激增,理想成功超越BBA

文/王俣祺 导语&#xff1a;随着“金九银十”的到来&#xff0c;国内汽车市场迎来了一个充满活力的开局。乘用车市场的销量已经迎来新的突破&#xff0c;彰显出中国汽车市场的韧性和潜力。尤为引人注目的是&#xff0c;新能源汽车销量同样激增&#xff0c;成为推动市场增长的重…

C++--IO流

目录 1. C语言的输入与输出 2. 流是什么 3. CIO流 4 stringstream的简单介绍 1. C语言的输入与输出 C 语言中我们用到的最频繁的输入输出方式就是 scanf () 与 printf() 。 scanf(): 从标准输入设备 ( 键 盘 ) 读取数据&#xff0c;并将值存放在变量中 。 printf(): 将…

C++ : 多态

1. 多态的概念 多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会 产生出不同的状态。 举个栗子&#xff1a;比如买票这个行为&#xff0c;当普通人买票时&#xff0c;是全价买票&#xff1b;学…

通过队列实现栈

请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09;。 实现 MyStack 类&#xff1a; void push(int x) 将元素 x 压入栈顶。int pop() 移除并返回栈顶元素。int to…

基于微信小程序爱心领养小程序设计与实现(源码+定制+开发)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

基于Hive和Hadoop的电信流量分析系统

本项目是一个基于大数据技术的电信流量分析系统&#xff0c;旨在为用户提供全面的通信数据和深入的流量使用分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xff0c;以 Spark…