简单的springboot 编写Socket服务接口

简单的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 条数据信息
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

洛谷 P11045 [蓝桥杯 2024 省 Java B] 最优分组

[Problem Discription] \color{blue}{\texttt{[Problem Discription]}} [Problem Discription] [Analysis] \color{blue}{\texttt{[Analysis]}} [Analysis] 首先得注意这么一点&#xff1a; k k k 必须得是 n n n 的因数&#xff08;这里的 n , k n,k n,k 对应于题目的 N ,…

量化交易backtrader实践(三)_指标与策略篇(2)_内置指标A开头

在第1节中&#xff0c;我们学习了移动平均线的原理&#xff0c;中位数以及正态分布的概念&#xff0c;并通过python手工做了一个双均线的策略回测。了解了怎么用pandas计算移动平均线&#xff08;rollingmean)&#xff0c;怎么得到某一列上1个的值&#xff08;shift)&#xff0…

【算法与图】通向高效解决方案的钥匙

文章目录 遍历算法BFS&#xff08;广度优先遍历&#xff09;1. 什么是 BFS&#xff1f;2. 特点和应用3. BFS 示例 DFS&#xff08;深度优先搜索&#xff09;1. 什么是 DFS&#xff1f;2. DFS 的基本步骤3. 特点4. DFS 的应用5. DFS 示例 最小生成树问题1. 什么是最小生成树&…

【mmengine】优化器封装(OptimWrapper)(入门)优化器封装 vs 优化器

MMEngine 实现了优化器封装&#xff0c;为用户提供了统一的优化器访问接口。优化器封装支持不同的训练策略&#xff0c;包括混合精度训练、梯度累加和梯度截断。用户可以根据需求选择合适的训练策略。优化器封装还定义了一套标准的参数更新流程&#xff0c;用户可以基于这一套流…

虚拟机三种网络模式详解

在电脑里开一台虚拟机&#xff0c;是再常见不过的操作了。无论是用虚拟机玩只有旧版本系统能运行的游戏&#xff0c;还是用来学习Linux、跑跑应用程序都是很好的。而这其中&#xff0c;虚拟机网络是绝对绕不过去的。本篇文章通俗易懂的介绍了常见的虚拟网络提供的三种网络链接模…

数据结构-3.5.队列的顺序实现

一.队列的顺序实现&#xff0c;初始化操作以及判断队列是否为空&#xff1a; 1.图解&#xff1a; 2.代码&#xff1a; #include<stdio.h> #define MaxSize 10 //定义一个队列最多存储的元素个数 ​ typedef struct {int data[MaxSize]; //用静态数组存放队列元素int f…

【springboot】整合沙箱支付

目录 1. 配置沙箱应用环境2. 配置springboot项目1. 引入依赖2. 配置文件注册下载ngrok 3. 创建支付宝支付服务类4. 支付界面模板5. 控制类实现支付6. 测试 1. 配置沙箱应用环境 使用支付宝账号登录到开放平台控制台。 使用支付宝登录后&#xff0c;看到以下页面&#xff0c;下…

MFC工控项目实例二十二主界面计数背景颜色改变

承接专栏《MFC工控项目实例二十一型号选择界面删除参数按钮禁用切换》 1、在SEAL_PRESSUREDlg.h文件中添加代码 class CSEAL_PRESSUREDlg : public CDialog { public: CBrush m_brush1;CBrush m_brush2;CBrush m_brush3;... } 2、在SEAL_PRESSUREDlg.cpp文件中添加代码 BO…

在2核2G服务器安装部署MySQL数据库可以稳定运行吗?

阿里云2核2G服务器可以安装MySQL数据库吗&#xff1f;当然可以&#xff0c;并且可以稳定运行MySQL数据库&#xff0c;目前阿里云服务器网aliyunfuwuqi.com使用的就是阿里云2核2G服务器&#xff0c;在云服务器上安装MySQL数据库&#xff0c;可以稳定运行。 目前阿腾云用于运行M…

查看 git log的过程中看到 :说明日志输出可能超出屏幕大小,系统进入了分页模式

