在网络性能测试领域,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协议,覆盖主流认证方式”,确保工具能在大多数环境中使用。

实现逻辑
  1. 会话初始化

    • 调用libssh库(SSH协议的开源实现)创建SSH会话对象,设置目标地址、端口、用户名等基础参数;
    • 支持绑定源IP(-b选项)、读取SSH配置文件(-F选项)、设置连接超时(-T选项),适配复杂网络环境。
  2. 认证机制适配

    • 先通过“none”认证(SSH协议要求)获取服务端支持的认证方式列表;
    • 自动尝试主流认证方式:
      • 公钥认证(优先,符合SSH安全最佳实践);
      • 键盘交互式认证(应对需二次验证的场景);
      • 密码认证(可选,-p选项,需注意密码明文风险);
    • 认证过程中嵌入计时(从“开始连接”到“认证成功”),输出ssh-Login-Time(SSH登录总延迟)。
设计亮点
  • 兼容性优先:依赖libssh库而非自研SSH协议,确保与OpenSSH等主流服务端兼容;
  • 安全性考量:默认不推荐密码认证,公钥认证失败时才降级,符合企业安全规范;
  • 可观测性:通过-v( verbose)选项输出认证过程细节,便于排查连接失败问题。

2. 核心1:交互式字符回显延迟测试

交互式SSH会话(如终端输入命令)的核心体验是“输入字符后,多久能看到回显”——这一延迟直接影响用户感知,sshping通过“单字符发送-等待回显-计时”的逻辑精准测量该指标。

测试原理
  1. 回显环境准备

    • 建立SSH伪终端(PTY)会话(模拟真实终端),执行回显命令(默认cat > /dev/null,可通过-e选项自定义);
    • 清空登录后的数据(如欢迎信息),避免干扰测试结果。
  2. 单字符往返计时

    • 循环发送随机字符(从a-zA-Z中选择,避免特殊字符干扰),每次仅发送1个字符;
    • 发送前记录时间戳(tw),收到回显字符后记录时间戳(tr),两者差值即为“单字符往返延迟”;
    • 支持两种终止条件:字符数量(-c选项,默认1000个)或时间限制(-t选项),适配不同测试需求。
  3. 数据统计与展示

    • 收集所有延迟数据后,计算关键统计指标:
      • 最小值(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协议的实际传输速度。

测试原理
  1. 测试文件准备

    • 生成指定大小的随机二进制文件(默认8MB,-s选项调整),避免压缩算法(SSH默认关闭压缩,工具也强制禁用)对结果的干扰;
    • 远程文件路径默认设为/tmp/sshping-PID.tmp-z选项自定义),避免权限问题。
  2. 上传吞吐量测试(Upload-Rate)

    • 通过libssh的SCP模块,将本地随机文件上传到远程路径;
    • 从“开始上传”到“上传完成”计时(t2t3),计算吞吐量:上传大小 / 耗时(单位:B/s)。
  3. 下载吞吐量测试(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 s9.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【程序猿编码

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