⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ftp.c

📁 简单的linux客户端命令行的
💻 C
📖 第 1 页 / 共 2 页
字号:
}

//进入PASV模式,成功返回0
int enpasv(int sock,char *dip,int *dport)
{
        int nret;
        int i=0;
        char buffer[1024];
        sprintf(buffer,"PASV\r\n");
        if(sendout(sock,buffer,strlen(buffer))<0) return -1; //write data error

        int movepos=0;                
        while(linefeedpos(buffer,movepos)==-1) //没有结束一行
        {
                if(movepos>1000) return -3; //buffer runs out
                if((nret=read(sock,buffer+movepos,1024-movepos))<=0) return -2; //read data error
                movepos+=nret;                
        }

        if(movepos<5) {
                logmsg("服务器返回无效数据。\n");
                return -5;
        }

        //读返回码
        char code[4];
        memcpy(code,buffer,3);
        code[4]=0;
        if(atoi(code)!=227) {
                logmsg("服务器不能进入PASV模式。\n");
                return -3;        
        }

        //读参数 (218,64,254,162,243,239)
        char s[32]={0};
        while(buffer[i++]!='(' && i<movepos);
        if(buffer[i-1]!='(') return -4;
        memcpy(s,&buffer[i],movepos-i+1);
        i=31;
        while(s[i]!=')' && i>1) i--;
        s[i]=0;

        int a,b,c,d,p1,p2;
        sscanf(s,"%d,%d,%d,%d,%d,%d",&a,&b,&c,&d,&p1,&p2);
        sprintf(dip,"%d.%d.%d.%d",a,b,c,d);
        *dport=(p1<<8) + p2;

        return 0;
        
}
//检查服务器的续传能力,有续传返回>0,无返回0,网络错误<0
int chkresume(int sock)
{
        int nret;

        char buffer[256];
        sprintf(buffer,"REST 100\r\n");
        if(sendout(sock,buffer,strlen(buffer))<0) return -1; //write data error

        nret=getretcode(sock);
        if(nret<0) return nret;
        else if(nret==350){
                sprintf(buffer,"REST 0\r\n");
                if(sendout(sock,buffer,strlen(buffer))<0) return -1; //write data error
                nret=getretcode(sock);
                if(nret==350)
                        return 1;
                else
                        return 0;
        }
        else         return 0;
}

//指定文件续传断点,成功返回>0
int setresume(int sock ,unsigned long pos)
{
        int nret;

        char buffer[256];
        sprintf(buffer,"REST %ld\r\n",pos);
        if(sendout(sock,buffer,strlen(buffer))<0) return -1; //write data error

        nret=getretcode(sock);
        if(nret<0) return nret;
        else if(nret==350)        return 1;
        else return 0;
}
//进入BINARY模式,成功返回0
int enbinary(int sock)
{
        int nret;

        char buffer[256];
        sprintf(buffer,"TYPE I\r\n");
        if(sendout(sock,buffer,strlen(buffer))<0) return -1; //write data error
        nret=getretcode(sock);
        if(nret<0) return nret;        
        if(nret==200) return 0;
        else return -1;
}

//filename like /test/test.fc
//返回<0 网络错误,=0不可获得文件大小 >0正确 
int getsize(int sock,char* filename,unsigned long *lsize)
{
        int nret;
        unsigned long size;

        char buffer[1024];
        sprintf(buffer,"SIZE %s\r\n",filename);
        if(sendout(sock,buffer,strlen(buffer))<0) return -1; //write data error

        int movepos=0;                
        while(linefeedpos(buffer,movepos)==-1) //没有结束一行
        {
                if(movepos>1000) return -3; //buffer runs out
                if((nret=read(sock,buffer+movepos,1024-movepos))<=0) return -2; //read data error
                movepos+=nret;                
        }

        assert(movepos>=5);
        buffer[movepos]=0;
        //读返回码
        //读文件大小
        sscanf(buffer,"%d %ld",&nret,&size);
        if(nret==213) { *lsize=size;return 1;}
        else return 0;
}

//控制连接上发送请求文件命令
int getfile(int sock,char*filename)
{
        int nret;

        char buffer[512];
        sprintf(buffer,"RETR %s\r\n",filename);

        if(sendout(sock,buffer,strlen(buffer))<0) return -1; //write data error
        nret=getretcode(sock);
        logmsg("getfile nret=%d\n",nret);
        if(nret<0) return nret;        
        if(nret==150) return 0;
        else return -1;
}

