【Linux】网络编程套接字

文章目录

  • 网络编程套接字
    • 1. 认识TCP协议
    • 2. 认识UDP协议
    • 3. 网络字节序
    • 4. socket编程接口
      • 4.1 sockaddr 结构
    • 5. 简单的UDP网络程序
    • 6. 简单的TCP网络程序
      • 6.1 TCP socket的封装
      • 6.2 TCP协议通讯流程

网络编程套接字

1. 认识TCP协议

  • 传输层协议
  • 有连接
  • 可靠传输
  • 面向字节流

2. 认识UDP协议

  • 传输层协议
  • 无连接
  • 不可靠传输
  • 面向数据包

3. 网络字节序

不管这台主机是大端还是小端,就需要先将数据转换为大端字节序

b31xq9ivwl-1691562183517.png

h表示本地,n 表示 network, l表示32为长整数, s表示16位短整数

4. socket编程接口

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

4.1 sockaddr 结构

d86s9hjfue-1691562350564.png

5. 简单的UDP网络程序

void InitServer()
{_sock = socket(AF_INET, SOCK_DGRAM, 0);if (_sock < 0){std::cout << "create sock error" << strerror(errno) << std::endl;exit(SOCKET_ERR);}std::cout << "create sock success" << std::endl;struct sockaddr_in local;sizeof (local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = inet_addr(_ip.c_str());if (bind(_sock, (struct sockaddr*)&local, sizeof(local)) < 0){std::cout << "bind socket error" << strerror(errno) << std::endl;exit(BIND_ERR);}std::cout << "bind socket success" << std::endl;
}

下面是实现的简单群聊服务器:

  1. 环形队列

    #pragma once#include <iostream>
    #include <vector>
    #include <pthread.h>
    #include <semaphore.h>static const int N = 50;template <class T>
    class RingQueue
    {
    private:void P(sem_t &s){sem_wait(&s);}void V(sem_t &s){sem_post(&s);}void Lock(pthread_mtuex_t &m){pthread_mutex_lock(&m);}void Unlock(pthread_mutex_t &m){pthread_mutex_unlock(&m);}public:RingQueue(int num = N) : _ring(num), _cap(num){sem_init(&_data_sem, 0, 0);sem_init(&_space_sem, 0, num);_c_step = _p_step = 0;pthread_mutex_init(&_c_mutex, nullptr);pthread_mutex_init(&_p_mutex, nullptr);}void push(const T &in){P(_space_sem);Lock(_p_mutex);_ring[_p_step++] = in;_p_step %= _cap;Unlock(_p_mutex);V(_data_sem);}void pop(T *out){P(_data_sem);Lock(_c_mutex);*out = _ring[_c_step++];_c_step %= _cap;Unlock(_c_mutex);V(_space_sem);}~RingQueue(){sem_destroy(&_data_sem);sem_destroy(&_space_sem);pthread_mutex_destroy(&_p_mutex);pthread_mutex_destroy(&_c_mutex);}private:std::vector<T> _ring;int _cap; // 环形队列的大小sem_t _data_sem; // 消费者关心sem_t _space_sem;  // 生产者关心int _c_step; // 消费位置int _p_step; // 生产位置pthread_mutex_t _c_mutex;pthread_mutex_t _p_mutex;
    };
    
  2. 线程包装

    #pragma once#include <iostream>
    #include <string>
    #include <cstdlib>
    #include <pthread.h>
    #include <functional>class Thread
    {
    public:typedef enum{NEW = 0, RUNNING, EXITED} ThreadStatus;using func_t = std::function<void ()>;public:Thread(int num, func_t func) : _tid(0), _status(NEW), _func(func){char name[128];snprintf(name, sizeof(name), "thread-%d", num);_name = name;   }int status() {return _status;}std::string threadname(){return _name;}pthread_t pthreadid(){if (_status == RUNNING){return _tid;}else{return 0;}}void operator()() // 仿函数{if (_func != nullptr) _func();}static void *runHelper(void *args){Thread* ts = (Thread*)args;(*ts)();return nullptr;}void run(){int n = pthread_create(&_tid, nullptr, runHelper, this);if (n != 0) exit(1);_status = RUNNING;}void join(){int n = pthread_join(_tid, nullptr);if (n != 0){std::cout << "man thread join thread" << _name << " error" << std::endl;return;}_status = EXITED;}~Thread(){}private:pthread_t _tid;std::string _name;func_t _func;ThreadStatus _status;
    };
    
  3. 锁包装

    #pragma once#include <iostream>
    #include <pthread.h>class Mutex // 自己不维护锁,外部传入
    {
    private:pthread_mutex_t *_pmutex;
    public:Mutex(pthread_mutex_t *mutex) : _pmutex(mutex){}void lock(){pthread_mutex_lock(_pmutex);}void unlock(){pthread_mutex_unlock(_pmutex);}~Mutex(){}
    };class lockGuard
    {
    private:Mutex _mutex;
    public:lockGuard(pthread_mutex_t *mutex) : _mutex(mutex){_mutex.lock();}~lockGuard(){_mutex.unlock();}
    };
    
  4. 服务器

    #pragma once#include <iostream>
    #include <cerrno>
    #include <cstring>
    #include <cstdlib>
    #include <functional>
    #include <strings.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <pthread.h>
    #include <unordered_map>
    #include "err.hpp"
    #include "RingQueue.hpp"
    #include "lockGuard.hpp"
    #include "Thread.hpp"const static uint16_t port = 8888;
    using func_t = std::function<std::string(std::string)>;class UdpServer
    {
    private:uint16_t _port;int _sock;std::unordered_map<std::string, struct sockaddr_in> _onlineuser;pthread_mutex_t _lock;RingQueue<std::string> _rq;Thread *c;Thread *p;public:UdpServer(uint16_t port = port) : _port(port){std::cout << "server addr " << _port << std::endl;pthread_mutex_init(&_lock, nullptr);p = new Thread(1, std::bind(&UdpServer::Recv, this));c = new Thread(1, std::bind(&UdpServer::Broadcast, this));}void start(){_sock = socket(AF_INET, SOCK_DGRAM, 0); // 创建套接字if (_sock < 0){std::cout << "create _sock error" << std::endl;exit(SOCKET_ERR);}std::cout << "create _sock success" << std::endl;struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_port = htons(_port);local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;if (bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0) // 绑定套接字{std::cout << "bind socket error" << std::endl;exit(BIND_ERR);}std::cout << "bind socket success" << std::endl;p->run();c->run();}void addUser(const std::string &name, const struct sockaddr_in &peer){lockGuard guard(&_lock);auto it = _onlineuser.find(name);if (it != _onlineuser.end()){return;}_onlineuser.insert(std::pair<const std::string, const struct sockaddr_in>(name, peer));}void Recv(){char buf[2056];while (true){struct sockaddr_in peer;socklen_t len = sizeof(peer);int n = recvfrom(_sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&peer, &len);if (n > 0){buf[n] = '\0';}elsecontinue;std::cout << "recv done" << std::endl;std::string clientip = inet_ntoa(peer.sin_addr);uint16_t clientport = ntohs(peer.sin_port);std::cout << clientip << "-" << clientport << "#" << buf << std::endl;std::string name = clientip;name += "-";name += std::to_string(clientport);addUser(name, peer);_rq.push(buf);}}void Broadcast(){while (true){std::string sendstring;_rq.pop(&sendstring);std::vector<struct sockaddr_in> v;{lockGuard guard(&_lock);for (auto user : _onlineuser){v.push_back(user.second);}}for (auto user : v){sendto(_sock, sendstring.c_str(), sendstring.size(), 0, (struct sockaddr*)&(user), sizeof(user));std::cout << "send done" << sendstring << std::endl;}}}~UdpServer(){pthread_mutex_destroy(&_lock);c->join();p->join();delete p, c;}
    };
    

    地址转换函数

    inet_ntoa 是把返回结果放到了静态区,这个时候不需要手动释放。如果多次调用,会覆盖掉上一次的值

    6. 简单的TCP网络程序

    TCP由于是全双工的,所以初始化工作一共有五步

    1. socket
    2. bind
    3. listen
    4. accept
    5. connect

    listen 声明socket fd处于监听状态,并且允许多个客户端来连接等待状态

    accept 三次握手完成后,调用服务器连接,

    id4zt0ehel-1691576412570.png

connect 客户端需要调用这个函数连接服务器

TCP 简单服务器:

#pragma once#include <iostream>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include "err.hpp"static const int defaultport = 8888;
static const int backlog = 32;
using func_t = std::function<std::string(const std::string &)>;class TcpServer;class ThreadData
{
public:int _sock;std::string _clientip;uint16_t _port;TcpServer *_current;
public: ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer *ts): _sock(fd), _clientip(ip), _port(port), _current(ts){}
};class TcpServer
{
public:TcpServer(func_t func, uint16_t port = defaultport) : _port(port), _func(func){}void initServer(){_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){std::cout << "create socket error" << std::endl;exit(SOCKET_ERR);}struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = htonl(INADDR_ANY);local.sin_port = htons(_port);if (bind(_listensock, (struct sockaddr*)&local, sizeof(local)) < 0){std::cout << "create bind error" << std::endl;exit(BIND_ERR);}if (listen(_listensock, backlog) < 0){std::cout << "create listen error" << std::endl;exit(LISTEN_ERR);}}static void* threadRuntinue(void* args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData*>(args);td->_current->server(td->_sock, td->_clientip, td->_port);delete td;return nullptr;}void start(){_quit = false;while (true){struct sockaddr_in client;socklen_t len = sizeof(client);int sock = accept(_listensock, (struct sockaddr*)&client, &len);                                                  if (sock < 0){std::cout << "create accept error" << std::endl;continue;}std::string clientip = inet_ntoa(client.sin_addr);uint16_t clientport = ntohs(client.sin_port);std::cout << "accept success" << clientip << " " << clientport << std::endl;// server(sock, clientip, clientport); 第一个版本// 多线程版本pthread_t tid;ThreadData *threadDate = new ThreadData(sock, clientip, clientport, this);pthread_create(&tid, nullptr, threadRuntinue, threadDate);}}void server(int sock, const std::string ip, uint16_t port){std::string who = ip + "-" + std::to_string(port);char buffer[1024];while (true){size_t s = read(sock, buffer, sizeof(buffer) - 1);if (s > 0){buffer[s] = 0;std::string res = _func(buffer); // 回调传进来的函数std::cout << who << ">>>" << res << std::endl;write(sock, res.c_str(), res.size());}else if (s == 0){close(sock);std::cout << who << "quit, me too" << std::endl;break;}else{close(sock);std::cout << "read error" << std::endl;break;}}}~TcpServer() {}
private:uint16_t _port;int _listensock;bool _quit;func_t _func;
};

TCP 简单的客户端

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "err.hpp"int main(int argc, char *argv[])
{std::string serverip = argv[1];uint16_t serverport = atoi(argv[2]);int sock = socket(AF_INET, SOCK_STREAM, 0);// 要不要bind? 要// 要不要自己bind? 不要,因为client要让OS自动给用户进行bind// 要不要listen?不要 要不要accept?不需要struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);inet_aton(serverip.c_str(), &(server.sin_addr));int cnt = 5;while (connect(sock, (struct sockaddr*)&server, sizeof(server)) != 0){sleep(1);std::cout << "正在尝试重新连接" << std::endl;cnt--;if (cnt < 0) break;}if (cnt <= 0) {std::cout << "连接失败" << std::endl;exit(CONNECT_ERR);}char buffer[1024];while (true){std::string line;std::getline(std::cin, line);write(sock, line.c_str(), line.size());size_t s = read(sock, buffer, sizeof(buffer) - 1);if (s > 0){buffer[s] = 0;std::cout << "server echo" << std::endl;}else if (s == 0){std::cout << "server quit" << std::endl;break;} else {std::cout << "read error" << std::endl;break;}}close(sock);return 0;
}

6.1 TCP socket的封装

#pragma once
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include "Log.hpp"
#include "Err.hpp"static const int gbacklog = 32;
static const int defaultfd = -1;class Sock
{
public:Sock() : _sock(defaultfd) {}void Socket(){_sock = socket(AF_INET, SOCK_STREAM, 0);if (_sock < 0){logMessage(Fatal, "socket error, code : %d", errno);exit(SOCKET_ERR);}}void Bind(const uint16_t &port){struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = INADDR_ANY;if (bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0){logMessage(Fatal, "bind error, code: %d, errstring: %s", errno, strerror(errno));exit(BIND_ERR);}}void Listen(){if (listen(_sock, gbacklog) < 0){logMessage(Fatal, "listen error, code: %d, errstring: %s", errno, strerror(errno));exit(LISTEN_ERR);}}int Accept(std::string *clientip, uint16_t *clientport){struct sockaddr_in temp;socklen_t len = sizeof(temp);int sock = accept(_sock, (struct sockaddr *)&temp, &len);if (sock < 0){logMessage(Warning, "accept error, code: %d, errstring: %s", errno, strerror(errno));}else{*clientip = inet_ntoa(temp.sin_addr);*clientport = ntohs(temp.sin_port);}return sock;}int Connect(const std::string &serverip, const uint16_t &serverport){struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);server.sin_addr.s_addr = inet_addr(serverip.c_str());return connect(_sock, (struct sockaddr *)&server, sizeof(server));}int Fd(){return _sock;}~Sock(){if (_sock != defaultfd)close(_sock);}private:int _sock;
};

6.2 TCP协议通讯流程

服务器初始化:

  • 调用socket,创建文件描述符
  • 调用bind,将当前的文件描述符和ip / port 绑定在一起,如果这个端口被占用了,就会bind失败
  • 调用listen,声明这个文件描述符是服务器的文件描述符,为后面的accept做好准备
  • 调用accept并阻塞,等待客户端连接

建立连接的过程:

  • 调用socket,创建文件描述符
  • 调用connect,向服务器发起连接请求
  • connect会发出SYN并阻塞等待服务器应答
  • 服务器收到客户端的SYN,会应答一个SYN-ACK,表示同意建立连接
  • 客户端收到SYN-ACK后会从connect()返回,同时应答一个ACK
    这个建立过程称为三次握手

TCPVSUDP

  • 可靠传输 VS 不可靠传输
  • 有连接 VS 无连接
  • 字节流 VS 数据报

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

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

相关文章

php通过各种函数判断0和空php实例

php通过各种函数判断0和空php实例 本文给大家介绍php同各种函数判断0和空的方法&#xff0c;在文章给大家补充介绍了php 语法里0不等于null为空的解决办法 补充&#xff1a;下面给大家介绍下php 语法里0不等于null为空的解决办法 今天遇到这样一个问题是这样的: php 语句里,我…

Blender如何给fbx模型添加材质贴图并导出带有材质贴图的模型

推荐&#xff1a;使用 NSDT场景编辑器快速助你搭建可二次编辑的3D应用场景 此教程适合新手用户&#xff0c;专业人士直接可直接绕路。 本教程中介绍了利用Blender建模软件&#xff0c;只需要简单几步就可以为模型添加材质贴&#xff0c;图&#xff0c;并且导出带有材质的模型文…

uniapp 使用canvas画海报(微信小程序)

效果展示&#xff1a; 项目要求&#xff1a;点击分享绘制海报&#xff0c;并实现分享到好友&#xff0c;朋友圈&#xff0c;并保存 先实现绘制海报 <view class"data_item" v-for"(item,index) in dataList" :key"index"click"goDet…

【OpenVINOSharp】 基于C#和OpenVINO2023.0部署Yolov8全系列模型

基于C#和OpenVINO2023.0部署Yolov8全系列模型 1 项目简介1.1 OpenVINOTM 2 OpenVinoSharp2.1 OpenVINOTM 2023.0安装配置2.2 C 动态链接库2.3 C#构建Core推理类2.4 NuGet安装OpenVinoSharp 3 获取和转换Yolov8模型3.1 安装ultralytics3.2 导出yolov8模型3.3 安装OpenVINOTM Pyt…

ssm学院党员管理系统源码和论文PPT

ssm学院党员管理系统源码和论文PPT002 开发工具&#xff1a;idea 数据库mysql5.7(mysql5.7最佳) 数据库链接工具&#xff1a;navcat,小海豚等 开发技术&#xff1a;java ssm tomcat8.5 选题意义、价值和目标&#xff1a; 随着鄂尔多斯应用技术学院招生规模的不断扩大&…

LeetCode209. 长度最小的子数组

题目&#xff1a;LeetCode209. 长度最小的子数组 描述&#xff1a; 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl1, …, numsr-1, numsr] &#xff0c;并返回其长度。如果不存在符合条件的子…

改进DevSecOps框架的 5 大关键技术

Markets and Markets的一项研究显示&#xff0c;全球DevOps的市场规模从2017年的29亿美元增加到2023年的103.1亿美元&#xff0c;预测期的年复合增长率(CAGR)为24.7%。人们对DevOps越来越感兴趣&#xff0c;因为DevOps不仅能够压缩软件的交付周期&#xff0c;还能提高交付的速度…

geeemap学习总结(1)——Anaconda-VSCode-geemap环境安装与配置

配置conda geemap 环境 通过Anaconda配置geemap环境较为方便&#xff0c;首先需在系统中完成 Anaconda安装。创建名为geemap的环境conda create -n geemap切换到新建的环境conda activate geemap安装geemap依赖包conda install -c conda-forge geemap 安装mambaconda install …

ACM算法竞赛中在编辑器中使用输入输出样例-CPH

通用方法 我们可以在编辑器中创建三个文件&#xff0c;一个是main.cpp,一个是test.in,一个是test.out分别用来写代码&#xff0c;输入输入数据&#xff0c;显示输出数据 这种方法的好处是不需要插件&#xff0c;在任何编辑器中都可以实现&#xff0c;例如Devc,sublime,vscode…

【Pyhthon实战】Python对全校电费查询采集并可视化分析

前言 今天,我来说说怎么抓取宿舍电费的过程。我们学校是在完美校园交电费的,我们可以不用取抓包完美校园的数据接口,我们可以直接登录学校的一卡通网站,每个学校都有,大家可以自己找找,这里我为什么要抓包呢,因为学校提供的网站已经打不开了,这里就不介绍怎么抓包了。 …

直播电商赋能跨境业务,Live Market创造全民参与的生态圈

全球疫情的影响让跨境电商业务受到了巨大的冲击&#xff0c;但同时也为跨境电商业务带来了新的机遇和挑战。直播电商作为电子商务行业的新兴业务版块&#xff0c;成为了跨境电商业务的一个重要推动力量。在这个背景下&#xff0c;直播电商成为了跨境电商业务的一个主流业务版块…

php-cgi.exe - FastCGI 进程超过了配置的请求超时时限

解决方案一&#xff1a; 处理(php-cgi.exe - FastCGI 进程超过了配置的请求超时时限)的问题 内容转载&#xff1a; 处理(php-cgi.exe - FastCGI 进程超过了配置的请求超时时限)的问题_php技巧_脚本之家 【详细错误】&#xff1a; HTTP 错误 500.0 - Internal Server Error C:…

python爬虫之scrapy框架介绍

一、Scrapy框架简介 Scrapy 是一个开源的 Python 库和框架&#xff0c;用于从网站上提取数据。它为自从网站爬取数据而设计&#xff0c;也可以用于数据挖掘和信息处理。Scrapy 可以从互联网上自动爬取数据&#xff0c;并将其存储在本地或在 Internet 上进行处理。Scrapy 的目标…

vue3中用watch监听响应式数据的注意点

如果你在vue3中使用reactive()方法创建响应式数据&#xff0c;然后又用torefs()方法将响应式数据解构成单一的ref响应式数据。 此时&#xff0c;如果你想用watch监听解构出来单一的响应式数据&#xff0c;watch不起作用。 此时&#xff0c;你需要用watch监听之前的reactive()…

Python如何打开pkl的sample text?(上篇)

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 昨夜闲潭梦落花&#xff0c;可怜春半不还家。 大家好&#xff0c;我是皮皮。 一、前言 前几天在Python私教群【Emma】问了一个Python读取数据的问题&am…

mysql_docker主从复制_实战_binlog混合模式_天座著

步骤1&#xff1a;拉取镜像 docker pull mariadb:latest 步骤2.1&#xff1a;创建两个文件夹用于放置挂载mysql的my.cnf /tianzuomysqlconf/master /tianzuomysqlconf/slave mkdir /tianzuomysqlconf cd /tianzuomysqlconf mkdir master mkdir slave 步骤2.2&#xff1a;创…

ElasticSearch:全文检索及倒排索引原理

1.从全文检索说起 首先介绍一下结构化与非结构化数据&#xff1a; 结构化数据将数据具有的特征事先以结构化的形式定义好&#xff0c;数据有固定的格式或有限的长度。典型的结构化数据就是传统关系型数据库的表结构&#xff0c;数据特征直接体现在表结构的字段上&#xff0c;…

idea数据库快速上手-库操作与表结构和数据操作

引言 对数据库的操作无非就是执行SQL语句&#xff0c;要想熟练操作数据库&#xff0c;就要熟练运用SQL语句。 一&#xff0c;数据库操作 展示当前服务器内的数据库 -- 展示服务器内的数据库 show databases; show schemas; 执行结果&#xff1a; 创建数据库&#xff1a; --…

云技术-混沌工程

目录 混沌工程 故障注入 监控和观测 自动化和持续集成 混沌工程 混沌工程&#xff08;Chaos Engineering&#xff09;是一种实验性的系统可靠性工程方法&#xff0c;主动引入故障和异常来测试系统的弹性和容错能力。混沌工程的核心思想是通过模拟故障场景来验证系统在各种异…

springboot项目打包 以及打包碰到各种问题

PS:以上资料都来自于网络 1.IDEA 将springboot项目打包 IDEA如何打包springboot成jar包&#xff0c;并运行、停止、重启&#xff0c;本地依赖不能打包怎么办_真是6的不行的博客-CSDN博客 2.[WARNING] Error injecting: org.springframework.boot.maven.RepackageMojo 1.注释…