Java分别用BIO、NIO实现简单的客户端服务器通信
分别用BIO、NIO实现客户端服务器通信
- BIO
- NIO
- NIO演示(无Selector)
- NIO演示(Selector)
前言:
Java I/O模型发展以及Netty网络模型的设计思想
BIO
Java BIO是Java平台上的BIO(Blocking I/O)模型,是Java中用于实现同步阻塞网络编程的一种方式。 在Java中,使用BIO模型需要通过Socket和ServerSocket类来完成网络连接和数据传输,但是由于BIO是同步阻塞的,所以会导致线程阻塞和资源浪费的问题。
因此,在高并发的网络编程场景中,通常会选择使用NIO(Non-blocking I/O)模型或者Netty等框架来实现。
服务端类代码
package com.yu.io.bio;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;public class BIOServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(8081);while (true) {System.out.println("wait connect...");Socket clientSocket = serverSocket.accept();System.out.println(clientSocket.getPort() + "connected");System.out.println("start read...");byte[] readArr = new byte[1024];int read = clientSocket.getInputStream().read(readArr);if (read != -1) {System.out.println("read info : " + new String(readArr,0,read));}System.out.println("end read...");byte[] resBytes = "server response".getBytes();clientSocket.getOutputStream().write(resBytes);System.out.println("response info : " + new String(readArr,0,read));clientSocket.getOutputStream().flush();}}
}
客户端类代码
package com.yu.io.bio;import java.io.IOException;
import java.net.Socket;public class BIOClient {public static void main(String[] args) throws IOException {Socket clientSocket = new Socket("127.0.0.1",8081);byte[] resBytes = "client response".getBytes();clientSocket.getOutputStream().write(resBytes);System.out.println("response info : " + new String(resBytes));clientSocket.getOutputStream().flush();byte[] readArr = new byte[1024];int read = clientSocket.getInputStream().read(readArr);if (read != -1) {System.out.println("read info : " + new String(readArr,0,read));}}
}
NIO
Java NIO 能够支持非阻塞网络编程,可以理解为new io 或者no blok io 我更喜欢称之为new io,因为他不仅仅实现了非阻塞的网络编程方式,同时也封装了常用的网络编程api,更重要的是引入了多路复用器Selector的概念
下面的代码只是展示NIO非阻塞的实现,并没有展示NIO的真正用法
NIO演示(无Selector)
服务端类代码
package com.yu.io.nio;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;public class NIOServer {public static List<SocketChannel> socketChannelList = new ArrayList<>();public static void main(String[] args) throws IOException {ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress(8081));serverSocketChannel.configureBlocking(false);System.out.println("server start...");while (true) {SocketChannel socketChannel = serverSocketChannel.accept();if (socketChannel != null) {System.out.println(socketChannel.socket().getPort() + " connected");socketChannel.configureBlocking(false);socketChannelList.add(socketChannel);}List<SocketChannel> rmChannelList = new ArrayList<>();for (SocketChannel channel : socketChannelList) {try {doChannel(rmChannelList, channel);} catch (IOException ioException) {//有客户端断开连接System.out.println(channel.socket().getPort() + " disconnected");channel.close();rmChannelList.add(channel);}}socketChannelList.removeAll(rmChannelList);}}private static void doChannel(List<SocketChannel> rmChannelList, SocketChannel channel) throws IOException {ByteBuffer readByteBuffer = ByteBuffer.allocate(2048);int read = channel.read(readByteBuffer);String readStr = new String(readByteBuffer.array());if (read > 0) {System.out.println(channel.socket().getPort() + " : " + readStr);if (readStr.contains("hello")) {ByteBuffer sendByteBuffer = ByteBuffer.wrap("hello! I am robot.".getBytes());channel.write(sendByteBuffer);System.out.println("me : " + new String(sendByteBuffer.array()));}if (readStr.contains("old")) {ByteBuffer sendByteBuffer = ByteBuffer.wrap("I am 1 years old.".getBytes());channel.write(sendByteBuffer);System.out.println("me : " + new String(sendByteBuffer.array()));}if (readStr.contains("bey")) {ByteBuffer sendByteBuffer = ByteBuffer.wrap("see you.".getBytes());channel.write(sendByteBuffer);System.out.println("me : " + new String(sendByteBuffer.array()));}}if (read == -1) {rmChannelList.add(channel);}}
}
客户端类代码
package com.yu.io.nio;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;public class NIOClient {public static void main(String[] args) throws IOException {SocketChannel clientSocketChannel = SocketChannel.open();clientSocketChannel.connect(new InetSocketAddress("127.0.0.1",8081));clientSocketChannel.configureBlocking(false);String sendStr = "hello! I am client " + clientSocketChannel.socket().getPort() + ".";ByteBuffer sendByteBuffer = ByteBuffer.wrap(sendStr.getBytes());clientSocketChannel.write(sendByteBuffer);System.out.println("me : " + new String(sendByteBuffer.array()));int msgSize = 0;while (msgSize < 10) {ByteBuffer readByteBuffer = ByteBuffer.allocate(1024);int read = clientSocketChannel.read(readByteBuffer);String readStr = new String(readByteBuffer.array());if (read > 0) {System.out.println("robot : " + readStr);msgSize ++;ByteBuffer resByteBuffer = null;if (readStr.contains("hello")) {resByteBuffer = ByteBuffer.wrap("how old are you?.".getBytes());clientSocketChannel.write(resByteBuffer);System.out.println("me : " + new String(resByteBuffer.array()));resByteBuffer.clear();}if (readStr.contains("old")) {resByteBuffer = ByteBuffer.wrap("en, place say hello!".getBytes());clientSocketChannel.write(resByteBuffer);System.out.println("me : " + new String(resByteBuffer.array()));resByteBuffer.clear();}}}ByteBuffer resByteBuffer = ByteBuffer.wrap("bey bey!".getBytes());clientSocketChannel.write(resByteBuffer);System.out.println("me : " + new String(resByteBuffer.array()));resByteBuffer.clear();clientSocketChannel.close();}
}
NIO演示(Selector)
无Selector的NIO演示中,显然会出现空转的情况,以及无效连接的处理问题,这些问题都会影响性能。
NIO提供Selector多路复用器,优化上述问题
以下demo实现服务器与客户端通信,相互发消息。并由服务器转发给其他客户端(广播功能)
服务端类代码
server main类
import java.io.IOException;public class NIOServerMain {public static void main(String[] args) throws IOException {NIOSelectorServer server = new NIOSelectorServer();server.start();}
}
server run类
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;public class NIOSelectorServer {public void start() throws IOException {ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress(8081));serverSocketChannel.configureBlocking(false);//注册到selector多路复用器中Selector selector = Selector.open();serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("server start...");run(selector);}/*** 遍历多路复用器的事件,处理事件*/private void run(Selector selector) throws IOException {while (true) {//阻塞等待事件selector.select();Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey selectedKey = iterator.next();try {if (selectedKey.isAcceptable()) {doAccept(selector, selectedKey);}if (selectedKey.isReadable()) {doReadChannel(selectedKey, selector);}} catch (IOException ioException) {//有客户端断开连接disConnect(selectedKey, "exception");}iterator.remove();}}}/*** 处理连接事件*/private void doAccept(Selector selector, SelectionKey selectedKey) throws IOException {ServerSocketChannel serverChannel = (ServerSocketChannel)selectedKey.channel();SocketChannel socketChannel = serverChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);System.out.println(socketChannel.socket().getPort() + " connected");}/*** 处理接收信息事件*/private void doReadChannel(SelectionKey selectedKey, Selector selector) throws IOException {SocketChannel channel = (SocketChannel)selectedKey.channel();ByteBuffer readByteBuffer = ByteBuffer.allocate(2048);int read = channel.read(readByteBuffer);String readStr = "";readByteBuffer.flip();readStr += StandardCharsets.UTF_8.decode(readByteBuffer);if (read > 0) {System.out.println(channel.socket().getPort() + " : " + readStr.length() + " - "+ readStr);//转发消息(其他客户端)broadcast(selectedKey, selector, readStr);if (readStr.contains("hello")) {sendMsg(channel, "hello! I am robot.");}if (readStr.contains("old")) {sendMsg(channel, "I am 1 years old.");}if (readStr.contains("bye")) {sendMsg(channel, "see you.");}}if (read == -1) {//有客户端断开连接disConnect(selectedKey, "read = -1");}}/*** 连接异常的处理*/private void disConnect(SelectionKey selectedKey, String type) throws IOException {SocketChannel channel = (SocketChannel)selectedKey.channel();System.out.println(channel.socket().getPort() + " disconnected. " + type);selectedKey.cancel();channel.close();}/*** 发送消息*/private void sendMsg(SocketChannel channel, String s) throws IOException {ByteBuffer sendByteBuffer = ByteBuffer.wrap(s.getBytes());channel.write(sendByteBuffer);System.out.println("me : " + new String(sendByteBuffer.array()));}/*** 广播* 转发消息(给其他客户端)*/private void broadcast(SelectionKey fromSelectedKey, Selector selector, String readStr) throws IOException {Iterator<SelectionKey> selectionKeyIterator = selector.keys().iterator();while (selectionKeyIterator.hasNext()) {SelectionKey otherKey = selectionKeyIterator.next();if (otherKey == fromSelectedKey) {continue;}if (!(otherKey.channel() instanceof SocketChannel)) {continue;}SocketChannel otherChannel = (SocketChannel)otherKey.channel();sendMsg(otherChannel, "(转发自 "+ ((SocketChannel)fromSelectedKey.channel()).socket().getPort() + ")" + readStr);}}
}
客户端代码
一共构造了两个客户端(消息客户端和looker客户端), looker客户端优先启动,随后启动消息客户端,消息客户端与服务器的通信会被转发给looker客户端
look client main类
import java.io.IOException;public class NIOClientLookMain {public static void main(String[] args) throws IOException, InterruptedException {NIOSelectorClient client = new NIOSelectorLookClient();client.start(8081, 100);}
}
msg client main类
import java.io.IOException;public class NIOClientMain {public static void main(String[] args) throws IOException, InterruptedException {NIOSelectorClient lookClient = new NIOSelectorClient();lookClient.start(8081, 10);}
}
client run类
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;public class NIOSelectorClient {protected int port;protected int size;public void start(int port, int size) throws IOException, InterruptedException {this.port = port;this.size = size;SocketChannel clientSocketChannel = SocketChannel.open();clientSocketChannel.connect(new InetSocketAddress("127.0.0.1",port));clientSocketChannel.configureBlocking(false);Selector selector = Selector.open();clientSocketChannel.register(selector, SelectionKey.OP_READ);//发送开始数据sendMsg(clientSocketChannel, "hello! I am client " + clientSocketChannel.socket().getLocalPort() + ".");run(selector);sendMsg(clientSocketChannel, "bye bye!");clientSocketChannel.close();}/*** 遍历多路复用器的事件,处理事件*/protected void run(Selector selector) throws IOException, InterruptedException {int msgSize = 0;while (msgSize < size) {int length=selector.select();if(length ==0){continue;}Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while(iterator.hasNext()){SelectionKey selectionKey = iterator.next();if(selectionKey.isReadable()){boolean readChannel = false;try {readChannel = doReadChannel(selectionKey, selector);} catch (IOException e) {selectionKey.cancel();System.out.println("robot disconnect, restart connect...");while (true){try {reConnect();return;} catch (IOException ioException) {System.out.println("restart connecting(5s) ");//ioException.printStackTrace();Thread.sleep(5000);}}}if (readChannel) {msgSize ++;}}iterator.remove();}}}protected boolean doReadChannel(SelectionKey selectedKey, Selector selector) throws IOException {SocketChannel channel = (SocketChannel)selectedKey.channel();ByteBuffer readByteBuffer = ByteBuffer.allocate(2048);int read = channel.read(readByteBuffer);String readStr = "";readByteBuffer.flip();readStr += StandardCharsets.UTF_8.decode(readByteBuffer);if (read > 0) {System.out.println("robot : " + readStr);if (readStr.contains("hello")) {sendMsg(channel, "how old are you?.");}if (readStr.contains("old")) {sendMsg(channel, "en, place say hello!");}return true;}return false;}protected void sendMsg(SocketChannel channel, String sendStr) throws IOException {ByteBuffer sendByteBuffer = ByteBuffer.wrap(sendStr.getBytes());channel.write(sendByteBuffer);System.out.println("me : " + new String(sendByteBuffer.array()));}protected void reConnect() throws IOException, InterruptedException {NIOSelectorClient client = new NIOSelectorClient();client.start(port, size);}
}
look client run类
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;public class NIOSelectorLookClient extends NIOSelectorClient{@Overridepublic void start(int port, int size) throws IOException, InterruptedException {this.port = port;this.size = size;SocketChannel clientSocketChannel = SocketChannel.open();clientSocketChannel.connect(new InetSocketAddress("127.0.0.1",port));clientSocketChannel.configureBlocking(false);Selector selector = Selector.open();clientSocketChannel.register(selector, SelectionKey.OP_READ);//发送开始数据sendMsg(clientSocketChannel, "I am looker. " + clientSocketChannel.socket().getLocalPort() + ".");run(selector);}@Overrideprotected boolean doReadChannel(SelectionKey selectedKey, Selector selector) throws IOException {SocketChannel channel = (SocketChannel)selectedKey.channel();ByteBuffer readByteBuffer = ByteBuffer.allocate(2048);int read = channel.read(readByteBuffer);String readStr = "";readByteBuffer.flip();readStr += StandardCharsets.UTF_8.decode(readByteBuffer);if (read > 0) {System.out.println("robot : " + readStr.length() + " - "+ readStr);if (readStr.contains("bye")) {sendMsg(channel, "bye.");}return true;}return false;}@Overrideprotected void reConnect() throws IOException, InterruptedException {NIOSelectorClient client = new NIOSelectorLookClient();client.start(port, size);}
}
相关文章:

Java分别用BIO、NIO实现简单的客户端服务器通信
分别用BIO、NIO实现客户端服务器通信 BIONIONIO演示(无Selector)NIO演示(Selector) 前言: Java I/O模型发展以及Netty网络模型的设计思想 BIO Java BIO是Java平台上的BIO(Blocking I/O)模型&a…...

React Portals
什么是React Portals React Portals(React 门户)是 React 提供的一种机制,用于将组件渲染到 DOM 树中的不同位置,而不受组件层次结构的限制。它允许你将一个组件的渲染内容“传送”到 DOM 结构中的任何位置,通常用于处…...

Python基础之高级函数
异常捕获 Python中,使用trycatch两个关键字来实现对异常的处理。在我们平时的工作中,异常的出现是在所难免的,但是异常一旦出现,极有可能会直接导致程序崩溃,无法正常运行,所以异常一定要及时的做出对应的…...

CSS3常用的新功能总结
CSS3常用的新功能包括圆角、阴渐变、2D变换、3D旋转、动画、viewpor和媒体查询。 圆角、阴影 border-redius 对一个元素实现圆角效果,是通过border-redius完成的。属性为两种方式: 一个属性值,表示设置所有四个角的半径为相同值ÿ…...

Lvs+KeepAlived高可用高性能负载均衡
目录 1.环境介绍 2.配置keepalived 3.测试 1.测试负载均衡 2.测试RS高可用 3.测试LVS高可用 3.1测试lvs主服务宕机 3.2.测试lvs主服务器恢复 4.我在实验中遇到的错误 1.环境介绍 环境:centos7 RS1---RIP1:192.168.163.145 VIP 192.168.163.200 RS2---RIP2…...

无涯教程-Android Online Test函数
Android在线测试模拟了真正的在线认证考试。您将看到基于 Android概念的多项选择题(MCQ),将为您提供四个options。您将为该问题选择最合适的答案,然后继续进行下一个问题,而不会浪费时间。完成完整的考试后,您将获得在线考试分数。 总问题数-20 最长时间-20分钟 Start Test …...

蓝桥杯打卡Day1
文章目录 全排列八皇后 一、全排列IO链接 本题思路:本题是一道经典的全排列问题,深度优先搜索即可解决。 #include <bits/stdc.h>constexpr int N10;std::string s; std::string ans; int n; bool st[N];void dfs(int u) {if(un){std::cout<<ans<…...

zipkin2.24.2源码install遇见的问题
1、idea导入项目后将Setting中的关于Maven和Java Compile相关的配置改为jdk11,同时Project Structure改为jdk11 2、将pom配置中的fork标签注释 标题未修改以上配置产生的问题 Compilation failure javac: Ч ı : --release : javac <options> <source files&g…...

yapi密码是如何生成的
yapi密码是如何生成的 关闭yapi注册功能后,想要通过手动插入用户数据到db中,那么密码是如何生成的呢? exports.generatePassword (password, passsalt) > { return sha1(password sha1(passsalt)); }; 所以如果想要创建一个用户&#x…...

2023-09-02 LeetCode每日一题(最多可以摧毁的敌人城堡数目)
2023-09-02每日一题 一、题目编号 2511. 最多可以摧毁的敌人城堡数目二、题目链接 点击跳转到题目位置 三、题目描述 给你一个长度为 n ,下标从 0 开始的整数数组 forts ,表示一些城堡。forts[i] 可以是 -1 ,0 或者 1 ,其中&…...

k8s环境部署配置
目录 一.虚拟机准备 二.基础环境配置(各个节点都做) 1.IP和hosts解析 2.防火墙和selinux 3.安装基本软件 4.配置时间同步 5.禁用swap分区 6.修改内核参数并重载 7.配置ipvs 三.docker环境(各个节点都做) 1.配置软件源并…...

Java之文件操作与IO
目录 一.认识文件 1.1文件是什么? 1.2文件的组织 1.3文件路径 1.4文件的分类 二.文件操作 2.1File概述 三.文件内容操作--IO 3.1JavaIO的认识 3.2Reader和Writer ⭐Reader类 ⭐Writer类 3.2FileInputStream和FileOutputStream ⭐FileInputStream类 …...

指令系统(408)
一、拓展操作码指令格式 【2017 统考】某计算机按字节编址,指令字长固定且只有两种指令格式,其中三地址指令29条、二地址指令107条,每个地址字段6位,则指令字长至少应该是( A) A、24位 B、26位 …...

Pygame中Trivia游戏解析6-3
3.3 Trivia类的show_question()函数 Trivia类的show_question()函数的作用是显示题目。主要包括显示题目框架、显示题目内容和显示题目选项等三部分。 3.3.1 显示题目的框架 在show_question()函数中,通过以下代码显示题目的框架。 print_text(font1, 210, 5, &q…...

热释电矢量传感器设计
1 概述 使用4个热释电传感器组成一个2X2的矩阵。通过曲线的相位差、 峰峰值等特征量来计算相关信息。本文使用STM32单片机设计、制作了热释电传感器矩阵;使用C#.NET设计了上位机软件。为以上研究做了试验平台。 2 硬件电路设计 2.1 热释电传感器介绍 热释电红外…...

MySql学习笔记10——视图介绍
视图 概述 view view可以看作是一张“虚拟表”,(但是他也是会作为文件存在的) 当我们通过复杂的查询语句获取一张表的时候,可以将这张表作为一个视图,和创建一个新表不同,在视图上进行的DML操作会对数据…...

【探索Linux】—— 强大的命令行工具 P.7(进程 · 进程的概念)
阅读导航 前言一、冯诺依曼体系结构二、操作系统(OS)1. 概念 三、进程1. 进程的概念2. PCB(Process Control Block)3. 查看进程 四、fork函数1. 函数简介2. 调用方式3. 返回值4. 使用示例 五、进程的几种状态1. 状态简介2. 进程状…...

JAVA宝典----容器(理解记忆)
目录 一、Java Collections框架是什么? 二、什么是迭代器? 三、Iterator与ListIterator有什么区别? 四、ArrayList、Vector和LinkedList有什么区别? 五、HashMap、Hashtable、TreeMap和WeakHashMap有哪些区别? 六…...

JS中的扩展操作符...(点点点)
标题 前言概述拓展操作符(Spread Operator)赋值与扩展操作符的区别 剩余操作符(Rest Operator) 前言 大家应该都在代码中用过或者看到…方法,我每次用到这个方法的时候都会想到第一次见到这个方法一头雾水的样子&#…...

找redis大key工具rdb_bigkeys
github官网 https://github.com/weiyanwei412/rdb_bigkeys 在centos下安装go [roothadoop102 rdb_bigkeys-master]# wget https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz [roothadoop102 rdb_bigkeys-master]# tar -zxf go1.13.5.linux-amd64.tar.gz -C /usr/local将g…...

ElasticSearch进阶
一、 search检索文档 ES支持两种基本方式检索; 通过REST request uri 发送搜索参数 (uri 检索参数);通过REST request body 来发送它们(uri请求体); 1、信息检索 API: https://w…...

Nor flash 页写地址与数据大小的限制
厂商提供的flash手册如下 如果页写指令的地址不是256的整数倍,并且写入的数据量超过了当前地址所在页的边界,则超过的那些数据会重新写入当前页的首地址(即256的整数倍地址),所以,在进行页写的时候&#x…...

python 深度学习 解决遇到的报错问题4
目录 一、DLL load failed while importing _imaging: 找不到指定的模块 二、Cartopy安装失败 三、simplejson.errors.JSONDecodeError: Expecting value: line 1 column 1 (char 0) 四、raise IndexError("single positional indexer is out-of-bounds") 五、T…...

C到C++的升级
C和C的关系 C继承了所有C语言的特性;C在C的基础上提供了更多的语法和特性,C语言去除了一些C语言的不好的特性。C的设计目标是运行效率与开发效率的统一。 变化一:所有变量都可以在使用时定义 C中更强调语言的实用性,所有的变量…...

《热题101》动态规划篇
思路:需要一个二维数组dp来记录当前的公共子序列长度,如果当前的两个值等,则dp[i][j]dp[i-1][j-1]1,否则dp[i][j] max(dp[i-1][j],dp[i][j-1])。也就是说,当前的dp值是由左、上、左上的值决定的。获得dp数组之后,倒序…...

【综述+3D】基于NeRF的三维视觉2023年度进展报告(截止2023.06.10)
论文:2003.Representing Scenes as Neural Radiance Fields for View Synthesis 官方网站:https://www.matthewtancik.com/nerf 突破性后续改进: Instant Neural Graphics Primitives with a Multiresolution Hash Encoding | 展示官网&#…...

基于JavaScript粒子流动效果
这是一个HTML文件,主要包含了一些CSS样式和JavaScript代码,用于创建一个动画效果。 在CSS部分,定义了一些基本的样式,包括页面的背景颜色、位置、大小等。特别的,定义了两种球形元素(.ball_A 和 .ball_B&am…...

【U盘】实现U盘清空并重置恢复存储
打开电脑,将U盘插入USB端口,点按快捷键【WinR】,弹出运行对话框,输入命令 diskpart 进入命令提示符窗口 输入指令 list disk 查看现在的硬盘 这里显示的U盘编号是“1”,因此输入select disk “1”,就是选择…...

基于Hugo 搭建个人博客网站
目录 1.环境搭建 2.生成博客 3.设置主题 4.将博客部署到github上 1.环境搭建 1)安装Homebrew brew是一个在 macOS 操作系统上用于管理软件包的包管理器。类似于centos下的yum或者ubuntu下的apt,它允许用户通过命令行安装、更新和管理各种软件工具、…...

Springboot + Sqlite实战(离线部署成功)
最近有个需求,是手机软件离线使用, 用的springboot mybatis-plus mysql,无法实现,于是考虑使用内嵌式轻量级的数据库SQLlite 引入依赖 <dependency><groupId>org.xerial</groupId><artifactId>sqlite-…...