C++ 实现的Ping类的封装

Ping 使用 Internet 控制消息协议(ICMP)来测试主机之间的连接。当用户发送一个 ping 请求时,则对应的发送一个 ICMP Echo 请求消息到目标主机,并等待目标主机回复一个 ICMP Echo 回应消息。如果目标主机接收到请求并且网络连接正常,则会返回一个回应消息,表示主机之间的网络连接是正常的。如果目标主机没有收到请求消息或网络连接不正常,则不会有回应消息返回。

编译报错问题解决

Windows环境下编程不可避免的会用到windows.hwinsock.h头文件,在默认情况下windows.h头文件会包含winsock.h,此时当尝试包含winsock.h时就会出现头文件定义冲突的情况。解决这个冲突的方式有两种,第一种,在头部定义#define WIN32_LEAN_AND_MEAN来主动去除winsock.h头文件包含。第二种是将#include <winsock2.h>头文件,放在#include<windows.h>之前。两种方式均可,这些方法在进行Windows套接字编程时非常重要,可以防止头文件冲突,确保编译顺利进行。

Ping头文件

如下头文件代码定义了几个结构体,用于表示IP协议头、ICMP协议头和Ping的回复信息。这些结构体主要用于网络编程中,解析和构建网络数据包。

#pragma once
#include <winsock2.h>
#pragma comment(lib, "WS2_32")#define DEF_PACKET_SIZE 32
#define ECHO_REQUEST 8
#define ECHO_REPLY 0struct IPHeader
{BYTE m_byVerHLen;           // 4位版本+4位首部长度BYTE m_byTOS;               // 服务类型USHORT m_usTotalLen;        // 总长度USHORT m_usID;              // 标识USHORT m_usFlagFragOffset;  // 3位标志+13位片偏移BYTE m_byTTL;               // TTLBYTE m_byProtocol;          // 协议USHORT m_usHChecksum;       // 首部检验和ULONG m_ulSrcIP;            // 源IP地址ULONG m_ulDestIP;           // 目的IP地址
};struct ICMPHeader
{BYTE m_byType;            // 类型BYTE m_byCode;            // 代码USHORT m_usChecksum;      // 检验和 USHORT m_usID;            // 标识符USHORT m_usSeq;           // 序号ULONG m_ulTimeStamp;      // 时间戳(非标准ICMP头部)
};struct PingReply
{USHORT m_usSeq;            // 来源IPDWORD m_dwRoundTripTime;   // 时间戳DWORD m_dwBytes;           // 返回长度DWORD m_dwTTL;             // TTL值
};class CPing
{
public:CPing(); // 构造函数~CPing(); // 析构函数// 执行 Ping 操作的方法,传入目标 IP 地址或域名、PingReply 结构体和超时时间BOOL Ping(DWORD dwDestIP, PingReply *pPingReply = NULL, DWORD dwTimeout = 2000);BOOL Ping(char *szDestIP, PingReply *pPingReply = NULL, DWORD dwTimeout = 2000);private:// Ping 核心方法,传入目标 IP 地址、PingReply 结构体和超时时间BOOL PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout);// 计算检验和的方法,传入缓冲区和大小USHORT CalCheckSum(USHORT *pBuffer, int nSize);// 获取时钟计时器的校准值ULONG GetTickCountCalibrate();private:SOCKET m_sockRaw;        // 原始套接字WSAEVENT m_event;        // WSA 事件USHORT m_usCurrentProcID; // 当前进程 IDchar *m_szICMPData;      // ICMP 数据BOOL m_bIsInitSucc;      // 初始化是否成功private:static USHORT s_usPacketSeq; // 静态变量,用于记录 ICMP 包的序列号
};

下面是对每个结构体成员的简要说明:

  1. IPHeader 结构体:
    • m_byVerHLen: 4位版本号 + 4位首部长度。
    • m_byTOS: 服务类型。
    • m_usTotalLen: 总长度。
    • m_usID: 标识。
    • m_usFlagFragOffset: 3位标志 + 13位片偏移。
    • m_byTTL: 生存时间。
    • m_byProtocol: 协议类型。
    • m_usHChecksum: 首部检验和。
    • m_ulSrcIP: 源IP地址。
    • m_ulDestIP: 目的IP地址。
  2. ICMPHeader 结构体:
    • m_byType: ICMP类型。
    • m_byCode: ICMP代码。
    • m_usChecksum: 检验和。
    • m_usID: 标识符。
    • m_usSeq: 序号。
    • m_ulTimeStamp: 时间戳(非标准ICMP头部)。
  3. PingReply 结构体:
    • m_usSeq: 序列号。
    • m_dwRoundTripTime: 往返时间。
    • m_dwBytes: 返回长度。
    • m_dwTTL: TTL值。

