主頁 > 後端開發 > FTP客戶端c代碼功能實作

FTP客戶端c代碼功能實作

2023-02-24 07:16:14 後端開發

  現在市面上有很多免費的FTP軟體:如FileZilla ,那如果想自己在代碼中實作與ftp服務器的上傳下載檔案該如何實作那? 

本質上ftp協議就是TCP基礎上建立的一種協議,具體如下,

FTP 概述

檔案傳輸協議(FTP)作為網路共享檔案的傳輸協議,在網路應用軟體中具有廣泛的應用,FTP的目標是提高檔案的共享性和可靠高效地傳送資料,

在傳輸檔案時,FTP 客戶端程式先與服務器建立連接,然后向服務器發送命令,服務器收到命令后給予回應,并執行命令,FTP 協議與作業系統無關,任何作業系統上的程式只要符合 FTP 協議,就可以相互傳輸資料,本文主要基于 LINUX 平臺,對 FTP 客戶端的實作原理進行詳盡的解釋并闡述如何使用 C 語言撰寫一個簡單的 FTP 客戶端,

 

FTP 協議

相比其他協議,如 HTTP 協議,FTP 協議要復雜一些,與一般的 C/S 應用不同點在于一般的C/S 應用程式一般只會建立一個 Socket 連接,這個連接同時處理服務器端和客戶端的連接命令和資料傳輸,而FTP協議中將命令與資料分開傳送的方法提高了效率,

FTP 使用 2 個埠,一個資料埠和一個命令埠(也叫做控制埠),這兩個埠一般是21 (命令埠)和 20 (資料埠),控制 Socket 用來傳送命令,資料 Socket 是用于傳送資料,每一個 FTP 命令發送之后,FTP 服務器都會回傳一個字串,其中包括一個回應代碼和一些說明資訊,其中的回傳碼主要是用于判斷命令是否被成功執行了,

命令埠

一般來說,客戶端有一個 Socket 用來連接 FTP 服務器的相關埠,它負責 FTP 命令的發送和接識訓傳的回應資訊,一些操作如“登錄”、“改變目錄”、“洗掉檔案”,依靠這個連接發送命令就可完成,

資料埠

對于有資料傳輸的操作,主要是顯示目錄串列,上傳、下載檔案,我們需要依靠另一個 Socket來完成,

如果使用被動模式,通常服務器端會回傳一個埠號,客戶端需要用另開一個 Socket 來連接這個埠,然后我們可根據操作來發送命令,資料會通過新開的一個埠傳輸,

如果使用主動模式,通常客戶端會發送一個埠號給服務器端,并在這個埠監聽,服務器需要連接到客戶端開啟的這個資料埠,并進行資料的傳輸,

下面對 FTP 的主動模式和被動模式做一個簡單的介紹,

主動模式 (PORT)

主動模式下,客戶端隨機打開一個大于 1024 的埠向服務器的命令埠 P,即 21 埠,發起連接,同時開放N +1 埠監聽,并向服務器發出 “port N+1” 命令,由服務器從它自己的資料埠 (20) 主動連接到客戶端指定的資料埠 (N+1),

FTP 的客戶端只是告訴服務器自己的埠號,讓服務器來連接客戶端指定的埠,對于客戶端的防火墻來說,這是從外部到內部的連接,可能會被阻塞,

被動模式 (PASV)

為了解決服務器發起到客戶的連接問題,有了另一種 FTP 連接方式,即被動方式,命令連接和資料連接都由客戶端發起,這樣就解決了從服務器到客戶端的資料埠的連接被防火墻過濾的問題,

被動模式下,當開啟一個 FTP 連接時,客戶端打開兩個任意的本地埠 (N > 1024 和 N+1) ,

第一個埠連接服務器的 21 埠,提交 PASV 命令,然后,服務器會開啟一個任意的埠 (P > 1024 ),回傳如“227 entering passive mode (127,0,0,1,4,18)”, 它回傳了 227 開頭的資訊,在括號中有以逗號隔開的六個數字,前四個指服務器的地址,最后兩個,將倒數第二個乘 256 再加上最后一個數字,這就是 FTP 服務器開放的用來進行資料傳輸的埠,如得到 227 entering passive mode (h1,h2,h3,h4,p1,p2),那么埠號是 p1*256+p2,ip 地址為h1.h2.h3.h4,這意味著在服務器上有一個埠被開放,客戶端收到命令取得埠號之后, 會通過 N+1 號埠連接服務器的埠 P,然后在兩個埠之間進行資料傳輸,

主要用到的 FTP 命令

FTP 每個命令都有 3 到 4 個字母組成,命令后面跟引數,用空格分開,每個命令都以 "\r\n"結束,

要下載或上傳一個檔案,首先要登入 FTP 服務器,然后發送命令,最后退出,這個程序中,主要用到的命令有 USER、PASS、SIZE、REST、CWD、RETR、PASV、PORT、QUIT,

USER: 指定用戶名,通常是控制連接后第一個發出的命令,“USER gaoleyi\r\n”: 用戶名為gaoleyi 登錄,

PASS: 指定用戶密碼,該命令緊跟 USER 命令后,“PASS gaoleyi\r\n”:密碼為 gaoleyi,

SIZE: 從服務器上回傳指定檔案的大小,“SIZE file.txt\r\n”:如果 file.txt 檔案存在,則回傳該檔案的大小,

CWD: 改變作業目錄,如:“CWD dirname\r\n”,

PASV: 讓服務器在資料埠監聽,進入被動模式,如:“PASV\r\n”,

PORT: 告訴 FTP 服務器客戶端監聽的埠號,讓 FTP 服務器采用主動模式連接客戶端,如:“PORT h1,h2,h3,h4,p1,p2”,

RETR: 下載檔案,“RETR file.txt \r\n”:下載檔案 file.txt,

STOR: 上傳檔案,“STOR file.txt\r\n”:上傳檔案 file.txt,

