当前位置: 首页 > news >正文

windows C++ TCP客户端

demo有一下功能

1、心跳包
2、断开重连
3、非阻塞
4、接受数据单独线程处理


#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <string>
#include <process.h>  // 用于Windows下的线程相关操作#pragma comment(lib, "ws2_32.lib")#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 6000
#define RECV_BUF_SIZE 1024
#define HEARTBEAT_INTERVAL 5000  // 心跳包发送间隔,单位:毫秒
#define HEARTBEAT_TIMEOUT 10000  // 心跳包超时时间,单位:毫秒
#define MAX_RECONNECT_ATTEMPTS 10 // 最大重连尝试次数
#define RECONNECT_INTERVAL_SECONDS 2  // 重连间隔时间(秒)class TCPClient
{
public:TCPClient();~TCPClient();bool connectToServer();void disconnect();int sendData(const std::string& data);private:SOCKET m_socket;sockaddr_in m_serverAddr;bool m_connected;// 心跳包相关变量和函数DWORD m_lastHeartbeatTime;bool m_heartbeatSent;HANDLE m_heartbeatThreadHandle;bool m_heartbeatThreadRunning;static unsigned int __stdcall HeartbeatThread(void* param);bool sendHeartbeat();bool checkHeartbeatResponse();// 用于设置套接字为非阻塞模式bool setSocketNonBlocking();// 尝试重连服务器bool reconnect();// 初始化Winsock库bool initializeWinsock();// 关闭套接字并清理相关资源void closeSocket();// 接收数据线程相关函数和变量static unsigned int __stdcall ReceiveDataThread(void* param);HANDLE m_receiveThreadHandle;bool m_receiveThreadRunning;
};// 构造函数,初始化成员变量并初始化Winsock库
TCPClient::TCPClient() : m_socket(INVALID_SOCKET), m_connected(false),m_lastHeartbeatTime(0), m_heartbeatSent(false),m_heartbeatThreadHandle(NULL), m_heartbeatThreadRunning(false),m_receiveThreadHandle(NULL), m_receiveThreadRunning(false) 
{if (!initializeWinsock()) {std::cerr << "初始化Winsock库失败" << std::endl;}m_serverAddr.sin_family = AF_INET;m_serverAddr.sin_port = htons(SERVER_PORT);if (inet_pton(AF_INET, SERVER_IP, &(m_serverAddr.sin_addr)) <= 0) {std::cerr << "inet_pton转换IP地址错误" << std::endl;}
}// 析构函数,断开连接并清理Winsock库,同时关闭心跳包线程和接收数据线程
TCPClient::~TCPClient()
{disconnect();if (m_heartbeatThreadHandle!= NULL) {m_heartbeatThreadRunning = false;// 等待心跳包线程结束WaitForSingleObject(m_heartbeatThreadHandle, INFINITE);CloseHandle(m_heartbeatThreadHandle);}if (m_receiveThreadHandle!= NULL) {m_receiveThreadRunning = false;// 等待接收数据线程结束WaitForSingleObject(m_receiveThreadHandle, INFINITE);CloseHandle(m_receiveThreadHandle);}WSACleanup();
}// 连接服务器的函数
bool TCPClient::connectToServer()
{m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (m_socket == INVALID_SOCKET){std::cerr << "创建套接字失败,错误码: " << WSAGetLastError() << std::endl;return false;}// 设置套接字为非阻塞模式if (!setSocketNonBlocking()){std::cerr << "设置套接字为非阻塞模式失败" << std::endl;closeSocket();return false;}int ret = connect(m_socket, (struct sockaddr*)&m_serverAddr, sizeof(m_serverAddr));if (ret == SOCKET_ERROR){int errCode = WSAGetLastError();if (errCode!= WSAEWOULDBLOCK) {std::cerr << "连接服务器失败,错误码: " << errCode << std::endl;closeSocket();return false;}}// 等待连接真正建立(非阻塞模式下需要轮询检查)timeval timeout;timeout.tv_sec = 5;  // 设置超时时间为5秒timeout.tv_usec = 0;fd_set writefds;FD_ZERO(&writefds);FD_SET(m_socket, &writefds);ret = select(0, NULL, &writefds, NULL, &timeout);if (ret == SOCKET_ERROR){std::cerr << "select函数出错,错误码: " << WSAGetLastError() << std::endl;closeSocket();return false;} else if (ret == 0){std::cerr << "连接超时" << std::endl;closeSocket();return false;}if (FD_ISSET(m_socket, &writefds)){m_connected = true;// 创建并启动接收数据线程m_receiveThreadHandle = (HANDLE)_beginthreadex(NULL, 0, ReceiveDataThread, this, 0, NULL);if (m_receiveThreadHandle == NULL) {std::cerr << "创建接收数据线程失败" << std::endl;closeSocket();return false;}m_receiveThreadRunning = true;// 创建并启动心跳包线程m_heartbeatThreadHandle = (HANDLE)_beginthreadex(NULL, 0, HeartbeatThread, this, 0, NULL);if (m_heartbeatThreadHandle == NULL) {std::cerr << "创建心跳包线程失败" << std::endl;closeSocket();return false;}m_heartbeatThreadRunning = true;std::cout << "成功连接到服务器" << std::endl;return true;}return false;
}// 断开与服务器连接的函数
void TCPClient::disconnect()
{if (m_connected) {closesocket(m_socket);m_connected = false;std::cout << "已断开与服务器的连接" << std::endl;}m_receiveThreadRunning = false;m_heartbeatThreadRunning = false;
}// 发送数据到服务器的函数
int TCPClient::sendData(const std::string& data) 
{if (!m_connected) {if (reconnect()){}else{std::cerr << "未连接到服务器,无法发送数据" << std::endl;return SOCKET_ERROR;}}int ret = send(m_socket, data.c_str(), data.size(), 0);if (ret == SOCKET_ERROR) {int errCode = WSAGetLastError();if (errCode == WSAEWOULDBLOCK) {// 在非阻塞模式下,缓冲区满等情况会返回此错误,可根据需要处理return 0;} else{std::cerr << "发送数据失败,错误码: " << errCode << std::endl;// 如果是连接断开相关错误,尝试重连if (errCode == WSAECONNRESET || errCode == WSAENETRESET){if (reconnect()){// 重连成功后再次发送数据return sendData(data);}}return SOCKET_ERROR;}}return ret;
}// 发送心跳包的函数
bool TCPClient::sendHeartbeat() 
{if (!m_connected){return false;}const std::string heartbeatData = "HEARTBEAT_CLIENT";int ret = send(m_socket, heartbeatData.c_str(), heartbeatData.size(), 0);if (ret == SOCKET_ERROR){int errCode = WSAGetLastError();if (errCode == WSAEWOULDBLOCK){return false;}else {std::cerr << "发送心跳包失败,错误码: " << errCode << std::endl;return false;}}m_heartbeatSent = true;return true;
}// 检查心跳包响应的函数
bool TCPClient::checkHeartbeatResponse()
{if (!m_connected){return false;}char buffer[RECV_BUF_SIZE];int ret = recv(m_socket, buffer, RECV_BUF_SIZE, 0);if (ret == SOCKET_ERROR){int errCode = WSAGetLastError();if (errCode == WSAEWOULDBLOCK) {return false;} else{std::cerr << "接收心跳包响应失败,错误码: " << errCode << std::endl;return false;}}else if (ret == 0){// 对方关闭了连接std::cerr << "服务器关闭了连接" << std::endl;disconnect();return false;} else{std::string response(buffer, ret);if (response == "HEARTBEAT_ACK"){return true;}}return false;
}// 设置套接字为非阻塞模式的函数
bool TCPClient::setSocketNonBlocking()
{u_long mode = 1;int ret = ioctlsocket(m_socket, FIONBIO, &mode);return ret!= SOCKET_ERROR;
}// 尝试重连服务器的函数
bool TCPClient::reconnect()
{int attempt = 0;while (attempt < MAX_RECONNECT_ATTEMPTS){attempt++;closeSocket();Sleep(RECONNECT_INTERVAL_SECONDS * 1000);  // 等待一段时间后重连m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (m_socket == INVALID_SOCKET){std::cerr << "重连时创建套接字失败,错误码: " << WSAGetLastError() << std::endl;continue;}// 设置套接字为非阻塞模式if (!setSocketNonBlocking()){std::cerr << "重连时设置套接字为非阻塞模式失败" << std::endl;closeSocket();continue;}int ret = connect(m_socket, (struct sockaddr*)&m_serverAddr, sizeof(m_serverAddr));if (ret == SOCKET_ERROR){int errCode = WSAGetLastError();if (errCode!= WSAEWOULDBLOCK){std::cerr << "重连失败,错误码: " << errCode << std::endl;continue;}}// 等待连接真正建立(非阻塞模式下需要轮询检查)timeval timeout;timeout.tv_sec = 5;  // 设置超时时间为5秒timeout.tv_usec = 0;fd_set writefds;FD_ZERO(&writefds);FD_SET(m_socket, &writefds);ret = select(0, NULL, &writefds, NULL, &timeout);if (ret == SOCKET_ERROR){std::cerr << "重连时select函数出错,错误码: " << WSAGetLastError() << std::endl;closeSocket();continue;} else if (ret == 0) {std::cerr << "重连超时" << std::endl;closeSocket();continue;}if (FD_ISSET(m_socket, &writefds)){m_connected = true;// 重新创建并启动接收数据线程if (m_receiveThreadHandle!= NULL){m_receiveThreadRunning = false;WaitForSingleObject(m_receiveThreadHandle, INFINITE);CloseHandle(m_receiveThreadHandle);}m_receiveThreadHandle = (HANDLE)_beginthreadex(NULL, 0, ReceiveDataThread, this, 0, NULL);if (m_receiveThreadHandle == NULL) {std::cerr << "重连后创建接收数据线程失败" << std::endl;closeSocket();return false;}m_receiveThreadRunning = true;// 重新创建并启动心跳包线程if (m_heartbeatThreadHandle!= NULL) {m_heartbeatThreadRunning = false;WaitForSingleObject(m_heartbeatThreadHandle, INFINITE);CloseHandle(m_heartbeatThreadHandle);}m_heartbeatThreadHandle = (HANDLE)_beginthreadex(NULL, 0, HeartbeatThread, this, 0, NULL);if (m_heartbeatThreadHandle == NULL) {std::cerr << "重连后创建心跳包线程失败" << std::endl;closeSocket();return false;}m_heartbeatThreadRunning = true;std::cout << "重连成功" << std::endl;return true;}}std::cerr << "达到最大重连尝试次数,重连失败" << std::endl;return false;
}// 初始化Winsock库的函数
bool TCPClient::initializeWinsock() 
{WSADATA wsaData;return WSAStartup(MAKEWORD(2, 2), &wsaData) == 0;
}// 关闭套接字并清理相关资源的函数
void TCPClient::closeSocket() 
{if (m_socket!= INVALID_SOCKET) {closesocket(m_socket);m_socket = INVALID_SOCKET;}
}// 心跳包线程函数
unsigned int __stdcall TCPClient::HeartbeatThread(void* param)
{TCPClient* client = static_cast<TCPClient*>(param);while (client->m_heartbeatThreadRunning && client->m_connected) {DWORD currentTime = GetTickCount();if (currentTime - client->m_lastHeartbeatTime >= HEARTBEAT_INTERVAL){if (client->sendHeartbeat()){client->m_lastHeartbeatTime = currentTime;}}if (currentTime - client->m_lastHeartbeatTime > HEARTBEAT_TIMEOUT){std::cerr << "心跳包超时,通知主线程尝试重连" << std::endl;client->m_connected = false;break;}Sleep(100);  // 适当休眠,避免过于频繁循环检查}return 0;
}// 接收数据线程函数
unsigned int __stdcall TCPClient::ReceiveDataThread(void* param) 
{TCPClient* client = static_cast<TCPClient*>(param);std::string receivedData;while (client->m_receiveThreadRunning && client->m_connected) {char recvBuf[RECV_BUF_SIZE];int ret = recv(client->m_socket, recvBuf, RECV_BUF_SIZE, 0);if (ret == SOCKET_ERROR){int errCode = WSAGetLastError();if (errCode == WSAEWOULDBLOCK){// 在非阻塞模式下,无数据可读时会返回此错误,可根据需要处理continue;}else {std::cerr << "接收数据线程中接收数据失败,错误码: " << errCode << std::endl;// 如果是连接断开相关错误,通知主线程尝试重连if (errCode == WSAECONNRESET || errCode == WSAENETRESET) {client->m_connected = false;break;}}} else if (ret == 0){// 对方关闭了连接std::cerr << "服务器关闭了连接(接收数据线程中)" << std::endl;client->m_connected = false;break;} else{receivedData.assign(recvBuf, ret);std::cout << "接收数据线程从服务器接收到数据: " << receivedData << std::endl;}}return 0;
}int main() 
{TCPClient client;if (client.connectToServer()){while (true){// 发送数据示例std::string sendDataStr = "Hello, server!\n";client.sendData(sendDataStr);// 简单的休眠,避免过于频繁循环Sleep(100);}}return 0;
}

1. 接收数据线程相关的成员变量

  • m_receiveThreadHandle:用于存储接收数据线程的句柄,通过_beginthreadex函数创建线程时获取,用于后续对线程的操作,比如等待线程结束、关闭线程句柄等。
  • m_receiveThreadRunning:布尔类型变量,用于标记接收数据线程是否正在运行,在启动线程时设置为true,当需要停止线程(比如断开连接或者程序结束时)设置为false,线程函数内部会根据这个变量来判断是否继续循环接收数据。

2. connectToServer函数

在成功连接到服务器后,不仅将m_connected标记设置为true,还会创建并启动接收数据线程。通过_beginthreadex函数创建线程,传入ReceiveDataThread函数作为线程执行的入口点,并将当前TCPClient对象指针this作为参数传递进去,以便在线程函数中能够访问对象的成员变量和函数。如果线程创建失败,会关闭套接字并返回false,表示连接失败;若线程创建成功,则将m_receiveThreadRunning设置为true,表示接收数据线程开始运行。

3. disconnect函数

除了关闭套接字并将m_connected标记设置为false外,还会将m_receiveThreadRunning设置为false,通知接收数据线程停止运行。这样线程函数在下次循环判断时就会退出循环,结束线程的执行。

4. reconnect`函数

在重连成功后,除了进行之前的一些连接相关的设置外,还需要重新创建并启动接收数据线程。

相关文章:

windows C++ TCP客户端

demo有一下功能 1、心跳包 2、断开重连 3、非阻塞 4、接受数据单独线程处理 #include <iostream> #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #include <string> #include <process.h> // 用于Windows下的线程相…...

Linux xargs 命令使用教程

简介 xargs 是一个功能强大的 Linux 命令&#xff0c;用于从标准输入构建和执行命令。它接受一个命令的输出&#xff0c;并将其作为参数提供给另一个命令。它在处理大量输入时特别有用&#xff0c;其含义可以解释为&#xff1a;extended arguments&#xff0c;使用 xargs 允许…...

什么是异步处理

什么是异步处理 if ( conditionA && conditionB &#xff09;mqSendService.sendMessageAsync(MqTopicConstant.YOUR_TOPIC, ID,JSONObject.toJSONString(CommonMsg.builder().data(ID).msgType(TypeCode).build()));}sendMessageAsync 发送消息的过程不会阻塞当前的执…...

【解决问题】Java2DRenderer生成图片时中文乱码 Linux安装字体

一&#xff0c;问题 在使用Java2DRenderer框架将html生成图片时&#xff0c;html中的中文文本在图片上显示框框&#xff0c;即出现了中文乱码。在确认使用正确的字符编码utf-8之后&#xff0c;并且确认了修改成unicode也同样乱码的情况下&#xff0c;找到了真正的原因&#xf…...

WPF 依赖属性和附加属性

除了普通的 CLR 属性&#xff0c; WPF 还有一套自己的属性系统。这个系统中的属性称为依赖属性。 1. 依赖属性 为啥叫依赖属性&#xff1f;不叫阿猫阿狗属性&#xff1f; 通常我们定义一个普通 CLR 属性&#xff0c;其实就是获取和设置一个私有字段的值。假设声明了 100 个 …...

leetcode hot100 删除链表的第n个节点

19. 删除链表的倒数第 N 个结点 已解答 中等 相关标签 相关企业 提示 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点 # Definition for singly-linked list. # class ListNode(object): # def __init__(self, val0, nextNon…...

MyBatis-Plus分页拦截器,源码的重构(重构total总数的计算逻辑)

1.1创建ThreadLocal工具类&#xff08;作为业务逻辑结果存放类&#xff09; package org.springblade.sample.utils;public class QueryContext {private static final ThreadLocal<Long> totalInThreadLocal new ThreadLocal<>();public static void setTotalIn…...

记一MySQL连接速度慢的问题

某一个程序启动速度超级慢&#xff0c;查看日志得知是是在Init DruidDataSource ~ {dataSource-1} inited 这一段耗时最长&#xff0c;这一段是Druid 数据源初始化&#xff0c;进行连接的创建等&#xff0c;使用mysql命令行连接发现连接超级慢&#xff0c;可见是在创建连接的时…...

asp.net core webapi项目中 在生产环境中 进不去swagger

builder.WebHost.UseUrls 是 ASP.NET Core 中配置应用程序监听 URL 或端口的方法。通过使用这个方法&#xff0c;你可以指定应用程序应该在哪些 URL 上运行&#xff0c;以便接收 HTTP 请求。 1.在appsetting.json中 添加 "LaunchUrl": "http://*:327"2.在…...

逆向攻防世界CTF系列63-secret-string-400

逆向攻防世界CTF系列63-secret-string-400 丢入exeinfo&#xff0c;查得zip&#xff0c;解压得四个文件 点进Task&#xff0c;查看源码&#xff1a;Test your luck! Enter valid string and you will know flag 顺理成章地看js 定位check函数 调用了machine的loadcode 跟进…...

Datawhale AI 冬令营学习笔记-零编程基础制作井字棋小游戏

井字棋小游戏是通过豆包MarsCode实现的&#xff0c;没有改动任何的代码&#xff0c;全部是通过对话让AI进行优化和改进。 开始进入正题&#xff1a;进入豆包MarsCode在线IDE&#xff0c;直接点击上方蓝字&#xff0c;或复制链接打开: 豆包 MarsCode - 编程助手。 IDE界面&…...

分布式专题(10)之ShardingSphere分库分表实战指南

一、ShardingSphere产品介绍 Apache ShardingSphere 是一款分布式的数据库生态系统&#xff0c; 可以将任意数据库转换为分布式数据库&#xff0c;并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。Apache ShardingSphere 设计哲学为 Database Plus&#xff0c;旨在…...

clickhouse解决suspiciously many的异常

1. 问题背景 clickhouse安装在虚拟机上&#xff0c;持续写入日志时&#xff0c;突然关机&#xff0c;然后重启&#xff0c;会出现clickhouse可以正常启动&#xff0c;但是查询sql语句&#xff0c;提示suspiciously many异常&#xff0c;如图所示 2. 问题修复 touch /data/cl…...

计算机的错误计算(一百九十)

摘要 用两个大模型计算cot(1.234). 其中&#xff0c;1.234是以弧度为单位的角度。结果保留10位有效数字。实验表明&#xff0c;两个的计算公式虽然不同&#xff0c;但是都是正确的。然而&#xff0c;数值计算则是有问题的---包括每一个中间运算与结果。 例1. 计算cot(1.234)…...

STM32-笔记12-实现SysTick模拟多线程流水灯

1、前言 正常STM32实现多线程&#xff0c;需要移植一个操作系统FreeRTOS。但是在这里不移植FreeRTOS怎么实现多线程呢&#xff1f;使用SysTick&#xff0c;那么怎么使用SysTick来模拟多线程呢&#xff1f;前面我们知道SysTick就是一个定时器&#xff0c;它不是在主函数的while循…...

牛客网刷题 ——C语言初阶——BC114 小乐乐排电梯

1.牛客网 &#xff1a;BC114 小乐乐排电梯 题目描述&#xff1a; 小乐乐学校教学楼的电梯前排了很多人&#xff0c;他的前面有n个人在等电梯。电梯每次可以乘坐12人&#xff0c;每次上下需要的时间为4分钟&#xff08;上需要2分钟&#xff0c;下需要2分钟&#xff09;。请帮助…...

web三、 window对象,延时器,定时器,时间戳,location对象(地址),本地存储-localStorage,数组去重new Set

一、window对象 window对象 是一个全局对象&#xff0c;也可以说是JavaScript中的 顶级对象 像document、alert()、console.log()这些都是window的属性&#xff0c;基本BOM的属性和方法都是window的 所有通过 var定义 在全局作用域中的 变量 、 函数 都会变成window对象的属…...

【EthIf-13】EthIfGeneral容器配置-01

1.EthIfGeneral类图结构 下面是EthIfGeneral配置参数的类图&#xff0c;比较重要的参数就是配置&#xff1a; 接收中断是否打开发送确认中断是否打开EthIf轮询周期 1.EthIfGeneral参数的含义...

‘pnpm’ 不是内部或外部命令,也不是可运行的程序或批处理文件。

‘pnpm’ 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。 1.情况: npm -v 和 node -v的都正常就是 pnpm-v 无效 检查环境变量也没看出问题 2.分析 没有正确添加环境变量 3.解决 找到npm的全局安装目录 npm list -g --depth 0这里出现了npm的全局安装…...

ECMAScript 6-11 概述

1. ECMA 介绍 ECMA&#xff08;European Computer Manufacturers Association&#xff09;是欧洲计算机制造商协会&#xff0c;目标是评估、开发和认可电信和计算机标准。1994年后改名为Ecma国际。 2. ECMAScript 是什么 ECMAScript 是由Ecma国际通过ECMA-262标准化的脚本程…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...