I try to upload multiple files via the libssh2 sftp subsystem to a remote server. My problem is that I have to close the sftp handle and the sftp session always after transmitting one file, otherwise the function libssh2_sftp_open() will fail with "FXP_STATUS 4, unable to open / create file". Because of this reinit of the session and file I lose valuable seconds.
Is there a way to upload multiple files in a loop without reinit of the session and/or handle? I hope this code snippet will show you my problem, here with reinit after one loop gothrough which works, but needs like 4 seconds for uploading 4 kbytes:
// Transfer at most MAXTRANSFER files
for (i=0;(i < ((MAXTRANSFER<nfiles)?MAXTRANSFER:nfiles)) && (iRet == SSHH_OK); i++) {
localfile = malloc(sizeof(char) * (strlen(directory) + strlen(fnames[i]) + 2));
strcpy(localfile, directory);
strcat(localfile, "/");
strcat(localfile, fnames[i]);
local = fopen(localfile, "rb");
if (!local) {
dbgPrintFormat(DEBUG_WARNING, "dir_sftp: Can't open local file %s", localfile);
iRet = SSHH_ERR_SETTINGS;
}
// Init SFTP
if (iRet == SSHH_OK) {
sftp_session = libssh2_sftp_init(session);
if (!sftp_session) {
dbgPrintFormat(DEBUG_ERROR, "dir_sftp: Unable to init SFTP session (%d)", sftp_session);
iRet = SSHH_ERR_SETTINGS;
}
}
if (iRet == SSHH_OK) {
stat(localfile, &fileinfo);
char *dstfile = malloc(sizeof(char) * (strlen(destdir) + strlen(fnames[i]) + 2));
strcpy(dstfile, destdir);
strcat(dstfile, "/");
strcat(dstfile, fnames[i]);
dbgPrintFormat(DEBUG_FINE, "dir_sftp: Start sending file %s", localfile);
// Request a file via SFTP
sftp_handle = libssh2_sftp_open(sftp_session, dstfile,
LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
LIBSSH2_SFTP_S_IRWXU|LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IXGRP);
if (!sftp_handle) {
dbgPrintFormat(DEBUG_ERROR, "dir_sftp: Unable to open file with SFTP");
iRet = SSHH_ERR_SETTINGS;
}
if (iRet == SSHH_OK) {
dbgPrintFormat(DEBUG_FINE, "dir_sftp: SFTP session waiting to send file %s",localfile);
do {
nread = fread(mem, 1, sizeof(mem), local);
if (nread <= 0) {
/* end of file */
break;
}
ptr = mem;
do {
/* write the same data over and over, until EOF */
rc = libssh2_sftp_write(sftp_handle, ptr, nread);
// EOF
if(rc < 0)
break;
ptr += rc;
nread -= rc;
} while (nread);
} while (rc > 0);
}
ulSftpTxTryCount[iSftpTxTryCountIdx]++;
if (iRet == SSHH_OK) {
// Remove localfile *** if no error
if (remove(localfile) == -1) {
dbgPrintFormat(DEBUG_WARNING,"dir_sftp: Error removing file: %s", localfile);
}
if( successCnt ) {
(*successCnt)++;
}
dbgPrintFormat(DEBUG_FINE, "dir_sftp: File sent after %lu tries", ulSftpTxTryCount[iSftpTxTryCountIdx]);
iSftpTxTryCountIdx = (iSftpTxTryCountIdx + 1) % SCP_TRYCNT_NOF;
ulSftpTxTryCount[iSftpTxTryCountIdx] = 0;
}
free (dstfile);
}
if (local)
fclose(local);
free (localfile);
// Close file sftp handle
if (libssh2_sftp_close(sftp_handle) < 0) {
dbgPrintFormat(DEBUG_WARNING, "dir_sftp: Error closing SFTP handle ");
}
// Close sftp session
libssh2_sftp_shutdown(sftp_session);
}
You have several problems. First problem (the one you are asking about) is that the server doesn't seem to like more than one file opened by the client at the same time. I.e. this can be a server-specific problem (as well as the issue of libssh2).
The bigger problem is that your approach of sending a block, waiting for result then sending another block is very slow. Third-party SFTP libraries implement what we call pipelining, when several blocks are sent asynchronously in parallel. This significantly speeds up file transfer.
My suggestion is to find some third-party library which was written specifically for file transfer. This will give you much better speed.