REST: 該命令并不傳送檔案,而是略過指定點后的資料,此命令后應該跟其它要求檔案傳輸的 FTP 命令,“REST 100\r\n”:重新指定檔案傳送的偏移量為 100 位元組,

QUIT: 關閉與服務器的連接,

FTP 回應碼

客戶端發送 FTP 命令后,服務器回傳回應碼,

回應碼用三位數字編碼表示:

第一個數字給出了命令狀態的一般性指示,比如回應成功、失敗或不完整,

第二個數字是回應型別的分類,如 2 代表跟連接有關的回應,3 代表用戶認證,

第三個數字提供了更加詳細的資訊,

第一個數字的含義如下:

1 表示服務器正確接收資訊,還未處理,

2 表示服務器已經正確處理資訊,

3 表示服務器正確接收資訊,正在處理,

4 表示資訊暫時錯誤,

5 表示資訊永久錯誤,

第二個數字的含義如下:

0 表示語法,

1 表示系統狀態和資訊,

2 表示連接狀態,

3 表示與用戶認證有關的資訊,

4 表示未定義,

5 表示與檔案系統有關的資訊,

Socket 編程的幾個重要步驟

Socket 客戶端編程主要步驟如下:

  1. socket() 創建一個 Socket
  2. connect() 與服務器連接
  3. write() 和 read() 進行會話
  4. close() 關閉 Socket

Socket 服務器端編程主要步驟如下:

  1. socket() 創建一個 Socket
  2. bind()
  3. listen() 監聽
  4. accept() 接收連接的請求
  5. write() 和 read() 進行會話
  6. close() 關閉 Socket

實作 FTP 客戶端上傳下載功能

下面讓我們通過一個例子來對 FTP 客戶端有一個深入的了解,本文實作的 FTP 客戶端有下列功能:

  1. 客戶端和 FTP 服務器建立 Socket 連接,
  2. 向服務器發送 USER、PASS 命令登錄 FTP 服務器,
  3. 使用 PASV 命令得到服務器監聽的埠號,建立資料連接,
  4. 使用 RETR/STOR 命令下載/上傳檔案,
  5. 在下載完畢后斷開資料連接并發送 QUIT 命令退出,

 

 

經過測驗可以正常上傳下載資料,,測驗代碼如下:

main.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "ftp.h" #define FTP_SERVER_IP "XXXXXXXX" #define FTP_SERVER_USER "XXXXX" #define FTP_SERVER_PASS "XXXXXX" #define MAX_BUF_LEN 512 typedef struct{ char usr[32]; char passwd[32]; char ser_filepath[512]; char ser_filename[64]; char new_filename[64]; int control_sock; }ftp_client_st; ftp_client_st ftp_st; int main (int argc , char * argv[]) { char str[MAX_BUF_LEN] ={0}; int ret =-1; // while(1){ printf("*************\n"); //printf("Please input the ftp server ip: "); memset(str,0,sizeof(str)); //scanf("%s",str); //從終端獲取到服務器ip地址, strcpy(str,FTP_SERVER_IP); printf("input fpt server ip:%s\n",str); /*連接到服務器*/ memset(&ftp_st,0,sizeof(ftp_client_st)); ftp_st.control_sock = connect_ftp_server(str,FTP_SERVER_PORT); if(ftp_st.control_sock > 0){/*連接成功*/ ret = -1; while(ret < 0){ strcpy(ftp_st.usr,FTP_SERVER_USER); strcpy(ftp_st.passwd,FTP_SERVER_PASS); printf("input usr:%s passwd:%s\n",ftp_st.usr,ftp_st.passwd); ret = login_ftp_server(ftp_st.control_sock,ftp_st.usr,ftp_st.passwd); if(ret < 0){ printf("\nUser or Passwd is wrong,input agin"); } else{ //列印服務器當前目錄和串列 while(1){ printf("Get list start:\n"); //ret = down_file_ftpserver(ftp_st.control_sock,"/","/list_mode",0,0,CMD_LIST); /*被動模式*/ ret = down_file_ftpserver(ftp_st.control_sock,"/","../list_passive",1,0,CMD_LIST); /*被動模式獲取檔案串列*/ // down_file_ftpserver(ftp_st.control_sock,"/down_test","list1",0,0,CMD_LIST); //printf("\nInput down file dir (Input quit to quit):"); //memset(ftp_st.ser_filepath,0,sizeof(ftp_st.ser_filepath)); //scanf("%s",ftp_st.ser_filepath); //if(strncmp(ftp_st.ser_filepath,"quit",4) ==0) // goto err0; #if 0 printf("\nInput down filename (Input quit to quit):"); memset(ftp_st.ser_filename,0,sizeof(ftp_st.ser_filename)); scanf("%s",ftp_st.ser_filename); if(strncmp(ftp_st.ser_filename,"quit",4) ==0) goto err0; printf("\nInput new filename (Input quit to quit):"); memset(ftp_st.new_filename,0,sizeof(ftp_st.new_filename)); scanf("%s",ftp_st.new_filename); if(strncmp(ftp_st.new_filename,"quit",4) ==0) goto err0; printf("input filename :%s; newfilename:%s; \n",ftp_st.ser_filename,ftp_st.new_filename); printf("down file start:\n"); //ret = down_file_ftpserver(ftp_st.control_sock,ftp_st.ser_filename,ftp_st.new_filename,0,0,CMD_RETR); ret = down_file_ftpserver(ftp_st.control_sock,ftp_st.ser_filename,ftp_st.new_filename,0,0,CMD_RETR); #endif down_file_ftpserver(ftp_st.control_sock,"/down_test/test_ftp.zip","../12.zip",1,0,CMD_RETR); up_file_ftpserver(ftp_st.control_sock, "/down_test/12.zip", "../12.zip", 1, 0); get_fsize_ftpserver(ftp_st.control_sock, "/down_test/12.zip"); goto err0; } } } } } err0: quit_fpt_server(ftp_st.control_sock); return 0; }

