基于SSH的网络性能测试工具深度解析(C/C++代码实现)
在网络性能测试领域,ping(基于ICMP协议)是最常用的工具之一,但它仅能衡量数据包往返时间(RTT),无法反映应用层(尤其是SSH协议)的实际性能。而sshping作为一款基于SSH协议的性能测试工具,填补了这一空白——它不仅能测量SSH会话的连接与登录延迟,还能精准评估交互式字符回显 latency(延迟)和文件传输吞吐量,为运维人员、开发者优化SSH服务性能提供关键数据支撑。
一、SSHping的核心定位与价值
1. 解决传统工具的局限性
传统ping工具存在两大核心缺陷:
- 协议层不匹配:ICMP协议与SSH(应用层协议)的传输路径、处理逻辑差异大,ICMP延迟无法代表SSH会话的实际响应速度(例如SSH需经过TCP三次握手、身份认证、加密协商等额外流程);
- 场景覆盖不足:无法测试交互式SSH会话(如终端命令输入)的字符回显延迟,也无法评估文件传输(如
scp)的实际吞吐量。
而sshping的设计目标就是**“以真实SSH场景为核心,提供端到端的性能度量”**——它通过模拟真实SSH用户的操作(登录、字符输入、文件传输),输出更贴近实际使用体验的性能数据。
2. 核心应用场景
- SSH服务优化:评估服务器SSH配置(如加密算法、认证方式)对性能的影响;
- 网络链路诊断:在跨地域、跨云厂商场景中,定位SSH连接慢的根源(是网络延迟还是服务端处理瓶颈);
- 运维监控:长期跟踪SSH服务的延迟与吞吐量变化,及时发现性能退化;
- 工具选型验证:对比不同SSH客户端(如OpenSSH、PuTTY)或优化工具(如SSH压缩、Multiplexing)的实际效果。
二、SSHping的核心功能与实现原理
sshping的核心能力分为三大模块:SSH会话建立与认证、交互式字符回显延迟测试、文件传输吞吐量测试。其设计思路是“模拟真实SSH操作流程,嵌入精准计时逻辑”,以下分模块解析原理。
1. 基础:SSH会话建立与认证流程
要进行性能测试,首先需建立合法的SSH连接——这一步的设计关键是“兼容标准SSH协议,覆盖主流认证方式”,确保工具能在大多数环境中使用。
实现逻辑
-
会话初始化:
- 调用
libssh库(SSH协议的开源实现)创建SSH会话对象,设置目标地址、端口、用户名等基础参数; - 支持绑定源IP(
-b选项)、读取SSH配置文件(-F选项)、设置连接超时(-T选项),适配复杂网络环境。
- 调用
-
认证机制适配:
- 先通过“none”认证(SSH协议要求)获取服务端支持的认证方式列表;
- 自动尝试主流认证方式:
- 公钥认证(优先,符合SSH安全最佳实践);
- 键盘交互式认证(应对需二次验证的场景);
- 密码认证(可选,
-p选项,需注意密码明文风险);
- 认证过程中嵌入计时(从“开始连接”到“认证成功”),输出
ssh-Login-Time(SSH登录总延迟)。
设计亮点
- 兼容性优先:依赖
libssh库而非自研SSH协议,确保与OpenSSH等主流服务端兼容; - 安全性考量:默认不推荐密码认证,公钥认证失败时才降级,符合企业安全规范;
- 可观测性:通过
-v( verbose)选项输出认证过程细节,便于排查连接失败问题。
2. 核心1:交互式字符回显延迟测试
交互式SSH会话(如终端输入命令)的核心体验是“输入字符后,多久能看到回显”——这一延迟直接影响用户感知,sshping通过“单字符发送-等待回显-计时”的逻辑精准测量该指标。
测试原理
-
回显环境准备:
- 建立SSH伪终端(PTY)会话(模拟真实终端),执行回显命令(默认
cat > /dev/null,可通过-e选项自定义); - 清空登录后的数据(如欢迎信息),避免干扰测试结果。
- 建立SSH伪终端(PTY)会话(模拟真实终端),执行回显命令(默认
-
单字符往返计时:
- 循环发送随机字符(从
a-z、A-Z中选择,避免特殊字符干扰),每次仅发送1个字符; - 发送前记录时间戳(
tw),收到回显字符后记录时间戳(tr),两者差值即为“单字符往返延迟”; - 支持两种终止条件:字符数量(
-c选项,默认1000个)或时间限制(-t选项),适配不同测试需求。
- 循环发送随机字符(从
-
数据统计与展示:
- 收集所有延迟数据后,计算关键统计指标:
- 最小值(Minimum-Latency):最佳网络条件下的延迟;
- 中位数(Median-Latency):排除极端值后的典型延迟,更贴近用户实际体验;
- 平均值(Average-Latency):整体延迟水平;
- 标准差(Average-Deviation):延迟波动程度(越小说明网络越稳定);
- 最大值(Maximum-Latency):最差情况延迟;
- 支持两种输出格式:
- 原始数值(默认,如
704123 ns),便于脚本解析; - 人类可读格式(
-H选项,如764 us),便于人工阅读; - 可选
ping风格摘要(-P选项),输出rtt min/avg/max/mdev,降低使用门槛。
- 原始数值(默认,如
- 收集所有延迟数据后,计算关键统计指标:
设计亮点
- 贴近真实场景:用伪终端模拟终端环境,而非直接发送TCP数据包,确保延迟数据反映“用户操作-服务端处理-回显”的完整链路;
- 统计科学性:引入中位数和标准差,避免极端值(如网络丢包重传)对结果的干扰,数据更具参考价值;
- 灵活性:支持自定义回显命令(应对特殊服务端配置)和终止条件,适配不同测试目标。
3. 核心2:文件传输吞吐量测试
SSH协议常用于文件传输(如scp),吞吐量(单位时间内传输的字节数)是关键性能指标。sshping通过“生成随机文件-上传/下载-计时”的逻辑,测量scp协议的实际传输速度。
测试原理
-
测试文件准备:
- 生成指定大小的随机二进制文件(默认8MB,
-s选项调整),避免压缩算法(SSH默认关闭压缩,工具也强制禁用)对结果的干扰; - 远程文件路径默认设为
/tmp/sshping-PID.tmp(-z选项自定义),避免权限问题。
- 生成指定大小的随机二进制文件(默认8MB,
-
上传吞吐量测试(Upload-Rate):
- 通过
libssh的SCP模块,将本地随机文件上传到远程路径; - 从“开始上传”到“上传完成”计时(
t2到t3),计算吞吐量:上传大小 / 耗时(单位:B/s)。
- 通过
-
下载吞吐量测试(Download-Rate):
- 从远程路径下载刚才上传的文件,丢弃本地接收数据(仅计时,不存储);
- 同样通过“下载大小 / 耗时”计算下载吞吐量;
- 若远程文件小于测试大小(如
-s 16MB但远程文件仅8MB),自动重复下载直到达到目标大小,确保数据准确性。
设计亮点
- 排除干扰因素:
- 禁用SSH压缩(避免压缩效率影响吞吐量);
- 使用随机文件(避免服务端/客户端缓存对结果的优化,模拟真实“未知文件”传输场景);
- 双向测试:同时测量上传和下载吞吐量,全面反映链路性能(很多场景下上传/下载带宽不对称,如家庭宽带);
- 资源友好:测试文件存放在
/tmp(临时目录,系统重启后自动清理),避免残留垃圾文件。
三、关键技术选型与设计思路
...
int authenticate_pubkey(ssh_session & ses);
int authenticate_password(ssh_session & ses);
int authenticate_kbdint(ssh_session & ses);
int authenticate_none(ssh_session & ses);
int authenticate_all(ssh_session & ses);
ssh_session begin_session();
ssh_channel login_channel(ssh_session & ses);
int run_echo_test(ssh_channel & chn);
int run_upload_test(ssh_session ses);
int run_download_test(ssh_session ses);
void logout_channel(ssh_channel & chn);
void end_session(ssh_session & ses);
...
int main(int argc, char* argv[])
{
...
if (opts[opHELP]) {
option::printUsage(std::cerr, usage);
die(0);
}
if (parse.error() || (argc < 1) || (parse.nonOptionsCount() != 1)) {
option::printUsage(std::cerr, usage); // I wish it didn't use streams
die("Command error, see usage\n", 255);
}
bool anyunk = false;
for (option::Option* opt = opts[opNONE]; opt; opt = opt->next()) {
if (!anyunk) {
option::printUsage(std::cerr, usage);
}
fprintf(stderr, "*** Unknown option %s\n", opt->name);
anyunk = true;
}
if (anyunk) {
die(0);
}
// 将目标用户@地址:端口拆分为多个部分
user = NULL;
addr = (char*)parse.nonOption(0);
port = NULL;
char* atpos = strrchr(addr, '@');
if (atpos) {
user = addr; *atpos = '\0';
if (!*user) user = NULL;
addr = atpos + 1;
}
char* bkpos = strrchr(addr, ']'); // 如果IPv6不查看括号内的内容
char* clpos = strrchr(bkpos? bkpos : addr, ':');
if (clpos) {
*clpos = '\0'; // 在此处结束地址部分,将冒号改为NUL
port = clpos + 1;
if (*port) {
errno = 0;
char* endp = NULL;
long nport = strtol(port, &endp, 10);
if (errno || *endp || (nport < 1) || (nport > 65535)) {
die("Bad port, must be integer from 1 to 65535\n", 255);
}
}
else {
port = NULL;
}
}
if (bkpos) {
// 移除存在的IPv6括号
if (*addr != '[') {
die("Unbalanced IPv6 address brackets\n", 255);
}
*bkpos = '\0';
addr++;
}
if (!strlen(addr)) {
die("Missing target address\n", 255);
}
if (!port) {
port = strdup(DEFAULT_PORT_STR);
}
...
if (opts[opECMD]) {
echo_cmd = opts[opECMD].arg;
}
if (opts[opPWD]) {
pass = (char*)opts[opPWD].arg;
}
if (opts[opID]) {
iden = (char*)opts[opID].arg;
}
if (opts[opBIND]) {
bynd = (char*)opts[opBIND].arg;
}
if (opts[opCFG]) {
ssh_config = (char*)opts[opCFG].arg;
}
if (opts[opREM]) {
remfile = (char*)opts[opREM].arg;
}
else {
snprintf(rembuf, sizeof(rembuf), "/tmp/sshping-%9.9d.tmp", getpid());
remfile = (char*)rembuf;
}
if (opts[opSIZE]) {
size = atoi(opts[opSIZE].arg);
}
if (opts[opNUM]) {
char_limit = atoi(opts[opNUM].arg);
}
if (opts[opTIME]) {
time_limit = atoi(opts[opTIME].arg);
}
if (opts[opCTIM]) {
contim = atoi(opts[opCTIM].arg);
}
if (!opts[opNUM] && !opts[opTIME]) {
char_limit = DEFAULT_COUNT;
}
bool do_echo = !opts[opTEST] || strchr(opts[opTEST].arg, 'e');
bool do_speed = !opts[opTEST] || strchr(opts[opTEST].arg, 's');
if (do_echo && (char_limit <= 0) && (time_limit <= 0)) {
die("For the echo test, a time limit or character count is required\n", 255);
}
if (do_speed && (size <= 0)) {
die("For the speed test, the transfer size must be 1 MB or more\n", 255);
}
verbosity = opts[opVERB].count();
delete[] opts;
delete[] buffer;
if (verbosity) {
printf("User: %s\n", user ? user : "--not specified--");
printf("Host: %s\n", addr);
printf("Port: %s\n", port);
printf("Echo: %s\n", echo_cmd.c_str());
printf(" Cfg: %s\n", ssh_config ? ssh_config : "--default--");
printf("\n");
}
// 开始会话并登录
ssh_session ses = begin_session();
if (!ses) {
die("Cannot establish ssh session", 255);
}
ssh_channel chn = login_channel(ses);
if (!chn) {
die("Cannot login and run echo command", 255);
}
if (do_echo) {
run_echo_test(chn);
}
if (do_speed) {
run_upload_test(ses);
run_download_test(ses);
}
// 程序将等待键盘输入以关闭
if (key_wait) {
printf("Press any key to exit...\n");
keyboard_wait();
}
// Cleanup
if (!clpos && port) free(port);
logout_channel(chn);
end_session(ses);
}
If you need the complete source code, please add the WeChat number (c17865354792)
基于SSH的ping测试,用于测量交互式字符回显延迟以及文件传输吞吐量
Usage: sshping [options] [user@]addr[:port]
Options:
-b --bindaddr IP Bind to this source address
-c --count NCHARS Number of characters to echo, default 1000
-d --delimited Use delimiters in big numbers, eg 1,234,567
-e --echocmd CMD Use CMD for echo command; default: cat > /dev/null
-f --config-file F Read the ssh config file F for options
-h --help Print usage and exit
-H --human-readable Use flesh-friendly units
-i --identity FILE Identity file, ie ssh private keyfile
-p --password PWD Use password PWD (can be seen, use with care)
-P --ping-summary Append measurements in ping-like rtt line format
-r --runtests e|s Run tests e=echo s=speed; default es=both
-s --size MB For speed test, send/recv MB megabytes; default=8 MB
-t --time SECS Time limit for echo test
-T --connect-time S Time limit for ssh connection; default 10 sec
-v --verbose Show more output, use twice for lots: -vv
-z --remote FILE Remote file for up/download tests;
default=/tmp/sshping-PID.tmp
示例
#sshping -d cheyenne.example.com
ssh-Login-Time: 1,835,377,610 ns
Minimum-Latency: 548,993 ns
Median-Latency: 698,494 ns
Average-Latency: 704,123 ns
Average-Deviation: 72,839 ns
Maximum-Latency: 1,075,017 ns
Echo-Count: 1,000 B
Upload-Size: 8,000,000 B
Upload-Rate: 9,371,579 B/s
Download-Size: 8,000,000 B
Download-Rate: 5,493,034 B/s
#sshping -H cheyenne.example.com
ssh-Login-Time: 1.84 s
Minimum-Latency: 633 us
Median-Latency: 751 us
Average-Latency: 764 us
Average-Deviation: 63.7 us
Maximum-Latency: 1.15 ms
Echo-Count: 1.00 kB
Upload-Size: 8.00 MB
Upload-Rate: 9.33 MB/s
Download-Size: 8.00 MB
Download-Rate: 5.48 MB/s
1. 核心技术选型:依赖libssh库
sshping没有自研SSH协议栈,而是基于libssh(开源SSH客户端/服务端库)开发,这一选择的核心考量是:
- 降低开发成本:
libssh已实现SSH协议的核心逻辑(TCP握手、加密协商、认证、SCP/SFTP),避免重复造轮子; - 兼容性保障:
libssh与OpenSSH等主流SSH实现高度兼容,确保工具能在大多数Linux、Windows、macOS环境中运行; - 稳定性可靠:
libssh经过多年迭代,修复了大量协议漏洞和兼容性问题,比自研栈更稳定。
2. 设计原则:“真实场景优先”
所有功能设计都围绕“模拟真实SSH使用”展开,避免“为测试而测试”的失真结果:
- 交互式测试用伪终端(PTY),而非直接TCP通信;
- 文件传输用SCP协议(而非自定义TCP包),与
scp命令性能一致; - 延迟测试用单字符(而非批量数据),模拟用户逐字输入的场景。
3. 易用性设计:兼顾开发者与运维人员
- 命令行选项友好:兼容
ping工具的使用习惯(如-c指定次数),降低学习成本; - 输出格式灵活:
- 机器可读格式(原始数值,便于脚本解析,如监控系统集成);
- 人类可读格式(
-H选项,如1.84 s、9.33 MB/s),便于人工快速理解;
- 错误处理完善:连接失败、认证失败时输出明确错误信息(如“公钥认证失败”),而非模糊的“连接超时”。
四、相关领域知识点拓展
理解sshping,需要结合SSH协议、网络性能测试的基础概念,以下是关键知识点补充。
1. SSH协议的性能影响因素
SSH会话的延迟和吞吐量,受以下因素影响(也是sshping可用于优化的方向):
- 加密算法:对称加密算法(如AES-256-GCM、ChaCha20)的性能差异会影响吞吐量(ChaCha20在ARM架构上性能更优);
- 认证方式:公钥认证(需读取密钥文件)比密码认证略慢,但安全性更高;
- TCP参数:SSH基于TCP协议,TCP窗口大小(
net.core.somaxconn)、超时时间(TCP_TIMEOUT)会影响大文件传输性能; - 网络条件:延迟(RTT)、丢包率、带宽,直接影响字符回显和吞吐量(丢包会导致TCP重传,显著增加延迟)。
2. 网络性能测试的核心指标
sshping输出的指标,对应网络性能测试的通用概念:
- 延迟(Latency):数据从发送到接收的时间差,交互式场景中“中位数延迟”比“平均延迟”更有参考价值(排除极端值);
- 吞吐量(Throughput):单位时间内传输的数据量,受带宽、加密性能、TCP窗口大小限制;
- 抖动(Jitter):延迟的波动程度(对应
sshping的标准差),抖动大说明网络不稳定(如VoIP、视频会议会卡顿)。
3. 与其他SSH性能工具的对比
| 工具 | 核心功能 | 优势 | 劣势 |
|---|---|---|---|
sshping |
字符回显延迟、SCP吞吐量、登录延迟 | 场景贴近真实、指标全面、跨平台 | 不支持SFTP协议、依赖libssh |
scp -v |
仅SCP传输速度(需手动计算) | 无需额外安装(系统自带) | 无延迟测试、无统计指标 |
sshuttle |
SSH隧道性能测试 | 支持隧道场景 | 仅测隧道吞吐量,无交互式延迟 |
tcptrace |
基于TCPdump日志分析SSH性能 | 离线分析、支持复杂链路 | 需提前抓包,操作复杂 |
五、总结
sshping的价值在于“将SSH协议的性能测试从‘模糊感知’转化为‘精准量化’”——它通过模拟真实SSH操作流程,嵌入科学的计时与统计逻辑,输出“登录延迟、字符回显延迟、上传/下载吞吐量”等关键指标,为SSH服务优化、网络链路诊断提供数据支撑。
其设计思路的核心启示是:应用层性能测试工具,必须贴近真实用户场景,才能输出有价值的结果。无论是依赖libssh保障兼容性,还是用伪终端模拟交互式操作,都体现了“真实场景优先”的设计原则。对于运维人员、网络工程师而言,sshping不仅是一款工具,更是理解SSH协议性能瓶颈的“放大镜”。
Welcome to follow WeChat official account【程序猿编码】
更多推荐



所有评论(0)