- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在尝试重现一个问题,当我们的一些客户在使用 SFTP 协议(protocol)下载时会收到错误的文件内容。
根据我们的 SFTP 服务器 (CrushFTP) 日志,他们可能会在一个 session 中打开多个文件,然后使用一些流水线来下载文件。我不知道他们使用什么类型的库,因为他们为此使用了一些 SAAS 提供商。
我正在尝试使用 libssh2 重现该行为,但在第一个打开的文件返回 LIBSSH2_ERROR_EAGAIN 后,在第二个打开的文件上异步调用 libssh2_sftp_read 时出现 sftp_read() 内部错误
- 即使在本地主机上也是如此连接到 OpenSSH。
浏览时SSH File Transfer Protocol IETF Draft我可以看到该协议(protocol)允许同时打开多个文件并使用多个 SSH_FXP_READ 请求请求它们的内容,而无需等待响应。
下面是我用于测试的代码(SSCCE,但相当长 - C 非常冗长)- 使用 gcc sftp_multifile.c -lssh2 -Wall -g -o sftp_multifile
编译并测试:
./sftp_multifile localhost 22 testusername testpassword /usr/share/dict/words /usr/share/doc/words/readme.txt
connect try: ai_family=10 ai_socktype=1 ai_protocol=6 addr=::1 port=22
opening: /usr/share/dict/words
opening: /usr/share/doc/words/readme.txt
reading: /usr/share/dict/words from 10134400 to 140736927200896
read result: -37
reading: /usr/share/doc/words/readme.txt from 10135536 to 140736927201920
read result: -31
Bad read result: -31
Read error: /usr/share/doc/words/readme.txt: sftp_read() internal error
是我的代码出错了,还是 libssh2 只是不支持流水线化 libssh2_sftp_read 用于多个打开的文件,或者这只是 libssh2 中的一个错误,应该报告给它的维护者?
#include <libssh2.h>
#include <libssh2_sftp.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
#include <arpa/inet.h>
#define CHUNK_SIZE (1024)
int sftp_connect(char const* hostname, char const* port)
{
struct addrinfo* addrinfo_result, *addrinfo_current;
int sfd, s;
struct addrinfo addrinfo_hints = {0};
addrinfo_hints.ai_family = AF_UNSPEC;
addrinfo_hints.ai_socktype = SOCK_STREAM;
s = getaddrinfo(hostname, port, &addrinfo_hints, &addrinfo_result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
for (addrinfo_current = addrinfo_result; addrinfo_current != NULL; addrinfo_current = addrinfo_current->ai_next) {
char s[INET6_ADDRSTRLEN];
sfd = socket(addrinfo_current->ai_family, addrinfo_current->ai_socktype, addrinfo_current->ai_protocol);
if (sfd == -1) continue;
{
void* addr;
in_port_t port;
switch(addrinfo_current->ai_family) {
case AF_INET: {
struct sockaddr_in* sockaddr = (struct sockaddr_in*)addrinfo_current->ai_addr;
addr = &(sockaddr->sin_addr);
port = ntohs(sockaddr->sin_port);
break;
}
case AF_INET6: {
struct sockaddr_in6* sockaddr = (struct sockaddr_in6*)addrinfo_current->ai_addr;
addr = &(sockaddr->sin6_addr);
port = ntohs(sockaddr->sin6_port);
break;
}
default:
fprintf(stderr, "unknown family: %d\n", addrinfo_current->ai_family);
exit(EXIT_FAILURE);
}
inet_ntop(addrinfo_current->ai_family, addr, s, INET6_ADDRSTRLEN);
fprintf(stderr, "connect try: ai_family=%d ai_socktype=%d ai_protocol=%d addr=%s port=%d\n", addrinfo_current->ai_family, addrinfo_current->ai_socktype, addrinfo_current->ai_protocol, s, port);
}
if (connect(sfd, addrinfo_current->ai_addr, addrinfo_current->ai_addrlen) != 0) {
fprintf(stderr, "connect: %s\n", strerror(errno));
close(sfd);
continue;
}
break;
}
if (addrinfo_current == NULL) {
fprintf(stderr, "connection failed\n");
exit(EXIT_FAILURE);
}
freeaddrinfo(addrinfo_result);
return sfd;
}
void retrieve_files(int socket, LIBSSH2_SESSION* session, LIBSSH2_SFTP *sftp_session, int filec, char *filev[])
{
LIBSSH2_SFTP_HANDLE* sftp_handles[filec];
int finished[filec];
int toread = filec;
for(int i=0; i<filec; i++) {
fprintf(stderr, "opening: %s\n", filev[i]);
sftp_handles[i] = libssh2_sftp_open(sftp_session, filev[i], LIBSSH2_FXF_READ, 0);
if (!sftp_handles[i]) {
char* errmsg;
libssh2_session_last_error(session, &errmsg, NULL, 0);
fprintf(stderr, "Failure opening remote file: %s: %s\n", filev[i], errmsg);
exit(EXIT_FAILURE);
}
finished[i]=0;
}
char buffer[CHUNK_SIZE];
libssh2_session_set_blocking(session, 0);
while (toread) {
for(int i=0; i<filec; i++) {
if (finished[i]) {
continue;
} else {
int read_result;
do {
fprintf(stderr, "reading: %s from %ld to %ld\n", filev[i], (long)sftp_handles[i], (long)(buffer+CHUNK_SIZE*i));
read_result = libssh2_sftp_read(sftp_handles[i], buffer, CHUNK_SIZE);
fprintf(stderr, "read result: %d\n", read_result);
if (read_result > 0) {
printf("%s: ", filev[i]);
fwrite(buffer, sizeof(char), read_result, stdout);
printf("\n");
} else {
break;
}
} while (1);
if (read_result == LIBSSH2_ERROR_EAGAIN) {
continue;
} else if (read_result == 0) {
finished[i] = 1;
toread--;
} else {
fprintf(stderr, "Bad read result: %d\n", read_result);
char* errmsg;
libssh2_session_last_error(session, &errmsg, NULL, 0);
fprintf(stderr, "Read error: %s: %s\n", filev[i], errmsg);
exit(EXIT_FAILURE);
}
}
}
if (toread) {
struct pollfd fds[1] = {0};
fds[0].fd = socket;
fds[0].events = POLLIN;
poll(fds, 1, 3000);
}
}
}
int main(int argc, char *argv[])
{
LIBSSH2_SESSION *session;
LIBSSH2_SFTP *sftp_session;
int rc, sock;
if (argc<6) {
fprintf(stderr, "Usage: %s hostname port username password file1 [file2] [file3...]\n", argv[0]);
return EXIT_FAILURE;
}
sock = sftp_connect(argv[1], argv[2]);
session = libssh2_session_init();
if (!session) {
return EXIT_FAILURE;
}
rc = libssh2_session_handshake(session, sock);
if (rc) {
char* errmsg;
libssh2_session_last_error(session, &errmsg, NULL, 0);
fprintf(stderr, "Failure establishing SSH session: %s\n", errmsg);
return EXIT_FAILURE;
}
rc = libssh2_userauth_password(session, argv[3], argv[4]);
if (rc) {
char* errmsg;
libssh2_session_last_error(session, &errmsg, NULL, 0);
fprintf(stderr, "%s\n", errmsg);
return EXIT_FAILURE;
}
sftp_session = libssh2_sftp_init(session);
if (sftp_session == NULL) {
char* errmsg;
libssh2_session_last_error(session, &errmsg, NULL, 0);
fprintf(stderr, "Unable to init SFTP session: %s\n", errmsg);
return EXIT_FAILURE;
}
retrieve_files(sock, session, sftp_session, argc-5, argv+5);
libssh2_session_set_blocking(session, 1);
libssh2_sftp_shutdown(sftp_session);
libssh2_session_disconnect(session, "");
libssh2_session_free(session);
libssh2_exit();
return EXIT_SUCCESS;
}
最佳答案
很有趣。我在 Arch 上使用 libssh2 1.8.1-1
。我将你的代码粘贴到 sftp_multifile.c
中,用
sftp_multifile: sftp_multifile.c
gcc -g -O -Wall $< -o $@ -lssh2
并将 args 写入 55728938.<count>
(不打算在此处发布 ;-)。只有一个文件,我得到
$ ./sftp_multifile $(cat 55728938.one)
connect try: ai_family=10 ai_socktype=1 ai_protocol=6 addr=::1 port=22
opening: path/text1
reading: path/text1 from 94221524604816 to 140729241245632
read result: -37
reading: path/text1 from 94221524604816 to 140729241245632
read result: 21
reading: path/text1 from 94221524604816 to 140729241245632
read result: -37
reading: path/text1 from 94221524604816 to 140729241245632
read result: 0
path/text1: So much depends
upon
...我觉得不错。但是对于两个或更多文件,
$ ./sftp_multifile $(cat 55728938.two)
connect try: ai_family=10 ai_socktype=1 ai_protocol=6 addr=::1 port=22
opening: path/text1
opening: path/text2
reading: path/text1 from 94473513555856 to 140727557764112
read result: -37
reading: path/text2 from 94473513556256 to 140727557765136
read result: -31
Bad read result: -31
Read error: path/text2: sftp_read() internal error
$ ./sftp_multifile $(cat 55728938.three)
connect try: ai_family=10 ai_socktype=1 ai_protocol=6 addr=::1 port=22
opening: path/text1
opening: path/text2
opening: path/text3
reading: path/text1 from 94652164568976 to 140727538608784
read result: -37
reading: path/text2 from 94652164569376 to 140727538609808
read result: -31
Bad read result: -31
Read error: path/text2: sftp_read() internal error
这和你看到的一致。由于打包的 libssh2
不是用调试符号构建的,所以 gdb
帮不上什么忙。我建议通过 libssh2 mailing list 解决这个问题。
编辑添加:
删除 libssh2_session_set_blocking(session, 0);
行似乎可以解决问题。
$ make && ./sftp_multifile $(cat 55728938.four)
gcc -g -O -Wall sftp_multifile.c -o sftp_multifile -lssh2
connect try: ai_family=10 ai_socktype=1 ai_protocol=6 addr=::1 port=22
opening: /home/pi/stackoverflow/text1
opening: /home/pi/stackoverflow/text2
opening: /home/pi/stackoverflow/text3
opening: /home/pi/stackoverflow/text4
4 of 4
reading: /home/pi/stackoverflow/text1 from 5453288 to 2129740008
read result: 21
/home/pi/stackoverflow/text1: So much depends
upon
reading: /home/pi/stackoverflow/text1 from 5453288 to 2129740008
read result: 0
3 of 4
reading: /home/pi/stackoverflow/text2 from 5453640 to 2129741032
read result: 19
/home/pi/stackoverflow/text2: a red wheel
barrow
reading: /home/pi/stackoverflow/text2 from 5453640 to 2129741032
read result: 0
2 of 4
reading: /home/pi/stackoverflow/text3 from 5455408 to 2129742056
read result: 23
/home/pi/stackoverflow/text3: glazed with rain
water
reading: /home/pi/stackoverflow/text3 from 5455408 to 2129742056
read result: 0
1 of 4
reading: /home/pi/stackoverflow/text4 from 5455760 to 2129743080
read result: 27
/home/pi/stackoverflow/text4: beside the white
chickens.
reading: /home/pi/stackoverflow/text4 from 5455760 to 2129743080
read result: 0
所有四个文件,存在并说明。 Arch ( libssh2-1.8
) 和 Raspbian ( libssh-1.7
) 上的结果相同。
关于c - libssh2 SFTP 管道化多个文件 : sftp_read() internal error,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55728938/
我试图将我的程序与 libssh 静态库链接。 以下是我从 libssh 教程中复制的简单代码: //sshtest.c #define LIBSSH_STATIC 1 #include #incl
我想知道在使用 ARM 的 Sourcery 工具链进行交叉编译时是否可以将我的应用程序链接到 libssh。我的主机系统是Ubuntu x86_64 :~/c/ssh$ arm-none-linux
我想知道在使用 ARM 的 Sourcery 工具链进行交叉编译时是否可以将我的应用程序链接到 libssh。我的主机系统是 Ubuntu x86_64 :~/c/ssh$ arm-none-linu
我正在使用 libssh。 调用ssh_channel_write后,我必须知道是否有数据要读取。 ( 但我不想检索数据。 ) 如果没有数据要读取(例如10s后),我会调用ssh_channel_wr
我正在使用 libssh 向计算机发送远程命令。该命令是实时的,因此我试图在生成数据时获取数据。基本上,我正在对鼠标事件进行十六进制转储,并且我想要该数据。如何从我的命令中返回实时结果? #inclu
已阅读相关内容docs和 tutorials并找到了similar问题,我仍然无法继续。如果这是一个常见问题,我提前表示歉意。我进行了搜索,但我不太确定我在寻找什么...... 我正在 Debian
我不知道为什么我的 libssh 程序无法连接到本地主机,但它对远程主机运行良好。 ssh_session my_ssh_session = ssh_new(); ssh_options_set(my
我正在尝试编写一个简单的程序(玩具示例),将文件从远程主机复制到本地计算机。 当我尝试复制一个 txt 文件时它有效,但不适用于 mp4 等文件。 这是我的代码,基本上是教程中的部分拼接而成:http
我正在尝试将一个库包含到我的代码 (libssh) 中,但没有任何效果...在这一点上,我不确定该怎么做,因为搜索引擎中没有针对我收到的错误弹出任何结果。 我在这里下载了 libssh: https:
我正在尝试一个非常简单的 libssh 实现,但 valgrind 显示内存泄漏。 代码是: #include #include #include #include #include int
我正在尝试在我的应用程序中使用 libssh,但我不断收到内存泄漏。我正在尝试的最简单的代码是: #include #define SSH_NO_CPP_EXCEPTIONS assert(0 ==
我的 SSH 服务器使用双重身份验证。我不知道它是如何实现的。但最初它要求输入密码,然后再次要求输入另一个密码才能登录到与通常控制不同的单独控制台。 我的代码与文档中显示的示例代码类似, #inclu
我按照教程尝试使用 libssh 打开和验证 SSH 连接,但在 ssh_connect 之后,无论我尝试密码验证、公钥验证还是不验证,应用程序都会崩溃并出现核心转储。 t@1 (l@1)
问题:让一个使用 libssh 库的简单程序成功编译而不会出现链接器错误。 程序: #include #define LIBSSH_STATIC 1 #include #include void
即使我可以在我的服务器上使用各种方法进行身份验证,使用 libssh 库,也没有检测到任何身份验证方法。这是一个代码示例: #include #include int main(int,ch
我正在使用 libssh 库并尝试创建一个新的 SFTP session 。我不断收到错误消息 ssh_packet_unimplemented: Received SSH_MSG_UNIMPLEME
在 ssh cli 中,我可以: ssh root@localhost -t aria2c https://github.com/JetBrains/intellij-community/archiv
我试图使用 libssh 0.7.0 将 SSH 通信包含到我的程序中。我按照教程创建了到远程计算机的简单连接,但遇到了这个错误: [2016/10/26 16:38:56.280261, 1] ss
我正在我的应用程序中为 xen VM 实现 VNC 连接。为了连接,我必须转发端口,因为 XenServer 只接受本地连接。我是这样做的: ssh -L 5903:localhost:5903 ro
我已经按照说明安装了 libssh,尽管一切似乎都正常,但我的编译器仍然在“#include”行中返回错误“找不到文件”。我猜它与目录或链接有关(我在下载它的同一个文件夹中有“make install
我是一名优秀的程序员,十分优秀!