fpt.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <arpa/inet.h>
#include <sys/unistd.h>

#include <sys/ioctl.h>
#include <net/if.h>


#include "ftp.h"


#define MAX_BUF 512
#define IP_LENGTH   16


//正常時服務器回復的回應碼
#define ACK_USER_NUM "331"
#define ACK_PASS_NUM "230"
#define ACK_PASV_NUM "227"
#define ACK_CWD_NUM  "250"
#define ACK_SIZE_NUM "213"
#define ACK_RETR_NUM "150" 
#define ACK_REST_NUM "350"
#define ACK_QUIT_NUM "200"
#define ACK_LIST_NUM "125"
#define ACK_STOR_NUM "150"
#define ACK_CONNECT_NUM "220"
#define ACK_PORT_NUM "200"




/*ftp server info*/
typedef struct 
{
    //char szUserName[16];
    //char szPassWd[32];
    char server_path[128];
    char server_filename[64];
    char new_filename[128];
    int data_sock;
    char data_ip[32];
    int  data_port;
    int client_server_sock;
    int file_handle;
}FTP_DATA_INFO;



static int itoa(int value, char * str, int radix);
static int send_cmd(int ctrl_sock,eu_cmd_type typ, const char *val,const char *ack_num);
static int enter_passive_mode(int ctrl_sock,char *data_ip, int * data_port);
static int enter_active_mode(int ctrl_sock);


static int get_data_sock(const char* server_ip,const int port);
static int get_active_data_sock(int client_server_sock);

static int GetAddr(const char *ifname, char *addr, int flag);
static int close_st_info(FTP_DATA_INFO * info);



FTP_DATA_INFO server_info;



static int GetAddr(const char *ifname, char *addr, int flag)
{
    struct sockaddr_in *sin;
    struct ifreq ifr;
    int sockfd;

    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        printf("socket create error!\n");
        return - 1;
    }

    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_ifrn.ifrn_name, ifname, IFNAMSIZ);

    if(ioctl(sockfd, flag, &ifr) < 0)
    {
        close(sockfd);
        return - 1;
    }

    close(sockfd);

    if(SIOCGIFHWADDR == flag)
    {
        memcpy((void *)addr, (const void *)&ifr.ifr_ifru.ifru_hwaddr.sa_data, 6);
    }
    else
    {
        sin = (struct sockaddr_in *)&ifr.ifr_ifru.ifru_addr;
        snprintf((char *)addr, IP_LENGTH, "%s", inet_ntoa(sin->sin_addr));
    }

    return 0;
}




static int itoa(int value, char * str, int radix)
{
 char temp[33];
  char *tp = temp; 
  int i; 
  unsigned v; 
  int sign; 
  char *sp;
  int num= 0;
  if(radix > 36 || radix < 1) 
    return 0; 
  sign = (radix == 10 && value < 0); //十進制負數 
  if(sign) 
    v = -value; 
  else
    v = (unsigned)value; 
  while(v || tp == temp)       //轉化操作 
  { 
    i = v % radix; 
    v = v / radix; 
    if(i < 10) 
      *tp++ = i + '0'; 
    else
      *tp++ = i + 'a' - 10; 
  } 
  if(str == 0) 
    str = (char*)malloc((tp - temp) + sign + 1); 
  sp = str; 
  if(sign)   //是負數的話把負號先加入陣列 
    *sp++ = '-'; 
  while(tp > temp)
  {
    *sp++ = *--tp;
    num++;
  }
  *sp = 0; 
  
  return num;
}



/*
* @brief 連接fpt服務器
* @param 無
* @return -1/成功建立的套接字
*/
int connect_ftp_server(const char* server_ip,const int port)
{
    int control_sock =-1;
    int ret =-1;
    struct sockaddr_in server;
    char read_buf[MAX_BUF]={0};
    struct timeval tv_out;
    
    memset(&server,0,sizeof(struct sockaddr_in));
    
    if(server_ip == NULL){
        printf("argc is NULL\n");
        return -1;
    }

    control_sock = socket(AF_INET,SOCK_STREAM,0);
    if(control_sock <0){
        printf("socket failed\n");
        return -1;
    }

    /*設定sock fd 接收超時時間*/
    tv_out.tv_sec =0;
    tv_out.tv_usec =500*1000;
    setsockopt(control_sock, SOL_SOCKET, SO_RCVTIMEO,&tv_out,sizeof(tv_out));
    
    
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(server_ip); 

    ret = connect(control_sock,(struct sockaddr *)&server,sizeof(server));
    if(ret < 0){
        printf("connect failed\n");
        return -1;
    }

    ret =1;
    
    /*接收服務端的應答訊息*/
    usleep(100*1000);
    ret = read(control_sock,read_buf,sizeof(read_buf));
    if(ret < 0){
        printf("read error\n");
        return -1;
    }
    printf("%s ret=%d \n",read_buf,ret);

    if(strncmp(read_buf,ACK_CONNECT_NUM,3) == 0) /*成功*/
    {
        printf("Connect ftp ok\n");
        return control_sock;
    }
    else
    {
        close(control_sock);
        return -1;
    }


}



/*
* @brief 被動模式連接獲取服務器的data_sock
* @param 無
* @return -1/成功建立的套接字
*/
static int get_data_sock(const char* server_ip,const int port)
{
    int data_sock =-1;
    int ret =-1;
    struct sockaddr_in server;
    char read_buf[MAX_BUF]={0};
    
    memset(&server,0,sizeof(struct sockaddr_in));
    
    if(server_ip == NULL){
        printf("argc is NULL\n");
        return -1;
    }

    data_sock = socket(AF_INET,SOCK_STREAM,0);
    if(data_sock <0){
        printf("socket failed\n");
        return -1;
    }
    /*設定為非阻塞*/
    //int cflags = fcntl(data_sock,F_GETFL,0);
    //fcntl(data_sock,F_SETFL,cflags|O_NONBLOCK);
    
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(server_ip); 

    ret = connect(data_sock,(struct sockaddr *)&server,sizeof(server));
    if(ret < 0){
        printf("connect failed\n");
        return -1;
    }
    /*無應答*/

    return data_sock;

}


