SpringBoot集成Sharding-JDBC-5.3.0实现按月动态建表分表
Sharding-JDBC系列
1、Sharding-JDBC分库分表的基本使用
2、Sharding-JDBC分库分表之SpringBoot分片策略
3、Sharding-JDBC分库分表之SpringBoot主从配置
4、SpringBoot集成Sharding-JDBC-5.3.0分库分表
5、SpringBoot集成Sharding-JDBC-5.3.0实现按月动态建表分表
前言
随着业务量的递增,项目产生海量的数据,在某些场景中,需要将数据按月存储。本篇基于Sharding-JDBC 5.3.0,分享一下按月自动建表以及分表的实现。
准备工作
创建一个数据库,创建一张表,表名为tb_order。该表作为基准表。
引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.1</version><relativePath/> <!-- lookup parent from repository --></parent><modelVersion>4.0.0</modelVersion><artifactId>Sharding-JDBC-demo2</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc-core</artifactId><version>5.3.0</version></dependency><dependency><groupId>org.yaml</groupId><artifactId>snakeyaml</artifactId><version>1.33</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.6</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version><scope>compile</scope></dependency><!--<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional><scope>runtime</scope></dependency>--></dependencies></project>
1)引入shardingsphere-jdbc-core 5.3.0 的版本;
2)项目中不要引入spring-boot-devtools,否则在调试启动时,会报错;
spring-boot-devtools 会在类路径上的文件发生更改时自动重启,方便开发调试。在项目部署时,通过 java -jar 启动项目时,会自动禁用开发工具。报错的原因下面说明。
分片规则配置
4.1 application.yml
server:port: 8080
spring:main:# 处理连接池冲突allow-bean-definition-overriding: truedatasource:# shardingsphere5.3.0引入ShardingSphereDriver数据库驱动driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriverurl: jdbc:shardingsphere:classpath:sharding.yml
指定分片规则的文件为sharding.yml。
4.2 sharding.yml
dataSources:order_ds:dataSourceClassName: com.zaxxer.hikari.HikariDataSourcedriverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/shardingjdbctest?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=falseusername: rootpassword: 123456rules:
- !SHARDINGtables:tb_order: #逻辑表actualDataNodes: order_ds.tb_order #表是自动创建keyGenerateStrategy: # 指定主键生成策略column: order_idkeyGeneratorName: snowflaketableStrategy:standard: shardingColumn: order_time #分片键shardingAlgorithmName: custom-time-shardingshardingAlgorithms: #分片算法custom-time-sharding:type: CLASS_BASED #自定义类props:strategy: standardalgorithmClassName: com.jingai.sharding.jdbc.algorithm.OrderTimeShardingAlgorithm #分片算法keyGenerators: # 主键生成器snowflake:type: SNOWFLAKE
props:sql-show: true # 是否打印sql
1)配置真实表为tb_order,作为分表的表前缀;
2)配置分表策略为standard标准策略,以订单创建日期为分片键;
3)配置分表算法为自定义类OrderTimeShardingAlgorithm;
分片算法OrderTimeShardingAlgorithm
package com.jingai.sharding.jdbc.algorithm;@Slf4j
public class OrderTimeShardingAlgorithm implements StandardShardingAlgorithm<Date> {private static final DateFormat TABLE_SHARD_TIME_FORMAT = new SimpleDateFormat("yyyyMM");// 表分片符号。如:tb_order_202407private static final String TABLE_SPLIT_SYMBOL = "_";private Properties props;@Overridepublic String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> shardingValue) {String logicTableName = shardingValue.getLogicTableName();log.info("精准分片,逻辑表名:{},节点表名:{}", logicTableName, availableTargetNames);Date time = shardingValue.getValue();String result = logicTableName + TABLE_SPLIT_SYMBOL + TABLE_SHARD_TIME_FORMAT.format(time);// 在配置中,只配置了逻辑表名。如果只有一个,且是逻辑表名,说明需要获取所有表名initAvailableTargetNames(availableTargetNames, logicTableName);return getAndCreateShardingTable(logicTableName, result, availableTargetNames);}@Overridepublic Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Date> shardingValue) {String logicTableName = shardingValue.getLogicTableName();log.info("精准分片,逻辑表名:{},节点表名:{}", logicTableName, availableTargetNames);// 在配置中,只配置了逻辑表名。如果只有一个,且是逻辑表名,说明需要获取所有表名initAvailableTargetNames(availableTargetNames, logicTableName);Range<Date> valueRange = shardingValue.getValueRange();// 如果没有最大值或最小值,则全库扫描if(!valueRange.hasLowerBound() || !valueRange.hasUpperBound()) {return availableTargetNames;}Date min = valueRange.lowerEndpoint();Date max = valueRange.upperEndpoint();Set<String> rs = new HashSet<>();while (min.compareTo(max) <= 0) {String tableName = logicTableName + "_" + TABLE_SHARD_TIME_FORMAT.format(min);rs.add(tableName);min = DateUtils.addMonths(min, 1);}return getAndCreateShardingTable(logicTableName, rs, availableTargetNames);}private void initAvailableTargetNames(Collection<String> availableTargetNames, String logicTableName) {if(availableTargetNames.size() == 1 && availableTargetNames.contains(logicTableName)) {Set<String> allTableNameBySchema = ShardingAlgorithmUtil.getAllTableNameBySchema(logicTableName);availableTargetNames.clear();availableTargetNames.addAll(allTableNameBySchema);}}/*** 检查可用的真实表,如果表名不存在,则创建新表* @param logicTableName 逻辑表* @param resultTableNames 操作需要的真实表* @param availableTargetNames 可用的真实表* @return 分片的真实表*/private List<String> getAndCreateShardingTable(String logicTableName, Set<String> resultTableNames, Collection<String> availableTargetNames) {return resultTableNames.stream().map(name -> getAndCreateShardingTable(logicTableName, name, availableTargetNames)).collect(Collectors.toList());}/*** 检查可用的真实表,如果表名不存在,则创建新表* @param logicTableName* @param resultTableName* @param availableTargetNames* @return*/private String getAndCreateShardingTable(String logicTableName, String resultTableName, Collection<String> availableTargetNames) {if(availableTargetNames.contains(resultTableName)) {return resultTableName;}boolean rs = ShardingAlgorithmUtil.createShardingTable(logicTableName, resultTableName);if(rs) {availableTargetNames.add(resultTableName);return resultTableName;}return null;}@Overridepublic Properties getProps() {return props;}@Overridepublic void init(Properties properties) {this.props = properties;}
}
1)实现StandardShardingAlgorithm接口,重写doSharding()方法;
2)根据传入的时间分片值,解析出年月,和逻辑表组合,为实际操作的真实表;
3)如果当前的真实表不存在,则调用工具类ShardingAlgorithmUtil创建一个真实表;
工具类ShardingAlgorithmUtil
package com.jingai.sharding.jdbc.util;@Slf4j
public class ShardingAlgorithmUtil {// 表分片符号。如:tb_order_202407private static final String TABLE_SPLIT_SYMBOL = "_";// 配置的数据库源private volatile static Map<String, Map<String, Object>> dataSources = null;public static void init(String url) {Assert.hasText(url, "分片策略不能为空");log.info("数据源获取...");byte[] bytes = new ShardingSphereDriverURL(url).toConfigurationBytes();try {YamlRootConfiguration yamlRootConfiguration = YamlEngine.unmarshal(bytes, YamlRootConfiguration.class);dataSources = yamlRootConfiguration.getDataSources();} catch(Exception e) {e.printStackTrace();log.error("分片策略配置解析失败");throw new IllegalArgumentException("分片策略解析失败");}}/*** 获取所有真实表名*/public static Set<String> getAllTableNameBySchema(String logicTableName) {Assert.notNull(dataSources, "分片策略配置未初始化");Set<String> rs = new HashSet<>();// 获取配置的数据源String startTable = logicTableName + TABLE_SPLIT_SYMBOL;for (Map<String, Object> dataSource : dataSources.values()) {try (Connection conn = DriverManager.getConnection(dataSource.get("url").toString(),dataSource.get("username").toString(), dataSource.get("password").toString())){Statement statement = conn.createStatement();ResultSet resultSet = statement.executeQuery("show tables like '" + startTable + "%'");while (resultSet.next()) {String tableName = resultSet.getString(1);if(StringUtils.hasText(tableName) && tableName.replaceFirst(startTable, "").matches("\\d{6}")) {rs.add(tableName);}}} catch(Exception e) {e.printStackTrace();throw new IllegalArgumentException("数据库连接失败");}}return rs;}/*** 创建分表* @param logicTableName* @param resultTableName* @return*/public static boolean createShardingTable(String logicTableName, String resultTableName) {synchronized (logicTableName.intern()) {for (Map<String, Object> dataSource : dataSources.values()) {try (Connection conn = DriverManager.getConnection(dataSource.get("url").toString(),dataSource.get("username").toString(), dataSource.get("password").toString())){Statement statement = conn.createStatement();log.info("创建{}表", resultTableName);statement.execute("create table if not exists `" + resultTableName + "` like `" + logicTableName + "`;");} catch(Exception e) {e.printStackTrace();throw new IllegalArgumentException("数据库连接失败");}}return true;}}}
1)init(String url) 初始化方法,通过传入的url(application.yml中配置的spring.datasource.url),解析分片配置文件,得到配置的datasources信息;
2)getAllTableNameBySchema(String logicTableName),通过传入的逻辑表(配置中的tb_order),结合配置的datasources信息,创建连接,从数据库中获取表名以tb_order为前缀的表。即数据库中的真实表;
真实表只需从主库中获取即可,此处可以完善。
3)createShardingTable(),结合配置的datasources信息,创建连接,创建真实表;
初始化类
package com.jingai.sharding.jdbc.listener;@Component
@Slf4j
public class ShardingInitRunner implements InitializingBean {@Value("${spring.datasource.url}")private String url;@Overridepublic void afterPropertiesSet() throws Exception {log.info("sharding初始化...");ShardingAlgorithmUtil.init(url);}}
该类获取spring.datasource.url的配置值,在初始化方法中,调用ShardingAlgorithmUtil.init(url),初始化ShardingAlgorithmUtil中的datasource值。
1)如果引入了spring-boot-devtools依赖,开启开发工具。项目启动的时候,ShardingAlgorithmUtil类的类加载器为devtools包下的RestartClassLoader,并执行了初始化,获取了datasources;
2)在分片算法OrderTimeShardingAlgorithm的类加载器为AppClassLoader,OrderTimeShardingAlgorithm中调用ShardingAlgorithmUtil时,会用AppClassLoader重新加载一次ShardingAlgorithmUtil,此时的datasources为null;
3)此时执行ShardingAlgorithmUtil操作数据库时,会报空指针;
如果一定要引入spring-boot-devtools依赖,可以在项目的resources目录下增加一个文件META-INF/spring-devtools.properties,在文件下添加RestartClassLoader额外要加载的包的信息。代码如下:
restart.include.shardingsphere=/shardingsphere-[\\w\\d-\.]+\.jarOrderTimeShardingAlgorithm是在shardingsphere中使用AppClassLoader加载的,所以设置shardingsphere使用RestartClassLoader加载。
实体类
package com.jingai.sharing.jdbc.entity;@Data
@ToString
@TableName("tb_order")
public class OrderEntity {private long orderId;private long memberId;private float totalPrice;private String status;private Date orderTime;}
在实体类中,@TableName指定配置中的逻辑表。
Mapper类
package com.jingai.sharing.jdbc.dao;public interface OrderMapper extends BaseMapper<OrderEntity> {@Insert("insert into tb_order(member_id, total_price, status, order_time) values " +"(#{memberId}, #{totalPrice}, #{status}, #{orderTime})")@Options(useGeneratedKeys = true, keyProperty = "orderId")int insert2(OrderEntity order);
}
在4.2的配置中,通过key-generator设置了逻辑表的主键生成策略为雪花算法。当进行数据插入时,需要编写新的插入接口,不能直接使用Mybatis-plus中的insert()接口。因为在默认的insert()接口中,实体对象的orderId为0,不会走配置的雪花算法。
Service类
package com.jingai.sharing.jdbc.service;@Service
public class OrderService extends ServiceImpl<OrderMapper, OrderEntity> {@Resourceprivate OrderMapper orderMapper;public long insert2(OrderEntity order) {int rs = orderMapper.insert2(order);return rs > 0 ? order.getOrderId() : 0;}}
为了便于测试,此处省略了Service的接口类。
Controller类
@RestController
public class OrderController {@Resourceprivate OrderService orderService;@RequestMapping("order")public String order(OrderEntity order) {order.setOrderTime(new Date());long insert = orderService.insert2(order);return insert > 0 ? "success" : "fail";}@RequestMapping("list")public List<OrderEntity> list() {return orderService.list();}}
小结
以上为本篇分享的全部内容。以下做一个小结:
1)创建一个基准表tb_order;
2)配置分片规则:标准策略、以订单时间为分片键、自定义分片算法;
3)在分片算法中,根据分片键的值日期值,找到对应月份的表。如果真实表不存在,则创建;
关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。
相关文章:
SpringBoot集成Sharding-JDBC-5.3.0实现按月动态建表分表
Sharding-JDBC系列 1、Sharding-JDBC分库分表的基本使用 2、Sharding-JDBC分库分表之SpringBoot分片策略 3、Sharding-JDBC分库分表之SpringBoot主从配置 4、SpringBoot集成Sharding-JDBC-5.3.0分库分表 5、SpringBoot集成Sharding-JDBC-5.3.0实现按月动态建表分表 前言 …...
ubuntu 上安装中文输入法
在Ubuntu上安装中文输入法,通常有以下几种方法: 方法一:使用Fcitx输入法框架和搜狗输入法 安装Fcitx: sudo apt update sudo apt install fcitx fcitx-bin fcitx-table-all 安装搜狗输入法: 首先,从搜狗…...
Postman导出excel文件
0 写在前面 在我们后端写接口的时候,前端页面还没有出来,我们就得先接口测试,在此记录下如何使用postman测试导出excel接口。 如果不会使用接口传参可以看我这篇博客如何使用Postman 1 方法一 2 方法二 3 写在末尾 虽然在代码中写入文件名…...
你还在手动构建Python项目吗?PyBuilder让一切自动化!
在 Python 项目开发中,构建和管理项目是一项繁琐但必不可少的工作。你可能需要处理依赖项、运行测试、生成文档等。这时候,PyBuilder 出场了。它是一个强大的构建自动化工具,可以帮助你简化项目管理,让你更专注于编写代码。 什么…...
WebRTC音视频-前言介绍
目录 效果预期 1:WebRTC相关简介 1.1:WebRTC和RTC 1.2:WebRTC前景和应用 2:WebRTC通话原理 2.1:媒体协商 2.2:网络协商 2.3:信令服务器 效果预期 1:WebRTC相关简介 1.1&…...
centos/rocky容器中安装xfce、xrdp记录
最近需要一台机器来测试rdp连接,使用容器linuxxfcexrdp来实现,在此记录下主要步骤 启动rockylinux容器(其他linux发行版步骤应该相似) docker run -it -p 33891:3389 rockylinux:9.3 bash容器内操作 # 省略替换软件源步骤 ...# …...
实战:Eureka的概念作用以及用法详解
概叙 什么是Eureka? Netflix Eureka 是一款由 Netflix 开源的基于 REST 服务的注册中心,用于提供服务发现功能。Spring Cloud Eureka 是 Spring Cloud Netflix 微服务套件的一部分,基于 Netflix Eureka 进行了二次封装,主要负责…...
jupyter_contrib_nbextensions安装失败问题
目录 1.文件路径长度问题 2.jupyter不出现Nbextensions选项 1.文件路径长度问题 问题: could not create build\bdist.win-amd64\wheel\.\jupyter_contrib_nbextensions\nbextensions\contrib_nbextensions_help_item\contrib_nbextensions_help_item.yaml: No su…...
设计模式-Git-其他
目录 设计模式? 创建型模式 单例模式? 啥情况需要单例模式 实现单例模式的关键点? 常见的单例模式实现? 01、饿汉式如何实现单例? 02、懒汉式如何实现单例? 03、双重检查锁定如何实现单例ÿ…...
【C#】计算两条直线的交点坐标
问题描述 计算两条直线的交点坐标,可以理解为给定坐标P1、P2、P3、P4,形成两条线,返回这两条直线的交点坐标? 注意区分:这两条线是否垂直、是否平行。 代码实现 斜率解释 斜率是数学中的一个概念,特别是…...
在项目服务器部署git 并实现自动提交
以下场景适合在服务器当中使用git 方便提交代码,同时不需要外部的git仓库(码云gitee或者github作为管理平台)。依靠服务器本身ssh 连接协议做为git提交的地址,同时利用钩子自动同步项目代码 首先下载git sudo apt update sudo a…...
前缀匹配工具之IP-Prefix
目录 基本概念: 技术背景: 用户需求: 安全需求: 企业内部的访问控制需求: IP-Prefix的配置与语句分析: 调用方式: 尾声 基本概念: IP-Prefix,即IP前缀,相比传统ACL,它能…...
等级保护测评案例分享及合规建议
一、黑龙江省等级保护测评概述 黑龙江省等级保护测评(简称“等保测评”)是依据国家网络安全等级保护制度的要求,对信息系统进行安全等级划分和安全保护能力的评估。等保测评不仅能够帮助企业和组织发现潜在的安全风险,还能够指导…...
GOLLIE : ANNOTATION GUIDELINES IMPROVE ZERO-SHOT INFORMATION-EXTRACTION
文章目录 题目摘要引言方法实验消融研究 题目 Techgpt-2.0:解决知识图谱构建任务的大型语言模型项目 论文地址:https://arxiv.org/abs/2310.03668 摘要 大型语言模型 (LLM) 与指令调优相结合,在泛化到未见过的任务时取得了重大进展。然而,它…...
2024-07-19 Unity插件 Odin Inspector9 —— Validation Attributes
文章目录 1 说明2 验证特性2.1 AssetsOnly / SceneObjectsOnly2.2 ChildGameObjectsOnly2.3 DisallowModificationsIn2.4 FilePath2.5 FolderPath2.6 MaxValue / MinValue2.7 MinMaxSlider2.8 PropertyRange2.9 Required2.10 RequiredIn2.11 RequiredListLength2.12 ValidateIn…...
跨平台WPF音乐商店应用程序
目录 一 简介 二 设计思路 三 源码 一 简介 支持在线检索音乐,支持实时浏览当前收藏的音乐及音乐数据的持久化。 二 设计思路 采用MVVM架构,前后端分离,子界面弹出始终位于主界面的中心。 三 源码 视窗引导启动源码: namesp…...
设计模式简述(一)
定义:设计模式指的是在软件开发过程中,经过验证的,用于解决在特定环境下,重复出现的,特定问题的解决方案。创建型设计模式关注对象的创建过程,提供了更灵活、可扩展的对象创建机制。结构型设计模式用于解决…...
OSI参考模型:解析网络通信的七层框架
引言 在现代计算机网络中,OSI(开放式系统互联)参考模型是理解和设计网络通信协议的基础。1978年由国际标准化组织(ISO)提出,OSI模型定义了网络通信的七层结构,每一层都承担着特定的功能&#x…...
QT通用配置文件库(QPreferences)
QT通用配置文件库(QPreferences) QPreferences项目是基于nlohmann/json的qt可视化配置文件库,将配置保存成json格式,并提供UI查看与修改,可通过cmake可快速添加进项目。默认支持基本类型、stl常用容器、基本类型与stl容器组成的结构体&#…...
如何搭建一个RADIUS服务器?
1. 系统环境 1.1.操作系统 Ubuntu-20.04.1 (kernel: 5.15.0-58-generic) 1.2.所需软件 FreeRADIUS MariaDB 1.3.注意事项 本文提到的所有操作,都是以root 身份执行; 2. FreeRADIUS的安装 2.1. 安装FreeRADIUS服务器程序 以…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
