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
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...