/*主動模式獲取data sock 
  必須要在LIST等下載上傳命令發送后
  accept接受才能成功
  
*/
static int get_active_data_sock(int client_server_sock)
{
    int data_sock =-1;
    struct sockaddr_in client_name;
    int len;

    len = sizeof(client_name);
    data_sock = accept(client_server_sock,(struct sockaddr *)&client_name ,&len);
    if(data_sock <0)
    {
        printf("accept failed\n");
    }
    printf("data_sock = %d\n",data_sock);
    
    return data_sock;
}


/*
* @brief 登陸fpt服務器
* @param 無
* @return -1/成功回傳0
*/
int login_ftp_server(int ctrl_sock,const char *user_name, const char * passwd)
{
    int ret =-1;

    if((user_name == NULL) ||(passwd == NULL)){
        printf("argc is NULL\n");
        return -1;
    }
    
    ret = send_cmd(ctrl_sock,CMD_USER,user_name,ACK_USER_NUM);
    if(ret < 0){
            printf("send_cmd  %d failed \n",CMD_USER);
            return -1;
    }


    ret = send_cmd(ctrl_sock,CMD_PASS,passwd,ACK_PASS_NUM);
    if(ret < 0){
            printf("send_cmd  %d failed \n",CMD_PASS);
            return -1;
    }
    return 0;
}



/*
* @brief 給服務器發送指令
* @param 
* @return 失敗回傳-1/成功回傳0
    SIZE 回傳等到的檔案大小 
*/

static int send_cmd(int ctrl_sock,eu_cmd_type typ, const char *val,const char *ack_num)
{
    int ret =-1;
    char send_buf[MAX_BUF]={0};
    char read_buf[MAX_BUF]={0};
    char *pos= NULL;
    char tmp[64] ={0};

    if((typ == CMD_USER) ||(typ == CMD_PASS) || (typ == CMD_CWD)){
        if((val == NULL) ||(ack_num == NULL)){
            printf("argc is NULL\n");
            return -1;
        }
    }

    
    switch(typ){
    case CMD_USER:
        memset(send_buf,0,sizeof(send_buf));
        sprintf(send_buf,"USER %s\r\n",val);
        ret = write(ctrl_sock,send_buf,strlen(send_buf));
        if(ret < 0){
            printf("write failed\n");
            return -1;
        }
        memset(read_buf,0,sizeof(read_buf));
        ret = read(ctrl_sock,read_buf,sizeof(read_buf));
        if(ret < 0){
            printf("read failed\n");
            return -1;
        }
        ret = strncmp(read_buf,ack_num,strlen(ack_num));
        break;
    case CMD_PASS:
        memset(send_buf,0,sizeof(send_buf));
        sprintf(send_buf,"PASS %s\r\n",val);
        ret = write(ctrl_sock,send_buf,strlen(send_buf));
        if(ret < 0){
            printf("write failed\n");
            return -1;
        }
        memset(read_buf,0,sizeof(read_buf));
        ret = read(ctrl_sock,read_buf,sizeof(read_buf));
        if(ret < 0){
            printf("read failed\n");
            return -1;
        }
        ret = strncmp(read_buf,ack_num,strlen(ack_num));
        break;
    case CMD_PASV: /*只發送命令,函式外面接收提取資訊*/
        memset(send_buf,0,sizeof(send_buf));
        sprintf(send_buf,"PASV\r\n");
        ret = write(ctrl_sock,send_buf,strlen(send_buf));
        if(ret < 0){
            printf("write failed\n");
            return -1;
        }
        break;
    case CMD_CWD:
        memset(send_buf,0,sizeof(send_buf));
        sprintf(send_buf,"CWD %s\r\n",val);
        ret = write(ctrl_sock,send_buf,strlen(send_buf));
        if(ret < 0){
            printf("write failed\n");
            return -1;
        }
        usleep(500*1000);
        memset(read_buf,0,sizeof(read_buf));
        ret = read(ctrl_sock,read_buf,sizeof(read_buf));
        if(ret < 0){
            printf("read failed\n");
            return -1;
        }
        ret = strncmp(read_buf,ack_num,strlen(ack_num));
        break;
    case CMD_QUIT:
        memset(send_buf,0,sizeof(send_buf));
        sprintf(send_buf,"QUIT\r\n");
        ret = write(ctrl_sock,send_buf,strlen(send_buf));
        if(ret < 0){
            printf("write failed\n");
            return -1;
        }
        usleep(500*1000);
        memset(read_buf,0,sizeof(read_buf));
        ret = read(ctrl_sock,read_buf,sizeof(read_buf));
        if(ret < 0){
            printf("read failed\n");
            return -1;
        }
    //    ret = strncmp(read_buf,ack_num,strlen(ack_num));

    case CMD_LIST:
        memset(send_buf,0,sizeof(send_buf));
        sprintf(send_buf,"LIST %s\r\n",val);
        ret = write(ctrl_sock,send_buf,strlen(send_buf));
        if(ret < 0){
            printf("write failed\n");
            return -1;
        }
        memset(read_buf,0,sizeof(read_buf));
        usleep(100*1000); /*等待一會把266 也接識訓來*/
        ret = read(ctrl_sock,read_buf,sizeof(send_buf));
        if(ret < 0){
            printf("read failed\n");
            return -1;
        }
    //    ret = strncmp(read_buf,ack_num,strlen(ack_num));

        break;

    case CMD_STOR:
        memset(send_buf,0,sizeof(send_buf));
        sprintf(send_buf,"STOR %s\r\n",val);
        ret = write(ctrl_sock,send_buf,strlen(send_buf));
        if(ret < 0){
            printf("write failed\n");
            return -1;
        }
        usleep(50*1000);
        memset(read_buf,0,sizeof(read_buf));
        ret = read(ctrl_sock,read_buf,sizeof(read_buf));
        if(ret < 0){
            printf("read failed\n");
            return -1;
        }
    //    ret = strncmp(read_buf,ack_num,strlen(ack_num));

        break;

    case CMD_RETR:
        memset(send_buf,0,sizeof(send_buf));
        sprintf(send_buf,"RETR %s\r\n",val);
        ret = write(ctrl_sock,send_buf,strlen(send_buf));
        if(ret < 0){
            printf("write failed\n");
            return -1;
        }
        usleep(50*1000);
        memset(read_buf,0,sizeof(read_buf));
        ret = read(ctrl_sock,read_buf,sizeof(read_buf));
        if(ret < 0){
            printf("read failed\n");
            return -1;
        }
    //    ret = strncmp(read_buf,ack_num,strlen(ack_num));

        break;

    case CMD_SIZE_FTP:
        memset(send_buf,0,sizeof(send_buf));
        sprintf(send_buf,"SIZE %s\r\n",val);
        ret = write(ctrl_sock,send_buf,strlen(send_buf));
        if(ret < 0){
            printf("write failed\n");
            return -1;
        }
        usleep(50*1000);
        memset(read_buf,0,sizeof(read_buf));
        ret = read(ctrl_sock,read_buf,sizeof(read_buf));
        if(ret < 0){
            printf("read failed\n");
            return -1;
        }
        /* 客戶端接收服務器的回應碼和資訊,正常為 ”213 <size>” */
        pos = strstr(read_buf,ack_num);
        if(pos != NULL){
            pos += strlen(ack_num) +1;
            strcpy(tmp,pos);
            ret = atoi(tmp);
        }
        else{
            ret =-1;
        }
        
        break;    
    case CMD_PORT_FTP:
        memset(send_buf,0,sizeof(send_buf));
        sprintf(send_buf,"PORT %s\r\n",val);
        ret = write(ctrl_sock,send_buf,strlen(send_buf));
        if(ret < 0){
            printf("write failed\n");
            return -1;
        }
        usleep(50*1000);
        memset(read_buf,0,sizeof(read_buf));
        ret = read(ctrl_sock,read_buf,sizeof(read_buf));
        if(ret < 0){
            printf("read failed\n");
            return -1;
        }
        ret = strncmp(read_buf,ack_num,strlen(ack_num));
        break;    
    case CMD_MLSD:
        memset(send_buf,0,sizeof(send_buf));
        sprintf(send_buf,"MLSD\r\n");
        ret = write(ctrl_sock,send_buf,strlen(send_buf));
        if(ret < 0){
            printf("write failed\n");
            return -1;
        }
        break;
    default:break;
    }
    printf("FTP server ack= %s\n",read_buf);    
    return ret;

}