//将获得的数据写进文件中,可以考虑用map
static pthread_mutex_t wmutex;
int writedata(unsigned long offset,unsigned char *buf,unsigned long len)
{
        static unsigned long total=0;
        pthread_mutex_lock(&wmutex);

        total+=len;
        logmsg("totalget=%ld\r",total);
        //写数据到文件。。。
        pthread_mutex_unlock(&wmutex);
        return 1;
}

//sock数据连接,spos起点偏移,epos终点偏移,bytes获得的字节数
//返回0正确完成任务,如果epos!=0 那么返回=epos-spos+1就完成了任务
//否则直到对方关闭连接或网络错误才算完成任务
//线程安全?
//调用这个函数,任务不能被中途剥夺,所以当线程分配到一个比较大的任务片时
//为了能够将来让出部分任务片给其他进程,最好小片地调用这个函数,完成小片
//后再检查任务的结尾点是否修改了
int getdata(int sock,int spos,int epos,unsigned long *bytes)
{
#define RECVBUF 32768        //16K缓冲区

        unsigned char buffer[RECVBUF];        
        int movepos=0;
        int nret;
        unsigned long total=0;        //本次已经接收的数据
        unsigned long need=epos-spos+1; //需要接收的数据量

        while(1){
                nret=read(sock,buffer+movepos,RECVBUF-movepos);
                if(nret==-1) {
                        if(errno!=EWOULDBLOCK){
                                //如果缓冲中有老数据,写出
                                if(movepos>0) {
                                        writedata(spos+total,buffer,movepos);
                                }
                                total+=movepos;
                                *bytes=total;
                                return -1;
                        }
                        else continue;
                }
                else if(nret==0)
                {        //数据传送正常关闭,传输完毕
                        //写剩余数据
                        writedata(spos+total,buffer,movepos);
                        total+=movepos;
                        *bytes=total;
                        return 0;
                }
                else if(nret<0)
                {
                        logmsg("网络错误。\n");
                        //如果缓冲中有老数据,写出
                        if(movepos>0) {
                                writedata(spos+total,buffer,movepos);
                        }
                        total+=movepos;
                        *bytes=total;
                        return -1;                
                }
                else
                {
                        movepos+=nret;
                        //这次是否就够数据量了?
                        if(total+movepos>=need)
                        {
                                writedata(spos+total,buffer,need-total);
                                *bytes=need;
                                return 0;        
                        }
                        if(movepos>=RECVBUF-1024)
                        {        
                                //buffer full , write to disk
                                writedata(spos+total,buffer,movepos);
                                total+=movepos;
                                movepos=0;
                        }
                }
        }

        return 0;
#undef RECVBUF 
}



//工作线程

void* workants(void *indata)
{
        char serverip[24];
        int port;
        unsigned long *spos,*epos; //任务的起点和终点,当epos=0时,没设定结束
        char user[24];
        char pass[24];        
        char enfile[1024];        //编码的文件名

        int  dport;                //数据服务器端口
        char dip[24];        //数据服务器地址


//读取数据
        struct antsdata *pin=(struct antsdata*)indata;

        strcpy(serverip,pin->serverip);
        strcpy(user,pin->user);
        strcpy(pass,pin->pass);
        strcpy(enfile,pin->enfile);
        port=pin->port;
        spos=pin->spos;
        epos=pin->epos;

        logmsg("进入工作线程,offset=%ld\n",*spos);                
//有了初始数据,开始工作
        int sockctrl,sockdata;
        int nret;
        //make connection
        if((sockctrl=makectrlconn(serverip,port))<0) {
                logmsg("控制连接失败。\n");                 
                pthread_exit(NULL);
        }
        logmsg("==>建立控制连接成功\n");
        //login
        if(login(sockctrl,user,pass)<0) {
                logmsg("登录失败\n");
                pthread_exit(NULL);
        }
        logmsg("==>登录成功\n");
        //进入BINARY
        if(enbinary(sockctrl)<0){
                logmsg("不能进入二进制传送模式\n");
                pthread_exit(NULL);
        }
        logmsg("==>进入二进制模式\n");

        if((nret=setresume(sockctrl ,spos))==0)
        {
                if(spos!=0) {
                        logmsg("不能设定指定的断点!pos=%ld\n",spos);
                        pthread_exit(NULL);
                }
        }
        else if(nret<0)
        {
                logmsg("网络失败。\n");
                pthread_exit(NULL);
        }

        //send PASV command to get data
        if(enpasv(sockctrl,dip,&dport)<0) {
                logmsg("不能进入PASV模式\n");
                pthread_exit(NULL);
        }
        //pasv ok,we then make data connection
        logmsg("数据服务器=%s,数据端口=%d\n",dip,dport);

        if((sockdata=makeconn(dip,dport))<0) {
                logmsg("数据连接失败。\n");
                pthread_exit(NULL);
        }
        //ok ,connected to data port
        //控制连接上先发REST设置位置(如果支持多点传送)
        //然后发RETR /filename请求传送,这时返回150表示命令成功
        logmsg("数据连接建立。\n");

        if(getfile(sockctrl,enfile)<0) {
                logmsg("文件传送命令失败。\n");
                pthread_exit(NULL);
        }        

        logmsg("从%ld开始接受数据...\n",spos);

        int retrytime=10;
        unsigned long ndata;

retry:

        nret=getdata(sockdata,spos,epos,&ndata);
        if(nret==0)
        {//正常完成任务
                logmsg("正常完成任务片,接受了%ld字节\n",ndata);
                close(sockctrl);
                close(sockdata);
                pthread_exit(NULL);
        }
        else
        {//出现网络问题,无限重试
                logmsg("网络故障,重试。\n");
                spos+=ndata;
                if(--retrytime<0) {logmsg("重试太多,退出\n");pthread_exit(NULL);}
                goto retry;
        }
}

