FTP客戶端c代碼功能實現

来源:https://www.cnblogs.com/hylife/archive/2023/02/23/17148425.html
-Advertisement-
Play Games

現在市面上有很多免費的FTP軟體:如FileZilla ,那如果想自己在代碼中實現與ftp伺服器的上傳下載文件該如何實現那? 本質上ftp協議就是TCP基礎上建立的一種協議,具體如下。 FTP 概述 文件傳輸協議(FTP)作為網路共用文件的傳輸協議,在網路應用軟體中具有廣泛的應用。FTP的目標是提高 ...


  現在市面上有很多免費的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	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 早期的計算工具 計算尺是基本的手動計算器,易於乘除 1614年,蘇格蘭數學家納皮爾發現利用加減計算乘除的方法,依此發明對數,納皮爾在製作第一張對數表的時候,必需進行大量的乘法運算,而一條物理線的距離或區間可表示真數,於是他設計出計算器納皮爾的骨頭協助計算[2]。到1633年,英國牧師奧特雷德利用對數 ...
  • 對於數值模擬而言,無論是商軟或者開源軟體,並行計算都是非常重要的, 作為一名模擬工程師,如果想把自身數值模擬能力提升一個層次,需要對並行計算有很好的理解與應用 openfoam並行通信主要通過Pstream類完成 Pstream類,類如其名,parallel_stream,並行計算時使用的信息流 O ...
  • 本文已收錄至Github,推薦閱讀 👉 Java隨想錄 微信公眾號:Java隨想錄 CSDN: 碼農BookSea 這篇文章來講講限流,在高併發系統中限流是必不可少的,限流可以保證一部分的請求得到正常的響應,是一種自我保護的措施。限流可以保證使用有限的資源提供最大化的服務能力,按照預期流量提供服務 ...
  • 監聽的文件變化的方式有很多,但是比較完美的還是jNotify https://jnotify.sourceforge.net/ 對比一下監控方式的優缺點 | 方式 | 缺點 | | | | |java原生watch | 可能對文件時間獲取有缺毫秒的問題 | |commons-io | 沒有文件重命名 ...
  • Java基礎語法:運算符、包機制、JavaDoc 自增、自減、一元運算符:++、-- 例子:b = a++; -->先給b賦值,a再自增:b=a; a=a+1; b = ++a; -->a先自增,再給b賦值:a=a+1; b=a; 初識Math類 冪運算:Math.pow(a, b):其中a與b都是 ...
  • 教程簡介 適用於初學者的Apache HttpClient教程 - 從基本到高級概念的簡單簡單步驟學習Apache HttpClient,其中包括概述,環境設置,Http獲取請求,Http Post請求,響應處理程式,關閉連接,中止請求,攔截器,用戶身份驗證等示例,使用代理,代理身份驗證,基於表單的 ...
  • C語言線上運行編譯,是一款可線上編程編輯器,在編輯器上輸入C語言代碼,點擊運行,可線上編譯運行C語言,C語言代碼線上運行調試,C語言線上編譯,可快速線上測試您的C語言代碼,線上編譯C語言代碼發現是否存在錯誤,如果代碼測試通過,將會輸出編譯後的結果。 該線上工具由IT寶庫提供,線上工具後端由眾多Doc ...
  • 不知道各位老色批們平常看視頻都是在哪裡看的,有人說某魚舞蹈區,有人說某牙舞蹈區,要我說都不如西瓜shipin,這裡面個個都是人才,說話又好聽,超喜歡這裡的… 好了話不多說,我們直接開始本次的內容。 模塊安裝 本次需要使用的模塊是requests 模塊,沒安裝的小伙伴直接pip安裝即可。 環境 本次使 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...