/*
* @brief 進入主動模式,讓服務器主動連接到客戶端的埠
* @param 無
* @return -1/成功回傳client_server_sock
*/
static int enter_active_mode(int ctrl_sock)
{
    int data_sock,server_sock;
    struct sockaddr_in name;
    struct sockaddr_in client_name,loc_addr;
    unsigned short server_port =0;
    int ret =-1;
    int len =0;
    char send_buf[64] ={0};
    char ip[20]={0};
    unsigned short  ip0,ip1,ip2,ip3,p1,p2;
    //char read_buf[128] ={0};

    struct timeval tv_out;

    /*設定sock fd 接收超時時間*/
    tv_out.tv_sec =3;
    tv_out.tv_usec =0;

  /*
    if(GetAddr("eth0", ip, SIOCGIFADDR) != 0)
    {    
         printf("get local ip failed\n");
        return -1;
    }
    printf("local ip =%s\n",ip);
    */
    memset(&name,0,sizeof(name));
    memset(&client_name,0,sizeof(client_name));

    /*通過ctrl_sock獲取到本機的ip*/
    len =sizeof(name);
    if(getsockname(ctrl_sock,(struct sockaddr*)&name,&len) == -1)
    {
        printf("get sock name failed\n");
        return -1;
    }
    
    sscanf(inet_ntoa(name.sin_addr),"%hu.%hu.%hu.%hu",&ip0,&ip1,&ip2,&ip3);
    
    
    server_sock = socket(AF_INET,SOCK_STREAM,0);
    if(server_sock <0){
        printf("get sock failed\n");
        return -1;
    }
    /*設定接收超時*/
    setsockopt(server_sock, SOL_SOCKET, SO_RCVTIMEO,&tv_out,sizeof(tv_out));
    
    name.sin_family = AF_INET;
    name.sin_port = 0;
    len = sizeof(name);
    ret = bind(server_sock,(struct sockaddr *)&name,len);
    if(ret < 0){
        printf("bind error\n");
        goto err0;
    }
    
    /*通過ctrl_sock獲取到系統分配到的埠*/
    len = sizeof(loc_addr);
    memset(&loc_addr,0,len);
    ret = getsockname(server_sock,(struct sockaddr *)&loc_addr,&len);
    if(ret < 0)
    {
        printf("get sock name failed\n");
        goto err0;
    }
    server_port = ntohs(loc_addr.sin_port); 

    p1 = server_port/256;
    p2 = server_port%256;

    ret = listen(server_sock,10);
    if(ret < 0)
    {
        printf("listen error\n");
        goto err0;
    }

    /*給服務器 命令 “PORT       \r\n*/
    /*將ip中的.更換為,*/
#if 0    
    &ip0 = strtok(ip,".");
    &ip1 = strtok(NULL,".");
    &ip2 = strtok(NULL,".");
    &ip3 = strtok(NULL,".");
#endif

    sprintf(send_buf,"%hu,%hu,%hu,%hu,%hu,%hu",ip0,ip1,ip2,ip3,p1,p2);
    printf("send_buf =%s server_port=%d\n",send_buf,server_port);
    ret = send_cmd(ctrl_sock,CMD_PORT_FTP, send_buf,ACK_PORT_NUM);
    if(ret < 0){
        printf("Send PORT failed\n");
        goto err0;
    }

    return server_sock;
err0:
    close(server_sock);
    return -1;
}