这些结构体主要用于在网络编程中处理与IP、ICMP和Ping相关的数据包。在实际应用中,可以使用这些结构体来解析接收到的网络数据包,或者构建要发送的数据包。

类成员说明:

  • m_sockRaw: 用于发送原始套接字的成员变量。
  • m_event: WSA 事件。
  • m_usCurrentProcID: 当前进程 ID。
  • m_szICMPData: ICMP 数据。
  • m_bIsInitSucc: 初始化是否成功的标志。
  • s_usPacketSeq: 静态变量,用于记录 ICMP 包的序列号。

类方法说明:

  • Ping: 执行 Ping 操作的方法,可以传入目标 IP 地址或域名、PingReply 结构体和超时时间。
  • PingCore: Ping 核心方法,用于发送 ICMP 数据包,计算往返时间等。
  • CalCheckSum: 计算检验和的方法。
  • GetTickCountCalibrate: 获取时钟计时器的校准值。

MyPing实现

1. CPing 构造函数和析构函数

CPing::CPing() : m_szICMPData(NULL), m_bIsInitSucc(FALSE)
{// ...(省略其他初始化代码)m_szICMPData = (char*)malloc(DEF_PACKET_SIZE + sizeof(ICMPHeader));if (m_szICMPData == NULL){m_bIsInitSucc = FALSE;}
}CPing::~CPing()
{WSACleanup();if (NULL != m_szICMPData){free(m_szICMPData);m_szICMPData = NULL;}
}

构造函数中,首先进行 Winsock 初始化,创建原始套接字,并分配内存用于存储 ICMP 数据。如果分配内存失败,则初始化标志 m_bIsInitSucc 置为 FALSE。析构函数负责清理 Winsock 资源和释放内存。

2. PingCore 函数

BOOL CPing::PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{// ...(省略其他代码)if (!m_bIsInitSucc){return FALSE;}// ...(省略其他代码)if (sendto(m_sockRaw, m_szICMPData, nICMPDataSize, 0, (struct sockaddr*)&sockaddrDest, nSockaddrDestSize) == SOCKET_ERROR){return FALSE;}// ...(省略其他代码)char recvbuf[256] = { "\0" };while (TRUE){// ...(省略其他代码)if (WSAWaitForMultipleEvents(1, &m_event, FALSE, 100, FALSE) != WSA_WAIT_TIMEOUT){WSANETWORKEVENTS netEvent;WSAEnumNetworkEvents(m_sockRaw, m_event, &netEvent);if (netEvent.lNetworkEvents & FD_READ){// ...(省略其他代码)if (nPacketSize != SOCKET_ERROR){IPHeader *pIPHeader = (IPHeader*)recvbuf;USHORT usIPHeaderLen = (USHORT)((pIPHeader->m_byVerHLen & 0x0f) * 4);ICMPHeader *pICMPHeader = (ICMPHeader*)(recvbuf + usIPHeaderLen);if (pICMPHeader->m_usID == m_usCurrentProcID && pICMPHeader->m_byType == ECHO_REPLY && pICMPHeader->m_usSeq == usSeq){// ...(省略其他代码)return TRUE;}}}}// ...(省略其他代码)if (GetTickCountCalibrate() - ulSendTimestamp >= dwTimeout){return FALSE;}}
}

PingCore 函数是 Ping 工具的核心部分,负责构建 ICMP 报文、发送报文、接收响应报文,并进行超时处理。通过循环等待接收事件,实时检测是否有 ICMP 响应报文到达。在接收到响应后,判断响应是否符合预期条件,如果符合则填充 pPingReply 结构体,并返回 TRUE

3. CalCheckSum 函数