int main(int argc,char* argv[])
{
        if(argc!=2)        {
                logmsg("usage:\n    %s urlstring\n",argv[0]);                return -1;
        }

        char *url=argv[1];

        int sockctrl,sockdata,nret,resume=0,port,dport;//port 控制端口 dport 数据服务器端口
        unsigned long filesize=0;

        char server[256],serverip[24],dip[24],user[24],pass[24],filename[512],encodefilename[1024];

//获得服务器基本信息
        if(!islegalurl(url)) return -1;
        logmsg("==>合法地址\n");
        if(!getserverandportbyurl(url,server,&port,256)) return -1;
        logmsg("==>服务器:%s,端口:%d\n",server,port);
        if(!getuserandpassbyurl(url,user,24,pass,24)) return -1;
        logmsg("==>用户:%s,密码:%s\n",user,pass);
        if(!getfilebyurl(url,filename,512)) return -1;
        logmsg("==>文件名:%s\n",filename);
        //解码文件名


        if(!getserverip(server,serverip,24)) return -1;
        logmsg("==>服务器IP:%s\n",serverip);

        //make connection
        if((sockctrl=makectrlconn(serverip,port))<0) return -2;
        logmsg("==>建立控制连接成功\n");
        //login
        if(login(sockctrl,user,pass)<0) return -3;
        logmsg("==>登录成功\n");
        //login ok,check if can resume
        if((resume=chkresume(sockctrl))<0) return -4;
        logmsg("==>服务器%s支持断点续传\n",resume?"":"不");
        //switch to Binary to get filesize
        if(enbinary(sockctrl)<0) return -5;

        //get the filesize
        if((nret=getsize(sockctrl,filename,&filesize))<0) return -6;
        if(nret==0) {
                logmsg("不能获得文件大小,无法多点传送。\n");
                //调用单个线程下载函数并返回
                return 0;//任务完成
        }else{

                close(sockctrl);
                logmsg("文件大小:%ld\n",filesize);
                
        }

        //到这里确定是要采用多线程下载了,暂时搞5个
        //工作线程传入结构
        pthread_mutex_init (&wmutex,NULL);

#define THREADNUM 5
        logmsg("==>准备多线程下载\n");
        struct antsdata taskin;
        taskin.serverip=serverip;
        taskin.user=user;
        taskin.pass=pass;
//        taskin.enfile=encodefilename;
        taskin.enfile=filename;
        taskin.port=port;
        int i;
        for(i=0;i<THREADNUM;i++) {
                g_tsk[i].start=0+i*(filesize/THREADNUM);
                g_tsk[i].end=(i+1)*(filesize/THREADNUM)-1;
                taskin.spos=&g_tsk[i].start;
                taskin.epos=&g_tsk[i].end;
                if(0!=pthread_create(&g_tid[i],NULL,workants,&taskin))
                {
                        logmsg("thread create error\n");
                }
                pthread_join(g_tid[i],NULL);
        }

#undef THREADNUM


}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -