libssh2:如何处理来自ssh服务器的未经请求的数据?

时间:2014-04-23 23:43:56

标签: c libssh2

我有一个使用libssh2管理某些Linux机器的程序。它相当简单:它连接到Linux机器,下载配置文件,然后保持libssh2连接打开(和空闲),这样如果用户按下GUI上的按钮,它就可以向Linux发送适当的shell命令必要的盒子。

它使用非阻塞I / O(通过libssh2_session_set_blocking(session,0)),以便网络I / O可以由单个线程处理,而不会话A的活动被阻止在我的会话B阻塞上读或写等。

这一切都很好。我主要是因为只有当程序一次连接到许多(即几十个)盒子时才会出现一个奇怪的问题。会发生的事情是会话都像往常一样连接,配置文件下载成功,连接都是空闲的,如预期的那样。但是在一些会话中,在下载完成后几毫秒(即在我的libssh2_channel_read()' d所有配置文件字节之后,并且在libssh2_channel_close()成功之后),一些额外的字节数据(通常为104或140)出现在会话的TCP套接字上,准备好由我读取并传递给libssh2。

此时我遇到了问题,因为(据我所知)libssh2会话应该处于空闲状态,而且我不知道如何处理这些未经请求的数据字节。我的选择都不是真的令人满意:

  • 我可以忽略这些字节,但是select()保持唤醒,因为字节在那里,这导致线程旋转,并且只要程序打开,我的CPU使用率就会显着上升,所以那不好。

  • 我可以使用额外数据停止select() - 用于套接字的准备读取,但是我无法判断套接字是否被远程对等端关闭,我想要能够在发生这种情况时通知用户。

  • 我可以recv()多余的字节并扔掉它们,但这显然搞砸了libssh2连接状态机的状态,因为下次我尝试使用它时(例如当用户希望我向机器发送ssh命令)命令不会被发送。

  • 我可以睡觉(1)或关闭连接并重新连接它,但那些对我认为应该正确处理的问题的处理方式是丑陋的。

所以我的问题是

  • 这些奇怪的未经请求的字节是什么?

  • 我应该调用什么函数将它们传递给libssh2以使其满意?

ps以下是通过使用MSG_PEEK标志执行recv()获得的神秘接收字节的一些示例:

--- peek'd data from session #1 (104 bytes): --------------------------------------------
0000: ..#|)62.!.A..... [ab 1f 23 7c 29 36 32 e8 21 cf 41 91 88 de 06 a4]
0016: xc.??n.+aJLS..di [78 63 f2 3f 3f 6e 16 2b 61 4a 4c 53 ab aa 64 69]
0032: ...d.]f.P...4;.3 [c2 d6 9e 64 b4 5d 66 db 50 ba 90 82 34 3b cc 33]
0048: cO"..5..Fr...Yy. [63 4f 22 ba b1 35 9c 00 46 72 a6 9c bb 59 79 a1]
0064: ..L....._..1.>.K [d2 fe 4c 2e e4 81 eb fd 5f 8e f2 31 da 3e c0 4b]
0080: ...........(d... [1d af df 0d 0f d1 ef 1e 07 d1 9f 28 64 f4 07 d3]
0096: 0...sl..         [30 b6 ee f7 73 6c cd 85]

--- peek'd data from session #2 (104 bytes): ----------------------------------------------
0000: g.5"....Q....... [67 d9 35 22 85 90 ab eb 51 95 11 0c e6 ca 9f de]
0016: ...)... .6V&.lkF [ed 04 cb 29 e9 87 95 20 85 36 56 26 a9 6c 6b 46]
0032: ...m).V....JS... [ce b2 d7 6d 29 bb 56 fc 8e 89 a2 4a 53 a9 02 19]
0048: .w1........kY... [a1 77 31 a1 fb f8 b7 94 ee e0 d3 6b 59 ea cc ae]
0064: ...~..[..vZ..... [e3 e8 f2 7e 2e 89 5b c4 82 76 5a da ff b6 ae 91]
0080: ......7.0Z..6g.J [8a cb c8 fc eb e1 37 8e 30 5a e5 b8 36 67 c7 4a]
0096: '...qJS.         [27 da b8 8f 71 4a 53 ef]

--- peek'd data from session #3 (140 bytes): -------------------------------------------------
0000: ...u.d.E..>a.... [04 c3 b1 75 e5 64 d1 45 9e b0 3e 61 81 e9 9b b7]
0016: ..\.....n..D..e. [e4 a2 5c b5 9e da a9 b9 6e 96 b7 44 12 bd 65 d9]
0032: .|(Jp..,k.....r' [c0 7c 28 4a 70 15 90 2c 6b 01 02 1a e6 d1 72 27]
0048: ..%..R]E:...N.CU [0b 8c 25 eb cd 52 5d 45 3a c4 12 f2 4e 11 43 55]
0064: gix$...d:.m..Ps1 [67 69 78 24 a1 e9 85 64 3a d5 6d 91 1a 50 73 31]
0080: Z.]...8#...q..AW [5a ca 5d db 1d 1d 38 23 0e 05 99 71 98 d8 41 57]
0096: ..]....U.7...J.? [ee 86 5d d1 0c b8 ce 55 f6 37 b5 1c 0f 4a b2 3f]
0112: ...10.......h(.* [17 15 ee 31 30 ea ee e0 b9 07 d1 c9 68 28 83 2a]
0128: ....>..g9...     [be c2 e4 f9 3e e2 ea 67 39 0f b2 8c]

1 个答案:

答案 0 :(得分:1)

正确关闭SSH频道涉及的步骤不仅仅是“关闭”#34;在一边。


首先,libssh2_channel_close()的文档说它向远程主机发送了一个" 一个SSH_MSG_CLOSE数据包,作为不再向其发送数据的指令&#34 ;,这是可以的,但是" 远程主机可能仍然会发回数据,直到它发送自己的关闭消息作为响应"。

因此,此命令后跟libssh2_channel_wait_closed(),后者处理剩余的通信" ,直到远程主机关闭指定的通道"。


另外,如所述,在评论this libssh2 commit时," 在没有频道EOF的情况下发送SSH_MSG_CHANNEL_CLOSE在RFC 4254中明确允许,但在EOF之前关闭频道时,某些不符合要求的服务器将挂起或超时 "

因此,在关闭操作本身之前执行libssh2_channel_send_eof()libssh2_channel_wait_eof()的另一个组合可能也是必要的。

自libssh2版本1.2。5(2010年4月13日)起,由于that commit mentioned earlier,如果尚未发送,libssh2应在关闭时自动发送EOF。


libssh2本身有一些例子(如scp_write.c),它们明确地进行了闭包,它看起来像这样:

libssh2_channel_send_eof(channel);
libssh2_channel_wait_eof(channel);
libssh2_channel_wait_closed(channel);
libssh2_channel_free(channel);

(当然,只要为非阻塞套接字返回LIBSSH2_ERROR_EAGAIN,上述所有函数都需要多次调用。)