You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

libssh2非阻塞SFTP示例疑问:为何仅部分函数调用waitsocket()

关于libssh2非阻塞模式下waitsocket()使用差异的疑问与解答

问题描述

我研究了libssh2的非阻塞SFTP写入实现示例,发现处理非阻塞函数调用返回值时存在如下差异:

waitsocket()未被用于libssh2_session_handshake()、libssh2_userauth_password()、libssh2_userauth_publickey_fromfile()、libssh2_sftp_init()、libssh2_sftp_open()等函数,例如:

while((rc = libssh2_session_handshake(session, sock)) == LIBSSH2_ERROR_EAGAIN);
if(rc)
{
    fprintf(stderr, "Failure establishing SSH session: %d\n", rc);
    goto shutdown;
}

但该函数被用于libssh2_sftp_write():

while((nwritten = libssh2_sftp_write(sftp_handle, ptr, nread)) == LIBSSH2_ERROR_EAGAIN) 
{
    waitsocket(sock, session);
}

以下是waitsocket()的实现代码:

static int waitsocket(libssh2_socket_t socket_fd, LIBSSH2_SESSION *session)
{
    struct timeval timeout;
    int rc;
    fd_set fd;
    fd_set *writefd = NULL;
    fd_set *readfd = NULL;
    int dir;
 
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;
 
    FD_ZERO(&fd);
 
    FD_SET(socket_fd, &fd);
 
    /* now make sure we wait in the correct direction */ 
    dir = libssh2_session_block_directions(session);
 
    if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
        readfd = &fd;
 
    if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
        writefd = &fd;
 
    rc = select((int)(socket_fd + 1), readfd, writefd, NULL, &timeout);
 
    return rc;
}

为何waitsocket()未在所有非阻塞函数调用场景中被使用?例如SSH握手成功难道不需要等待Socket可读或可写吗?


解答

这其实是示例代码的简化写法,并非libssh2非阻塞模式的标准正确实现,核心原因如下:

1. 示例的侧重点不同

该示例主要聚焦于演示非阻塞模式下SFTP写入的核心逻辑,对于握手、认证等前期流程,采用了空循环自旋的方式快速带过,目的是减少代码复杂度,让读者把注意力放在SFTP写入的非阻塞处理上。但这种写法在生产环境中完全不可取——空循环会持续占用100%CPU,完全浪费了IO多路复用的优势。

2. 非阻塞模式的本质要求

实际上,在非阻塞模式下,所有返回LIBSSH2_ERROR_EAGAIN的libssh2函数,都应该调用waitsocket()(或类似的IO等待逻辑)来等待Socket进入可读/可写状态后再重试。包括握手、认证、SFTP初始化这些步骤:这些操作需要和SSH服务器进行多轮数据交互,必然会遇到Socket缓冲区已满或需要等待服务器响应的情况,此时返回LIBSSH2_ERROR_EAGAIN是合理的,必须等待IO就绪后再重试,否则会陷入无效的空循环。

3. 示例代码的疏漏

libssh2官方示例有时会为了简洁性牺牲严谨性,这种省略waitsocket()的写法是不规范的。在实际生产代码中,必须统一处理所有非阻塞函数的LIBSSH2_ERROR_EAGAIN返回值,正确写法如下:

int rc;
do {
    rc = libssh2_session_handshake(session, sock);
    if(rc == LIBSSH2_ERROR_EAGAIN) {
        waitsocket(sock, session);
    }
} while(rc == LIBSSH2_ERROR_EAGAIN);

这样才能避免CPU空转,同时保证非阻塞模式的正确性和性能。


内容的提问来源于stack exchange,提问作者asdf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 13:31:01