I. Background
FTP protocol is based on the TCP protocol implementation of the file transfer protocol, this article through the use of libcurl library programming to familiarize with the FTP protocol.
II. Relevant knowledge
2.1 FTP(File transfer protocol)
FTP is one of the protocols in the TCP/IP protocol group, the protocol is the basis of the Internet file transfer, which consists of a series of specifications document, the goal is to improve the sharing of files, to provide non-directive use of remote computers, so that the storage medium is transparent to the user and reliable and efficient transfer of data. Simply put, FTP is to complete the copy between two computers, copy files from a remote computer to their own computers, called download (download) files. If the file will be copied from their own computer to the remote computer, it is called upload (upload) file [1,2].
The FTP protocol is implemented through the TCP protocol and is accomplished through a command channel and a data channel, which considers firewall rules in the network because there is a dynamically negotiated data channel, and is subdivided into active and passive modes in terms of the mode of operation [3].
The FTP address is formatted as follows: ftp://user:password@/
2.2 libcurl
The libcurl library is a network programming library that implements various client protocols. It currently supports more than 12 protocols , including FTP, HTTP,Telnetand other security variants [4].
The libcurl library adds similar functionality to languages like C and C++, but it is portable between languages.
III. Realization
source code referencecurl-7.54.0/docs/examples/ Make changes;
The first encapsulates a simple structure for maintaining the context of the FTP module, where ftp_host is the FTP host address;
typedef struct mod_ftp
{
CURL *curl;
char ftp_host[SIZE_NAME_LONG];
} mod_ftp_t; Initialization functions, one being the library initialization function curl_global_init().multi-threadedBeware of multiple calls;
Then there is the application of the curl instance, and finally the splicing of the ftp address, where the username and password are not required;
int mod_ftp_init(mod_ftp_t *pftp,
const char *ftp_addr, u16 ftp_port,
const char *username, const char *password)
{
CURLcode ret = CURLE_FAILED_INIT;
if ( !pftp || !ftp_addr ) {
LOGW("NULL\n");
return FAILURE;
}
if ( pftp->curl ) {
LOGW("mod_ftp has exist\n");
return SUCCESS;
}
curl_global_init(CURL_GLOBAL_DEFAULT);
pftp->curl = curl_easy_init();
if ( !pftp->curl ) {
LOGW("curl_easy_init failed\n");
return FAILURE;
}
snprintf(pftp->ftp_host, sizeof(pftp->ftp_host), "ftp://%s:%s@%s:%hu/",
username, password, ftp_addr, ftp_port);
return SUCCESS;
}
Next is the file upload function, which mainly accomplishes two parts, setting the property curl_easy_set_opt and executing the action curl_easy_perform.
Then there is a small detail is to upload the file when the data is first written to a temporary file, write the complete file before renaming the file over, to prevent intermediate errors lead to incomplete files;
int mod_ftp_upload_local_file(mod_ftp_t *pftp,
const char *local_file, u64 filesize,
const char *remote_tmp, const char *remote_file)
{
CURLcode ret = CURLE_FAILED_INIT;
struct curl_slist *headerlist = NULL;
char ftp_rnfr[SIZE_NAME_LONG] = {0};
char ftp_rnto[SIZE_NAME_LONG] = {0};
char ftp_url [SIZE_NAME_LONG] = {0};
FILE *fp = NULL;
if ( !pftp || !local_file || !remote_tmp || !remote_file ) {
return FAILURE;
}
if ( !pftp->curl ) {
return FAILURE;
}
fp = fopen(local_file, "rb");
if ( !fp ) {
return FAILURE;
}
snprintf(ftp_rnfr, sizeof(ftp_rnfr), "RNFR %s", remote_tmp);
snprintf(ftp_rnto, sizeof(ftp_rnto), "RNTO %s", remote_file);
snprintf(ftp_url, sizeof(ftp_url), "%s%s", pftp->ftp_host, remote_tmp);
/* Alloc and execute ftp commands after upload */
headerlist = curl_slist_append(headerlist, ftp_rnfr);
headerlist = curl_slist_append(headerlist, ftp_rnto);
curl_easy_setopt(pftp->curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(pftp->curl, CURLOPT_URL, ftp_url);
curl_easy_setopt(pftp->curl, CURLOPT_POSTQUOTE, headerlist);
curl_easy_setopt(pftp->curl, CURLOPT_READDATA, fp);
curl_easy_setopt(pftp->curl, CURLOPT_READFUNCTION, readfile_cb);
curl_easy_setopt(pftp->curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)filesize);
ret = curl_easy_perform(pftp->curl);
if ( CURLE_OK != ret ) {
LOGW("curl_easy_perform fail: %s\n", curl_easy_strerror(ret));
}
curl_slist_free_all(headerlist);
curl_easy_reset(pftp->curl);
CLOSE_FILE(fp);
return (ret != CURLE_OK) ? FAILURE: SUCCESS;
}
De-initialize the function and call curl_easy_cleanup to release the connection;
int mod_ftp_cleanup(mod_ftp_t *pftp)
{
if ( !pftp ) {
LOGW("NULL\n");
return FAILURE;
}
if ( pftp->curl ) {
curl_easy_cleanup(pftp->curl);
pftp->curl = NULL;
}
curl_global_cleanup();
return SUCCESS;
}
The test program is as follows:
int main(int argc, char *argv[])
{
mod_ftp_t ftp = {0};
mod_ftp_init(&ftp, "127.0.0.1", 21, "test01", "test01");
mod_ftp_upload_local_file(&ftp, "/tmp/", 1024, "__tmp__.", "");
mod_ftp_cleanup(&ftp);
return EXIT_SUCCESS;
}
IV. Analysis of results
Using libcurl is indeed easier than writing your own ftp parser based on a tcp connection;
Check the network status via netstat. libcurl Multiple executions of upload The control channel is automatically multiplexed, and the connection is closed only when cleanup is called;
But if you run into an FTP server disconnecting you between multiple uploads (idle culling), libcurl doesn't perform a timely close socket action, and only resumes at the next perform;
Disable multiplexing can be turned on option CURLOPT_FORBID_REUSE; note that a place is libcurl are inclined to blocking socket use scenarios, that is, not good with the I/O multiplexing scenarios, more suitable for multi-threaded, multi-process scenarios; reference articles: [1] /wiki/File_Transfer_Protocol [2] / sunada2005/articles/ [3] /xiaohh/p/ [4] /question/54100_8602