/*
* @brief 進入被動模式,讓服務器在資料埠監聽資料
* @param 無
* @return -1/成功回傳0
*/

static int enter_passive_mode(int ctrl_sock,char *data_ip, int * data_port)
{
    int ret =-1;
    char read_buf[MAX_BUF]={0};
    char tmp_buf[64]={0};
    unsigned char ip1,ip2,ip3,ip4,port1,port2;
    //char *tmp;

    if((data_ip == NULL) ||(data_port == NULL)){
        printf("argc is NULL\n");
        return -1;
    }
    ret = send_cmd(ctrl_sock,CMD_PASV,NULL,NULL);
    if(ret < 0){
            printf("send_cmd  %d failed \n",CMD_PASV);
            return -1;
    }

    usleep(100*1000);
    ret = read(ctrl_sock,read_buf,sizeof(read_buf));
    if(ret < 0){
        printf("read failed\n");
        return -1;
    }
    printf("rev =%d: %s\n",ret,read_buf);
    if(strstr(read_buf,ACK_PASV_NUM) != NULL){
        
        sscanf(strchr(read_buf,'(')+1,"%hhu,%hhu,%hhu,%hhu,%hhu,%hhu",&ip1,&ip2,&ip3,&ip4,&port1,&port2);
        //printf("ip1=%d,ip2=%d,ip3=%d,ip4=%d,port1 =%d ,port2 = %d\n",ip1,ip2,ip3,ip4,port1,port2);
        
        //snprintf(data_ip,sizeof(data_ip),"%hhu,%hhu,%hhu,%hhu",ip1,ip2,ip3,ip4);

        //memset(data_ip,0,sizeof(data_ip));
        //snprintf(data_ip,sizeof(data_ip),"%d.%d.%d.%d",ip1,ip2,ip3,ip4);
        //printf("data_ip = %s\n",data_ip);

        memset(data_ip,0,sizeof(data_ip));

        memset(tmp_buf,0,sizeof(tmp_buf));
        itoa(ip1,tmp_buf,10);
        strcat(data_ip,tmp_buf);
        strcat(data_ip,".");
        
        memset(tmp_buf,0,sizeof(tmp_buf));
        itoa(ip2,tmp_buf,10);
        strcat(data_ip,tmp_buf);
        strcat(data_ip,".");

        memset(tmp_buf,0,sizeof(tmp_buf));
        itoa(ip3,tmp_buf,10);
        strcat(data_ip,tmp_buf);
        strcat(data_ip,".");

        memset(tmp_buf,0,sizeof(tmp_buf));
        itoa(ip4,tmp_buf,10);
        strcat(data_ip,tmp_buf);
        //printf("data_ip1 = %s\n",data_ip);
    
        *data_port = port1*256+port2;
        
    }
    return 0;

}


/*
* @brief 從ftp服務器上下載檔案
* @param 
  ctrl_sock 控制服務器sock
  connect_mode= 0 設定服務器被動模式下載,非0服務器主動模式下載
  server_filepath_name 要下載檔案的路徑
  newfilename 下載到本地的路徑和檔案名字
  offset 設定下載檔案偏移的位置,不偏移寫0 ,可用于錯誤時續傳,
* @return -1/成功回傳未接收到的位元組數
*/

