30.1 前言
ftpclient相对于ftpsever在应用实现上比较简单,可以调用现成的很多库快速实现ftp、http等客户端,本篇将实现使用curl 实现ftp客户端的实例编程。
本篇博客整合了ftp上送文件以及上送内存数据的方式,便于理解和使用,用户只需填写对应的结构数据,并将该参数传入对应的函数,便可实现ftp上送文件或ftp上送内存数据功能。模块的编写是基于模块独立的设计思想,与用户代码实现尽可能的隔离开来,降低耦合。
30.2 代码实例
main.c
#ifndef __FTPCLIENT_H
#define __FTPCLIENT_H
#ifdef __cplusplus
extern "C" {
#endif
/*****************************************************************
* 宏定义
******************************************************************/
#define FTP_OPERATION_UPLOAD_MEM 0 //上传内存数据
#define FTP_OPERATION_UPLOAD_FILE 1 //上传文件数据
/*****************************************************************
* 结构、枚举定义
******************************************************************/
typedef struct _stFtpUserInfo
{
int nOperation; //操作选择
int nHaveSendLen; //已发送长度
int nSedSize; //发送数据大小
int nRcvBufLen; //缓冲区长度
int nRcvSize; //接收body数据大小
char cSndFilePathName[128]; //上送文件路径名
char *pRcvBuffer; //指向用户用来存储数据的buf
char *pSendBuffer; //指向用户发送数据的buf
}stFtpUserInfo;
typedef struct _stFtpClientInfo
{
int nServerPort; //ftp服务器监听端口号
char chServerIP[32]; //ftp服务器IP
char chServerPath[128]; //ftp服务器文件路径
char chUserName[32]; //ftp服务器登录用户
char chUserPassWord[32]; //ftp服务器登录密码
int use_proxy; //是否使用http服务器代理
int nHttpServerPort; //ftp服务器监听端口号
char chHttpServerIP[32]; //ftp服务器IP
char chHttpServerPath[64];//ftp服务器文件路径
stFtpUserInfo stFtpUser;
}stFtpClientInfo;
int ftpClient_ftpUpload(stFtpClientInfo *pstUserArg,long *pRespCode);
#ifdef __cplusplus
}
#endif
#endif
ftpClient.c
#ifdef __cplusplus
extern "C" {
#endif
/*****************************************************************
* 包含头文件
******************************************************************/
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "curl.h"
#include "ftpClient.h"
/*************************************************
** Function:ftpClient_writeCallBack
** Description:ftp回调数据接收函数
** Input:无
** Output:
** Return:-1-失败 0-成功
** Author:
** Date:2018-11-27
** Modification History:
** Author:
** Date:
** Description:
*************************************************/
size_t ftpClient_writeCallBack(void *ptr, size_t size, size_t nmemb, void *stream)
{
int writeLength = 0;
int written = size*nmemb;
stFtpUserInfo *pSt = (stFtpUserInfo*)stream;
if(0)//此处记录body内容
{
if(pSt->pRcvBuffer != NULL)
{
writeLength = (written <= (pSt->nRcvBufLen - pSt->nRcvSize))?(written):(pSt->nRcvBufLen - pSt->nRcvSize);
memcpy((char*)(pSt->pRcvBuffer) +pSt->nRcvSize, ptr, writeLength);
pSt->nRcvSize += writeLength;
//printf("%d line,nWRLength=%d written=%d nUsedLength=%d writeLength=%d\n",__LINE__,pSt->nRcvBufLen, written,pSt->nRcvSize,writeLength);
}
return writeLength;
}
if(0){
printf("\n %s\n", ptr);
}
return 0;
}
/*************************************************
** Function:ftpClient_file2Ftp
** Description:ftp读取文件并上送
** Input:无
** Output:
** Return:-1-失败 0-成功
** Author:
** Date:2018-11-27
** Modification History:
** Author:
** Date:
** Description:
*************************************************/
size_t ftpClient_file2Ftp(void *ptr, size_t size, size_t nmemb, void *stream)
{
size_t retcode = fread(ptr, size, nmemb, stream);
return retcode;
}
/*************************************************
** Function:ftpClient_mem2Ftp
** Description:ftp读取内存数据
** Input:无
** Output:
** Return:-1-失败 0-成功
** Author:
** Date:2018-11-27
** Modification History:
** Author:
** Date:
** Description:
*************************************************/
size_t ftpClient_mem2Ftp(void *ptr, size_t size, size_t nmemb, void *stream)
{
size_t cpoyLength = 0;
size_t copySize = size*nmemb;
stFtpUserInfo *pSt = (stFtpUserInfo*)stream;
if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
return 0;
}
if(pSt->pSendBuffer != NULL)
{
cpoyLength = (copySize <= (pSt->nSedSize - pSt->nHaveSendLen))?(copySize):(pSt->nSedSize - pSt->nHaveSendLen);
memcpy(ptr,(char*)(pSt->pSendBuffer) +pSt->nHaveSendLen, cpoyLength);
pSt->nHaveSendLen += cpoyLength;
//printf("%d line,copySize=%d nSedSize=%d nHaveSendLen=%d cpoyLength=%d\n",__LINE__,copySize, pSt->nSedSize,pSt->nHaveSendLen,cpoyLength);
}
return cpoyLength;
}
/*************************************************
** Function:ftpClient_ftpUpload
** Description:ftpUpload
** Input:无
** Output:
** Return:-1-失败 0-成功
** Author:
** Date:2018-11-27
** Modification History:
** Author:
** Date:
** Description:
*************************************************/
int ftpClient_ftpUpload(stFtpClientInfo *pstUserArg,long *pRespCode)
{
int nRet = 0;
long response_code = -1;
char chftpUrl[160] ={0};
char chHttpUrl[128] ={0};
FILE *ffp = NULL;
curl_off_t fsize = 0;
CURL *curl = NULL;
CURLcode code = 0;
struct stat file_info;
struct curl_slist *chunk = NULL;
*pRespCode = 0;
if(NULL == pstUserArg){
printf("%s func Invalid parameter at (%d) line\n",__FUNCTION__,__LINE__);
return -1;
}
curl = curl_easy_init();
if(curl)
{
snprintf(chftpUrl,160,"ftp://%s:%u/%s", pstUserArg->chServerIP, pstUserArg->nServerPort, pstUserArg->chServerPath);
// printf("%s func User=%s ,passwd=%s,chUrl=%s at (%d) line\n",__FUNCTION__,pstUserArg->chUserName,pstUserArg->chUserPassWord,chftpUrl,__LINE__);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_URL, chftpUrl);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); //上传
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); //注意,毫秒超时一定要设置这个
curl_easy_setopt(curl, CURLOPT_USERNAME, pstUserArg->chUserName);
curl_easy_setopt(curl, CURLOPT_PASSWORD, pstUserArg->chUserPassWord);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L); //设置延时1s
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 60L);
curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1);//创建不存在的目录
/* enable TCP keep-alive for this transfer */
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
/* keep-alive idle time to 120 seconds */
curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 600L);
/* interval time between keep-alive probes: 60 seconds */
curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 60L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ftpClient_writeCallBack);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &pstUserArg->stFtpUser); /*接收响应*/
switch(pstUserArg->stFtpUser.nOperation)
{
case FTP_OPERATION_UPLOAD_MEM: //发送内存数据
curl_easy_setopt (curl, CURLOPT_READDATA, &pstUserArg->stFtpUser);
curl_easy_setopt (curl, CURLOPT_READFUNCTION, ftpClient_mem2Ftp);
curl_easy_setopt (curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) pstUserArg->stFtpUser.nSedSize);
break;
case FTP_OPERATION_UPLOAD_FILE: //ftp发送文件
/********************打开文件***************/
if(stat(pstUserArg->stFtpUser.cSndFilePathName, &file_info)){
printf("ftp upload stat %s failed\n", pstUserArg->stFtpUser.cSndFilePathName);
goto iExit;
}
fsize = (curl_off_t) file_info.st_size;
ffp = fopen (pstUserArg->stFtpUser.cSndFilePathName, "rb");
if(NULL == ffp){
printf("ftp fopen %s failed!\n", pstUserArg->stFtpUser.cSndFilePathName);
goto iExit;
}
curl_easy_setopt (curl, CURLOPT_READDATA, ffp);
curl_easy_setopt (curl, CURLOPT_READFUNCTION, ftpClient_file2Ftp);
curl_easy_setopt (curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) fsize);
break;
default:
break;
}
/*是否使用了http代理服务器*/
if(pstUserArg->use_proxy == 1)
{
snprintf(chHttpUrl,128,"http://%s/%s/%s", pstUserArg->chHttpServerIP, pstUserArg->nHttpServerPort, pstUserArg->chHttpServerPath);
curl_easy_setopt (curl, CURLOPT_PROXY, chHttpUrl);
}
code = curl_easy_perform(curl);
if(code != CURLE_OK)
{
nRet = -1;
printf("%s func curl_easy_perform failed:%s at (%d) line\n",__FUNCTION__,curl_easy_strerror(code),__LINE__);
}
else
{
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
if(226 != response_code)//对于ftp成功是226码
{
nRet = -1;
printf("%s func curl_easy_getinfo RESPONSE_CODE: %d at (%d) line\n",__FUNCTION__,response_code,__LINE__);
}
}
switch(pstUserArg->stFtpUser.nOperation)
{
case FTP_OPERATION_UPLOAD_MEM: //发送内存数据
break;
case FTP_OPERATION_UPLOAD_FILE: //ftp发送文件
if(NULL != ffp) fclose(ffp);
break;
default:
break;
}
curl_slist_free_all(chunk);
curl_easy_cleanup(curl);
*pRespCode = response_code;
return nRet;
}
return -1;
iExit:
curl_slist_free_all(chunk);
curl_easy_cleanup(curl);
return -1;
}
#ifdef __cplusplus
}
#endif
main.c
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include "ftpClient.h"
int main()
{
int nRet = -1;
char *pSendData = "123456789";
long lRespCode = 0;
stFtpClientInfo stFtpClienter;
memset(&stFtpClienter,0,sizeof(stFtpClienter));
memcpy(stFtpClienter.chServerPath,"/test.jpg",strlen("/test.jpg"));
memcpy(stFtpClienter.chServerIP,"192.168.36.56",strlen("192.168.36.56"));
memcpy(stFtpClienter.chUserName,"root",strlen("root"));
memcpy(stFtpClienter.chUserPassWord,"3214",strlen("3214"));
stFtpClienter.nServerPort = 2125;
stFtpClienter.use_proxy = 0;//是否选择http代理服务器,0-不选择
stFtpClienter.stFtpUser.nOperation = FTP_OPERATION_UPLOAD_MEM; //选择是否上送内存数据或文件
stFtpClienter.stFtpUser.pRcvBuffer = NULL;
stFtpClienter.stFtpUser.nRcvBufLen = 0;
stFtpClienter.stFtpUser.nRcvSize = 0;
stFtpClienter.stFtpUser.nHaveSendLen = 0;
stFtpClienter.stFtpUser.pSendBuffer = pSendData; //发送数据
stFtpClienter.stFtpUser.nSedSize = strlen(pSendData); //发送数据长度
/*上送至ftp服务器*/
nRet = ftpClient_ftpUpload(&stFtpClienter,&lRespCode);
if(nRet<0){
printf("ftpClient_ftpUpload failed\n");
return -1;
}
return 0;
}