在命令行提示符中&#xff0c;通常 : 表示系统等待进一步的输入。如果你在查看 git log 的过程中看到 :&#xff0c;说明日志输出可能超出屏幕大小&#xff0c;系统进入了分页模式&#xff0c;默认使用 less 命令查看内容。 此时你可以&#xff1a; 按 q 退出日志查看。按 En…

算法笔记(五)——分治

文章目录 算法笔记&#xff08;五&#xff09;——分治快排颜色分类排序数组数组中的第K个最大元素库存管理 III 归并排序数组交易逆序对的总数计算右侧小于当前元素的个数翻转对 算法笔记&#xff08;五&#xff09;——分治 分治算法字面上的解释是“分而治之”&#xff0c;就…

Python 从入门到实战32(数据库MySQL)

我们的目标是&#xff1a;通过这一套资料学习下来&#xff0c;通过熟练掌握python基础&#xff0c;然后结合经典实例、实践相结合&#xff0c;使我们完全掌握python&#xff0c;并做到独立完成项目开发的能力。 上篇文章我们讨论了数据库编程接口操作的相关知识。今天我们将学习…

【框架篇】过滤器和拦截器的区别以及使用场景

在项目开发中&#xff0c;常常会同时配置拦截器&#xff08;Interceptor&#xff09;和过滤器&#xff08;Filter&#xff09;&#xff0c;以下就是它们两个主要的区别&#xff1a; 过滤器&#xff08;Filter&#xff09; 配置和实现 Filter的实现还是很简单的&#xff0c;可…

【微服务】组件、基础工程构建(day2)

组件 服务注册和发现 微服务模块中&#xff0c;一般是以集群的方式进行部署的&#xff0c;如果我们调用的时候以硬编码的方式&#xff0c;那么当服务出现问题、服务扩缩容等就需要对代码进行修改&#xff0c;这是非常不好的。所以微服务模块中就出现了服务注册和发现组件&…

计算机毕业设计 基于Python的广东旅游数据分析系统的设计与实现 Python+Django+Vue Python爬虫 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

华为云+WordPress+Puock主题搭建个人博客

网站访问地址&#xff1a;qingxuly.cn 搭建网站 购买华为云服务器&#xff0c;购买域名&#xff0c;进行备案&#xff0c;配置域名解析等操作&#xff0c;请参考华为云文档。 安装Ubuntu系统 华为云控制台中给云服务器安装Ubuntu2204。 配置服务器安全组 华为云安全组中创建安…

【嵌入式系统】第18章 脉宽调试器(PWM)

目录 18.1 结构框图 18.3 功能说明 18.3.4 PWM 信号发生器 18.3.5 死区发生器 18.3.6 中断/ADC 触发选择器 18.3.7 同步方法 18.3.8 故障条件 18.3.9 输出控制块 LES 硬件介绍&#xff08;12&#xff09;正交编码接口QEI 19.1 结构框图 19.2 信号描述 19.3 功能说明…

GPG error golang 1.19

1. 问题描述及原因分析 在飞腾2000的服务器&#xff0c;OS为Kylin Linux Advanced Server release V10环境下&#xff0c;docker版本为18.09.0&#xff08;docker-engine-18.09.0-101.ky10.aarch64&#xff09;&#xff0c;基于容器镜像golang:1.19编译新的容器镜像&#xff0…

C++黑暗迷宫

目录 开头程序程序的流程图程序游玩的效果下一篇博客要说的东西 开头 大家好&#xff0c;我叫这是我58。 程序 #include <iostream> #include <cstdlib> #include <ctime> using namespace std; struct near {int i;int ia;int ix;int iy;int iwalk; }; v…

22.1 k8s不同role级别的服务发现

本节重点介绍 : 服务发现的应用3种采集的k8s服务发现role 容器基础资源指标 role :nodek8s服务组件指标 role :endpoint部署在pod中业务埋点指标 role :pod 服务发现的应用 所有组件将自身指标暴露在各自的服务端口上&#xff0c;prometheus通过pull过来拉取指标但是promet…