USHORT CPing::CalCheckSum(USHORT *pBuffer, int nSize)
{unsigned long ulCheckSum = 0;while (nSize > 1){ulCheckSum += *pBuffer++;nSize -= sizeof(USHORT);}if (nSize){ulCheckSum += *(UCHAR*)pBuffer;}ulCheckSum = (ulCheckSum >> 16) + (ulCheckSum & 0xffff);ulCheckSum += (ulCheckSum >> 16);return (USHORT)(~ulCheckSum);
}

CalCheckSum 函数用于计算 ICMP 报文的校验和。校验和的计算采用了累加和的方法,最后对累加和进行溢出处理。计算完成后,返回取反后的校验和。

4. GetTickCountCalibrate 函数

ULONG CPing::GetTickCountCalibrate()
{// ...(省略其他代码)return s_ulFirstCallTick + (ULONG)(llCurrentTimeMS - s_ullFirstCallTickMS);
}

GetTickCountCalibrate 函数用于获取经过调校的系统时间。通过计算系统时间相对于 Ping 工具启动时的时间差,实现对系统时间的校准。这样做是为了处理系统时间溢出的情况。

5. Ping 函数

BOOL CPing::Ping(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{return PingCore(dwDestIP, pPingReply, dwTimeout);
}BOOL CPing::Ping(char *szDestIP, PingReply *pPingReply, DWORD dwTimeout)
{if (NULL != szDestIP){return PingCore(inet_addr(szDestIP), pPingReply, dwTimeout);}return FALSE;
}

Ping 函数是对 PingCore 函数的封装,根据目标 IP 地址调用 PingCore 进行 Ping

最后的MyPing.cpp完整实现如下所示;

#include "MyPing.h"USHORT CPing::s_usPacketSeq = 0;// 构造函数
CPing::CPing() :m_szICMPData(NULL), m_bIsInitSucc(FALSE)
{WSADATA WSAData;if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0){// 如果初始化不成功则返回return;}m_event = WSACreateEvent();m_usCurrentProcID = (USHORT)GetCurrentProcessId();m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);if (m_sockRaw == INVALID_SOCKET){// 10013 以一种访问权限不允许的方式做了一个访问套接字的尝试return;}else{WSAEventSelect(m_sockRaw, m_event, FD_READ);m_bIsInitSucc = TRUE;m_szICMPData = (char*)malloc(DEF_PACKET_SIZE + sizeof(ICMPHeader));if (m_szICMPData == NULL){m_bIsInitSucc = FALSE;}}
}// 析构函数
CPing::~CPing()
{WSACleanup();if (NULL != m_szICMPData){free(m_szICMPData);m_szICMPData = NULL;}
}// Ping 方法,传入目标 IP 地址或域名、PingReply 结构体和超时时间
BOOL CPing::Ping(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{return PingCore(dwDestIP, pPingReply, dwTimeout);
}// Ping 方法,传入目标 IP 地址或域名、PingReply 结构体和超时时间
BOOL CPing::Ping(char *szDestIP, PingReply *pPingReply, DWORD dwTimeout)
{if (NULL != szDestIP){return PingCore(inet_addr(szDestIP), pPingReply, dwTimeout);}return FALSE;
}// Ping 核心方法,传入目标 IP 地址、PingReply 结构体和超时时间
BOOL CPing::PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{// 判断初始化是否成功if (!m_bIsInitSucc){return FALSE;}// 配置 SOCKETsockaddr_in sockaddrDest;sockaddrDest.sin_family = AF_INET;sockaddrDest.sin_addr.s_addr = dwDestIP;int nSockaddrDestSize = sizeof(sockaddrDest);// 构建 ICMP 包int nICMPDataSize = DEF_PACKET_SIZE + sizeof(ICMPHeader);ULONG ulSendTimestamp = GetTickCountCalibrate();USHORT usSeq = ++s_usPacketSeq;memset(m_szICMPData, 0, nICMPDataSize);ICMPHeader *pICMPHeader = (ICMPHeader*)m_szICMPData;pICMPHeader->m_byType = ECHO_REQUEST;pICMPHeader->m_byCode = 0;pICMPHeader->m_usID = m_usCurrentProcID;pICMPHeader->m_usSeq = usSeq;pICMPHeader->m_ulTimeStamp = ulSendTimestamp;pICMPHeader->m_usChecksum = CalCheckSum((USHORT*)m_szICMPData, nICMPDataSize);// 发送 ICMP 报文if (sendto(m_sockRaw, m_szICMPData, nICMPDataSize, 0, (struct sockaddr*)&sockaddrDest, nSockaddrDestSize) == SOCKET_ERROR){return FALSE;}// 判断是否需要接收相应报文if (pPingReply == NULL){return TRUE;}char recvbuf[256] = { "\0" };while (TRUE){// 接收响应报文if (WSAWaitForMultipleEvents(1, &m_event, FALSE, 100, FALSE) != WSA_WAIT_TIMEOUT){WSANETWORKEVENTS netEvent;WSAEnumNetworkEvents(m_sockRaw, m_event, &netEvent);if (netEvent.lNetworkEvents & FD_READ){ULONG nRecvTimestamp = GetTickCountCalibrate();int nPacketSize = recvfrom(m_sockRaw, recvbuf, 256, 0, (struct sockaddr*)&sockaddrDest, &nSockaddrDestSize);if (nPacketSize != SOCKET_ERROR){IPHeader *pIPHeader = (IPHeader*)recvbuf;USHORT usIPHeaderLen = (USHORT)((pIPHeader->m_byVerHLen & 0x0f) * 4);ICMPHeader *pICMPHeader = (ICMPHeader*)(recvbuf + usIPHeaderLen);if (pICMPHeader->m_usID == m_usCurrentProcID    // 是当前进程发出的报文&& pICMPHeader->m_byType == ECHO_REPLY      // 是 ICMP 响应报文&& pICMPHeader->m_usSeq == usSeq            // 是本次请求报文的响应报文){pPingReply->m_usSeq = usSeq;pPingReply->m_dwRoundTripTime = nRecvTimestamp - pICMPHeader->m_ulTimeStamp;pPingReply->m_dwBytes = nPacketSize - usIPHeaderLen - sizeof(ICMPHeader);pPingReply->m_dwTTL = pIPHeader->m_byTTL;return TRUE;}}}}// 超时if (GetTickCountCalibrate() - ulSendTimestamp >= dwTimeout){return FALSE;}}
}// 计算检验和的方法
USHORT CPing::CalCheckSum(USHORT *pBuffer, int nSize)
{unsigned long ulCheckSum = 0;while (nSize > 1){ulCheckSum += *pBuffer++;nSize -= sizeof(USHORT);}if (nSize){ulCheckSum += *(UCHAR*)pBuffer;}ulCheckSum = (ulCheckSum >> 16) + (ulCheckSum & 0xffff);ulCheckSum += (ulCheckSum >> 16);return (USHORT)(~ulCheckSum);
}// 获取时钟计时器的校准值
ULONG CPing::GetTickCountCalibrate()
{static ULONG s_ulFirstCallTick = 0;static LONGLONG s_ullFirstCallTickMS = 0;SYSTEMTIME systemtime;FILETIME filetime;GetLocalTime(&systemtime);SystemTimeToFileTime(&systemtime, &filetime);LARGE_INTEGER liCurrentTime;liCurrentTime.HighPart = filetime.dwHighDateTime;liCurrentTime.LowPart = filetime.dwLowDateTime;LONGLONG llCurrentTimeMS = liCurrentTime.QuadPart / 10000;if (s_ulFirstCallTick == 0){s_ulFirstCallTick = GetTickCount();}if (s_ullFirstCallTickMS == 0){s_ullFirstCallTickMS = llCurrentTimeMS;}return s_ulFirstCallTick + (ULONG)(llCurrentTimeMS - s_ullFirstCallTickMS);
}

如何使用

在主程序中直接引入头文件MyPing.h,并在main()函数中直接调用CPing类即可实现探测主机是否存活。

探测主机是否存活

#include "MyPing.h"
#include <iostream>// 探测主机是否存活
bool TestPing(char *szIP)
{CPing objPing;PingReply reply;objPing.Ping(szIP, &reply);if (reply.m_dwTTL >= 10 && reply.m_dwTTL <= 255){return true;}return false;
}int main(int argc, char *argv[])
{bool is_open = TestPing("202.89.233.100");std::cout << "本机是否存活: " << is_open << std::endl;system("pause");return 0;
}

运行效果如下所示;

模拟系统Ping测试

#include "MyPing.h"
#include <iostream>// 模拟系统Ping测试
void SystemPing(char *szIP, int szCount)
{CPing objPing;PingReply reply;for (int x = 0; x < szCount; x++){objPing.Ping(szIP, &reply);std::cout << "探测主机: " << szIP << " 默认字节: " << DEF_PACKET_SIZE << " 发送长度: " << reply.m_dwBytes << " 时间: " << reply.m_dwRoundTripTime << " TTL: " << reply.m_dwTTL << std::endl;Sleep(1000);}
}int main(int argc, char *argv[])
{SystemPing("202.89.233.100", 5);system("pause");return 0;
}

运行效果如下所示;

参考资料

代码的实现来源于博客园Snser博主,此处仅用于功能收录以便于后期在项目中应用。

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

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

相关文章

SpringCloud+Nacos项目集成Seata分布式事务

上一篇&#xff1a; 《 Seata-分布式事务介绍 》&#xff1a; 简单介绍了分布式事务的实现方式&#xff0c;以及详细讲述了Seata-AT模式的两阶段提交步骤流程。 完整示例项目代码地址&#xff1a; https://gitee.com/cnyunze/yz-seata.git Seata快速上手 安装教程Seata Server…

动手学深度学习笔记

1. 深度学习基础与MLP 1.1 框架&#xff1a; 线性回归&#xff1b; Softmax回归&#xff08;实际上用于分类问题&#xff09;&#xff1b; 感知机与多层感知机&#xff1b; 模型选择&#xff1b; 权重衰退&#xff08;weight decay&#xff09;&#xff1b; 丢弃法&…

sql面试题之累计消耗问题

sql中累计求和是我们比较经常遇到的问题&#xff0c;那么与之相反的累计消耗的问题不知你是否挑战过 –问题&#xff1a;在活动大促中&#xff0c;有玩游戏瓜分奖金环节。现有奖金池为3000元&#xff0c;代表奖金池中的初始额度 表中的数据代表每一个用户和其对应的得分&#…

Java 并发编程面试题——Java 线程间通信方式

目录 1.✨Java 线程间有哪些通信方式&#xff1f;1.1.volatile 和 synchronized 关键字1.2.等待/通知机制1.2.1.概述1.2.2.经典范式 1.3.管道输入/输出流1.4.信号量 2.Thread.join() 有什么作用&#xff1f;它的使用场景是什么&#xff1f;3.Java 中需要主线程等待子线程执行完…

java:封装统一的响应体code、data、msg、paging

背景 我们在写接口的时候一般不会直接返回给前端数据&#xff0c;而是会有响应体&#xff0c;比如 code、data、msg&#xff0c;这样就有一个统一的结构方便前端处理&#xff0c;那么今天就来封装一个统一的响应体 封装基本响应体 1、在 config 包里新建 ApiResponse.java …

5+铜死亡+预后模型+分型生信思路,热点搭配免疫相关思路

今天给同学们分享一篇生信文章“The pathogenesis of DLD-mediated cuproptosis induced spinal cord injury and its regulation on immune microenvironment”&#xff0c;这篇文章发表在Front Cell Neurosci期刊上&#xff0c;影响因子为5.3。 结果解读&#xff1a; 基因芯…

LeetCode - 110. 平衡二叉树(C语言,二叉树,配图,简单)

根据题意&#xff0c;我们只需要比较当前节点的左右子树高度差是否小于1&#xff0c;利用分治法&#xff0c;只需要满足&#xff1a; 1. 根节点的左右子树的高度差小于1。 2. 根节点左右子树的满足高度差小于1&#xff0c;在往下走&#xff0c;判断左子树根节点的左右子树是否满…

质量检查管理制度

质量检查管理制度 建立健全质量检查管理制度&#xff0c;明确过程检查、最终检查、质量评定、检查记录和检查报告等要求

40.坑王驾到第六期:这是什么奇葩错误?

一、问题再现 在HbuilderX中引用了一个checkALL模块&#xff0c;正常编译然后启动到微信开发者工具&#xff0c;工具报错&#xff0c;找不到XXXXXX/index2.js。为什么编译后在微信工具中会自动加一个2呢&#xff1f;&#xff1f;&#xff1f;&#xff1f; 二、解决问题 经过清…

【Node.js】基础梳理 6 - MongoDB

写在最前&#xff1a;跟着视频学习只是为了在新手期快速入门。想要学习全面、进阶的知识&#xff0c;需要格外注重实战和官方技术文档&#xff0c;文档建议作为手册使用 系列文章 【Node.js】笔记整理 1 - 基础知识【Node.js】笔记整理 2 - 常用模块【Node.js】笔记整理 3 - n…

【LeetCode】每日一题 2023_12_5 到达首都的最少油耗(树,搜索)

文章目录 刷题前唠嗑题目&#xff1a;到达首都的最少油耗题目描述代码与解题思路 刷题前唠嗑 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 题目&#xff1a;到达首都的最少油耗 题目链接&#xff1a;2477. 到达首都的最少油耗 题目描述 代码与解题思路 …

Python代码编译并生成Docker镜像

Python代码编译并生成Docker镜像 前言 实际python项目交付时往往有针对关键代码进行保护的需求&#xff0c;本文介绍了一种简单可行的方案&#xff1a;1. 在Linux系统上先将 .py 文件编译为 .so 文件&#xff0c;2. 将整个项目打包成Docker镜像&#xff08;解决 .so 文件的环…

【性能测试】业务/吞吐量与存量数据设计关系+压测常见解决方案

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、性能测试中业务…

C语言错误处理之 “信号处理方式<signal.h>及signal函数等内置函数”

目录 前言 signal.h头文件 信号宏 signal函数 实例&#xff1a;在Linux环境下验证signal函数 实例&#xff1a;在Linux中演示保存signal函数的返回值 预定义的信号处理函数&#xff08;简单了解&#xff09; SIG_DFL函数 SIG_IGN函数 raise函数 实例&#xff1a;测试…

leetcode 255.用队列实现栈

255.用队列实现栈 不出意外大概率这几天都会更新 leetcode&#xff0c;如果没有做新的题&#xff0c;大概就会把 leetcode 之前写过的题整理&#xff08;单链表的题目居多一点&#xff09;出来写成博客 今天讲的题蛮容易出错的&#xff08;注意传参啊&#xff0c;最好把队列的…

渗透测试——五、网站漏洞——SQL注入

一、走进DVWA测试网站 1、网站渗透测试步骤 (1)收集信息 1、获取域名的 Whois 信息&#xff0c;获取注册者邮箱、姓名、电话等。2、查询服务器旁站及子域名站点&#xff0c;因为主站一般比较难&#xff0c;所以先看看旁站有没有通用性的CMS或者其他漏洞。3、查看服务器操作系…

ROS话题通信基本操作(C++)

目录 一、发布 1、实现步骤 2、代码实例 二、接收 1、实现步骤 2、代码实例 三、配置运行 1、修改CMakeLists.txt 2、运行结果 一、发布 1、实现步骤 1.包含头文件 2.初始化 ROS 节点:命名(唯一) 3.实例化 ROS 句柄 4.实例化 发布者 对象 5.组织被发布的数据&#…

Pytest做性能测试?

Pytest其实也是可以做性能测试或者基准测试的。是非常方便的。 可以考虑使用Pytest-benchmark类库进行。 安装pytest-benchmark 首先&#xff0c;确保已经安装了pytest和pytest-benchmark插件。可以使用以下命令安装插件&#xff1a; pip install pytest pytest-benchmark …

JSP控制项目启动后默认去访问指定的WebServlet 而不是index.jsp

我的 index.jsp 代码是这样 现在每次启动 访问的都是index.jsp 这也是它的默认配置 我这里写了一个 WebServlet 代码是这样 简单可以理解为 我们定义了WebServlet 访问路径为1cginServlet 其中在request作用域中 定义了一个userName值为 欢迎来到jsp世界 然后 跳转向 page.j…

Android : AndroidStudio开发工具优化

1.开启 gradle 单独的守护进程 Windows: 进入目录 C:\Users\Administrator\.gradle 创建文件&#xff1a; gradle.properties # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Settings specified in this file will override any Gradle s…