int down_file_ftpserver(int ctrl_sock, char *server_filepath_name,
const  char *newfilename,int connect_mode,int offset,eu_cmd_type typ)
{
    
    int ret =-1,file_size=0;
    char rec_buf[2048] ={0};
    char stri[128]={0};
    int read_size =0;
    char * pos =NULL;
    char *tmp = NULL;
    int flags =O_CREAT|O_RDWR|O_TRUNC;;

    
    close_st_info(&server_info);

    if(server_filepath_name == NULL  || newfilename == NULL){
        printf("argc is NULL\n");
        return -1;
    }

    
    /*提取出server_path file name*/
    tmp = server_filepath_name;

    if(tmp != NULL){
        while(tmp != NULL){    
            tmp = strstr(tmp,"/");
        //    printf("tmp =%x :%s\n",tmp,tmp);
            if(tmp != NULL){
                pos = tmp;
                tmp++; /*越過找到的"/"*/
            }
        }
        if(pos !=NULL){
            strncpy(server_info.server_path,server_filepath_name,pos-server_filepath_name+1);
            strcpy(server_info.server_filename,pos+1);
        }
        else{
                strcpy(server_info.server_filename,server_filepath_name);
        }
    }

    printf("server path = %s ;file name =%s\n",server_info.server_path,server_info.server_filename);

    if((typ !=CMD_RETR) && (typ !=CMD_LIST)){
        printf("typ value is not CMD_RETR or CMD_LIST\n");
        return -1;    

    }

    if(connect_mode){ /*主動模式*/
    
        server_info.client_server_sock =enter_active_mode(ctrl_sock);
        if(server_info.client_server_sock< 0)
        {
            printf("get data_scok failed\n");
            return -1;
        }

    }else{ /*被動模式*/
        ret = enter_passive_mode(ctrl_sock,server_info.data_ip, &server_info.data_port);
        if(ret < 0){
                printf("set passive mode failed\n");
                return -1;
        }
        printf("server_info.data_ip =%s, data_port =%d \n",server_info.data_ip,server_info.data_port);
        server_info.data_sock = get_data_sock(server_info.data_ip,server_info.data_port);
        if(server_info.data_sock < 0){
            printf("get data sock failed\n");
            return -1;
        }
    }

    /*更改目錄*/
      if(strlen(server_info.server_path) !=0) 
      {
         ret =  send_cmd(ctrl_sock,CMD_CWD,server_info.server_path,ACK_CWD_NUM);
         if(ret < 0){
                printf("set passive mode failed\n");
                goto err0;
         }
      }
      
     if(typ ==CMD_RETR){/*發送下載檔案命令*/

         /*設定下載檔案偏移的位置*/
         if(offset >0){
        
             flags =O_CREAT|O_RDWR|O_APPEND;     
              itoa(offset,stri,10);
             ret =    send_cmd(ctrl_sock,CMD_REST,stri,ACK_REST_NUM);
              if(ret < 0){
                     printf("set file offsize failed\n");
                     goto err0;
              }  
         }
         ret =  send_cmd(ctrl_sock,CMD_RETR,server_info.server_filename,ACK_RETR_NUM);
         if(ret < 0){
                printf("send RETR failed\n");
                goto err0;
         } 
     }
    else if(typ ==CMD_LIST){
         ret =  send_cmd(ctrl_sock,CMD_LIST,server_filepath_name,ACK_LIST_NUM);
         if(ret < 0){
                printf("send LIST failed\n");
                goto err0;
         } 
     }

    if(connect_mode){
        server_info.data_sock= get_active_data_sock(server_info.client_server_sock);
        if(server_info.data_sock <0)
        {
            printf("accept failed\n");
            goto err0;
        }
    }

      server_info.file_handle = open(newfilename,flags,0766);
     if(server_info.file_handle < 0){
        printf("open file failed\n");
            goto err0;
     }
     if(offset >0){
         lseek(server_info.file_handle,offset, SEEK_SET);
         read_size += offset;
     }
     
     for(;;){
         memset(rec_buf,0,sizeof(rec_buf));
        
        ret = recv(server_info.data_sock,rec_buf,sizeof(rec_buf),0);
         if(ret < 0)
         {
            printf("Read error\n");
            goto err1;
        }
        else if(ret == 0)
        {
            ret = read_size;
            goto err1;
        }
        else if(ret >0)
        {
            read_size += ret;
            ret = write(server_info.file_handle,rec_buf,ret);
            if(ret < 0)
            {
                printf("Write error\n");
                goto err1;
            }
            //printf("read_buf =%s\n",rec_buf);
        }
     
     }

    
err1:
    if(server_info.file_handle >0)
        close(server_info.file_handle);
err0:
    if(server_info.client_server_sock > 0)
        close(server_info.client_server_sock);

    if(server_info.data_sock > 0)
        close(server_info.data_sock);
    memset(rec_buf,0,sizeof(rec_buf));
    /* 客戶端接收服務器的回應碼和資訊,正常為 ”226 Transfer complete.” */
    read(ctrl_sock,rec_buf,sizeof(rec_buf)); /*有時訊息再發送完命令后里面就能收到,導致會阻塞在這個上面*/
    printf("%s \n Download file end!!\n",rec_buf);
    
    return ret;
    

}



/*
* @brief 上傳檔案從ftp服務器
* @param 
  ctrl_sock 控制服務器sock
  connect_mode = 0 設定服務器被動模式,非0 服務器主動模式
  server_filepath_name  上傳到服務器上的檔案路徑和名稱
  srcfilename 本地要上傳的路徑和檔案名字
  offset 設定下載檔案偏移的位置,不偏移寫0 ,可用于錯誤時續傳,
* @return -1/成功回傳上傳完成的位元組數
*/

int up_file_ftpserver(int ctrl_sock, char *server_filepath_name,
        const  char *srcfilename,int connect_mode,int offset)

{

    int ret =-1,file_size=0;
    int file_handle =0;
    char rec_buf[2048] ={0};
    int read_size =0;
    char stri[128]={0};
    char *tmp = NULL;
    char * pos =NULL;

    close_st_info(&server_info);
    if((server_filepath_name == NULL) ||(srcfilename == NULL)){
        printf("argc is NULL\n");
        return -1;
    }
    
    /*提取出server_path file name*/
    tmp = server_filepath_name;

    if(tmp != NULL){
        while(tmp != NULL){    
            tmp = strstr(tmp,"/");
        //    printf("tmp =%x :%s\n",tmp,tmp);
            if(tmp != NULL){
                pos = tmp;
                tmp++; /*越過找到的"/"*/
            }
        }
        if(pos !=NULL){
            strncpy(server_info.server_path,server_filepath_name,pos-server_filepath_name+1);
            strcpy(server_info.server_filename,pos+1);
        }
        else{
                strcpy(server_info.server_filename,server_filepath_name);
        }
    }

    printf("server path = %s ;file name =%s\n",server_info.server_path,server_info.server_filename);


    if(connect_mode){ /*主動模式*/
        
        server_info.client_server_sock =enter_active_mode(ctrl_sock);
        if(server_info.client_server_sock <= 0)
        {
            printf("get data_scok failed\n");
            return -1;
        }
        
    }else{ /*被動模式*/
        ret = enter_passive_mode(ctrl_sock,server_info.data_ip, &server_info.data_port);
        if(ret < 0){
                printf("set passive mode failed\n");
                return -1;
        }
        printf("server_info.data_ip =%s, data_port =%d\n",server_info.data_ip,server_info.data_port);
        server_info.data_sock =  get_data_sock(server_info.data_ip,server_info.data_port);
        if(server_info.data_sock < 0){
            printf("get data sock failed\n");
            return -1;
        }
    }
     /*更改目錄*/
       if(strlen(server_info.server_path) !=0) 
       {
          ret =  send_cmd(ctrl_sock,CMD_CWD, server_info.server_path,ACK_CWD_NUM);
          if(ret < 0){
                 printf("set passive mode failed\n");
                 goto err0;
          }
       }

     /*設定上傳檔案偏移的位置*/
     if(offset >0){
          itoa(offset,stri,10);
         ret =    send_cmd(ctrl_sock,CMD_REST,stri,ACK_REST_NUM);
          if(ret < 0){
                 printf("set file offsize failed\n");
                 goto err0;
          }  
     }

    /*發送上傳檔案命令*/
     ret =  send_cmd(ctrl_sock,CMD_STOR,server_info.server_filename,ACK_STOR_NUM);
     if(ret < 0){
            printf("send STOR failed\n");
            goto err0;
     } 
     
     if(connect_mode){
        server_info.data_sock= get_active_data_sock(server_info.client_server_sock);
        if(server_info.data_sock <0)
        {
            printf("accept failed\n");
            goto err0;
        }
    }
          
      server_info.file_handle = open(srcfilename,O_RDONLY);
     if(server_info.file_handle < 0){
        printf("open file failed\n");
            goto err0;
     }
     if(offset >0){
         lseek(server_info.file_handle,offset, SEEK_SET);
         read_size += offset;
     }
     
     for(;;){
         memset(rec_buf,0,sizeof(rec_buf));
        ret = read(server_info.file_handle,rec_buf,sizeof(rec_buf));
         if(ret < 0)
         {
             printf("read file error\n");
            goto err1;
        }
        else if(ret == 0)
        {
            ret = read_size;
            goto err1;
        }
        else if(ret >0)
        {
            //printf("read_buf =%s\n",rec_buf);
            
            ret = write(server_info.data_sock,rec_buf,ret);
            if(ret < 0)
            {
                    printf("Write failed\n");
                    goto err1;
    
            }else{
                  read_size += ret;
                // lseek(file_handle,read_size, SEEK_SET);
            }    
        }
     }

err1:
    if(server_info.file_handle >0)
        close(server_info.file_handle);
err0:
    if(server_info.client_server_sock > 0)
        close(server_info.client_server_sock);

    if(server_info.data_sock > 0)
        close(server_info.data_sock);
    memset(rec_buf,0,sizeof(rec_buf));
    /* 客戶端接收服務器的回應碼和資訊,正常為 ”226 Transfer complete.” */
    read(ctrl_sock,rec_buf,sizeof(rec_buf)); 
    printf("%s\n Up file end!!!\n",rec_buf);
    
    return ret;
    

}


static int close_st_info(FTP_DATA_INFO * info)
{
    if(info == NULL)
        return -1;

    if(info->file_handle >0)
        close(info->file_handle);

    if(info->client_server_sock > 0)
        close(info->file_handle);

    if(info->data_sock > 0)
        close(info->file_handle);

    memset(info,0,sizeof(FTP_DATA_INFO));
    return 0;
}


int get_fsize_ftpserver(int ctrl_sock, char *server_filepath_name)
{
    int ret =-1;

    if(server_filepath_name ==NULL){
        printf("argc is null\n");
        return -1;
    }
        /*發送上傳檔案命令*/
     ret =  send_cmd(ctrl_sock,CMD_SIZE_FTP,server_filepath_name,ACK_SIZE_NUM);
     if(ret < 0){
            printf("send SZIE failed\n");
            return -1;
     } 
    //printf("file size =%d\n",ret);
    return ret;
}




/*
* @brief 從ftp服務器退出
* @param 
  ctrl_sock 控制服務器sock
* @return -1/成功回傳0
*/
int quit_fpt_server(int ctrl_sock)
{
    int ret =-1;
    close_st_info(&server_info);
    
    ret = send_cmd(ctrl_sock,CMD_QUIT,NULL,ACK_QUIT_NUM);
    if(ret < 0)
        printf("quit fpt server error\n");

    close(ctrl_sock);
    
    return ret;
}

fpt.h

#ifndef __FTP_H__
#define __FTP_H__

#define FTP_SERVER_PORT 21

typedef enum
{
    CMD_USER =0, /*用戶名*/
    CMD_PASS,    /*密碼*/
    CMD_PASV,    /*讓服務器進入被動模式*/
    CMD_CWD,     /*切換作業目錄*/
    CMD_SIZE_FTP,    /*獲取檔案大小*/
    CMD_RETR,    /*下載檔案*/
    CMD_REST,      /*指定下載檔案的偏移量*/
    CMD_QUIT,      /*退出命令*/ 
    CMD_LIST,    /*串列*/
    CMD_STOR,    /*上傳檔案*/    
    CMD_PORT_FTP,/*發送客戶端埠給服務器*/
    CMD_MLSD, /*串列*/
    
}eu_cmd_type;



int connect_ftp_server(const char* server_ip,const int port);

int login_ftp_server(int ctrl_sock,const char *user_name, const char * passwd);

int down_file_ftpserver(int ctrl_sock, char *server_filepath_name,
    const  char *newfilename,int connect_mode,int offset,eu_cmd_type typ);


int up_file_ftpserver(int ctrl_sock, char *server_filepath_name,
        const  char *srcfilename,int connect_mode,int offset);

int get_fsize_ftpserver(int ctrl_sock, char *server_filepath_name);


int quit_fpt_server(int ctrl_sock);




#endif

 

 

 

遇到的主要問題記錄:

  1、實作FTP主動模式的時候,開始的accpet一直無法接收到服務器的連接請求,

后來用wireshark跟蹤FileZilla與服務器直接的通信資料才找到問題所在,原來accept要在

必須要在LIST等下載上傳命令發送后服務器才會連接過來,

      2、主要一定要關閉掉防火墻

      3、ftp還有一些傳送方法型別的選擇,本代碼中并沒有進行設定,后續用到的時候再進行完善,

  4、 代碼中接收服務器回復的時候增加了不少延遲,后來我有在sock 上設定接收超時,延遲太多反正感覺不是太好,

 

參考資料:

(29條訊息) FTP協議講解_zhubao124的博客-CSDN博客_ftp協議

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/544828.html

標籤:其他

上一篇:redis分布式鎖的實作

下一篇:西瓜視頻視頻顏值區如此驚艷,看我只用30行Python代碼批量下載,保存本地慢慢看

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more