FTP客户端c代码功能实现

yizhihongxing

  现在市面上有很多免费的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 上设置接收超时,延迟太多反正感觉不是太好。

   5、最近更换了一个ftp服务器平台时,发现新问题。发现下载后的文件大小变大了。经过对比发现多了很多0D .

从网上查询发现时Linux系统的回车时\n. 而windows系统的回车时\r\n。 所以当ftp 采用ascii方式传播的时候是有着问题的。

解决方法是发送TYPE  I\r\n命令将传输方式设置为二进制传输 。问题解决。

 

命令及响应码

命令 描述
ABOR 中断数据连接程序
ACCT <account> 系统特权帐号
ALLO <bytes> 为服务器上的文件存储器分配字节
APPE <filename> 添加文件到服务器同名文件
CDUP <dir path> 改变服务器上的父目录
CWD <dir path> 改变服务器上的工作目录
DELE <filename> 删除服务器上的指定文件
HELP <command> 返回指定命令信息
LIST <name> 如果是文件名列出文件信息,如果是目录则列出文件列表
MODE <mode> 传输模式(S=流模式,B=块模式,C=压缩模式)
MKD <directory> 在服务器上建立指定目录
NLST <directory> 列出指定目录内容
NOOP 无动作,除了来自服务器上的承认
PASS <password> 系统登录密码
PASV 请求服务器等待数据连接
PORT <address> IP 地址和两字节的端口 ID
PWD 显示当前工作目录
QUIT 从 FTP 服务器上退出登录
REIN 重新初始化登录状态连接
REST <offset> 由特定偏移量重启文件传递
RETR <filename> 从服务器上找回(复制)文件
RMD <directory> 在服务器上删除指定目录
RNFR <old path> 对旧路径重命名
RNTO <new path> 对新路径重命名
SITE <params> 由服务器提供的站点特殊参数
SMNT <pathname> 挂载指定文件结构
STAT <directory> 在当前程序或目录上返回信息
STOR <filename> 储存(复制)文件到服务器上
STOU <filename> 储存文件到服务器名称上
STRU <type> 数据结构(F=文件,R=记录,P=页面)
SYST 返回服务器使用的操作系统
TYPE <data type> 数据类型(A=ASCII,E=EBCDIC,I=binary)
USER <username> 系统登录的用户名

 

响应代码 解释说明
110 新文件指示器上的重启标记
120 服务器准备就绪的时间(分钟数)
125 打开数据连接,开始传输
150 打开连接
200 成功
202 命令没有执行
211 系统状态回复
212 目录状态回复
213 文件状态回复
214 帮助信息回复
215 系统类型回复
220 服务就绪
221 退出网络
225 打开数据连接
226 结束数据连接
227 进入被动模式(IP 地址、ID 端口)
230 登录因特网
250 文件行为完成
257 路径名建立
331 要求密码
332 要求帐号
350 文件行为暂停
421 服务关闭
425 无法打开数据连接
426 结束连接
450 文件不可用
451 遇到本地错误
452 磁盘空间不足
500 无效命令
501 错误参数
502 命令没有执行
503 错误指令序列
504 无效命令参数
530 未登录网络
532 存储文件需要帐号
550 文件不可用
551 不知道的页类型
552 超过存储分配
553 文件名不允许

 

 

 

参考资料:

(29条消息) FTP协议讲解_zhubao124的博客-CSDN博客_ftp协议

FTP:文件传输协议(指令及响应代码) - CTHON - 博客园 (cnblogs.com)

 

原文链接:https://www.cnblogs.com/hylife/p/17148425.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:FTP客户端c代码功能实现 - Python技术站

(0)
上一篇 2023年4月18日
下一篇 2023年4月18日

相关文章

  • C语言类的基本语法详解

    C语言类的基本语法详解 概述 C语言是一门广泛使用的编程语言,具有较强的系统编程能力。本文将详细介绍C语言的基本语法。 变量 C语言中的变量由其类型和名称两部分组成。变量的类型定义变量能够保存的数据类型,常用的变量类型包括: int: 整数类型,占用4个字节 float: 单精度浮点数类型,占用4个字节 double: 双精度浮点数类型,占用8个字节 cha…

    C 2023年5月22日
    00
  • c语言实现24小时制转换为12小时制示例

    C语言实现24小时制转换为12小时制的攻略 前言 在现实生活中,我们经常会遇到需要将时间格式进行转换的需求。其中最常见的需求就是将24小时制的时间转换为12小时制的时间。本文将详细讲解如何使用C语言实现24小时制转换为12小时制的示例。 程序思路 该程序的源码主要包含以下几个步骤: 获取系统时间; 将24小时制的时间转换为12小时制的时间; 输出转换后的时间…

    C 2023年5月23日
    00
  • C语言实现运动会管理系统

    C语言实现运动会管理系统攻略 运动会管理系统是一个基于C语言编写的管理软件。本文将详细讲解如何使用C语言实现运动会管理系统。 一、系统功能 运动会管理系统主要包括以下功能: 注册和登录:新用户可以注册账号,已有账号可以登录系统。 学生信息管理:包括添加、修改、查询和删除学生信息。 运动员报名和成绩管理:运动员可以报名参加比赛,并记录比赛成绩。 赛程安排管理:…

    C 2023年5月23日
    00
  • C 基本语法

    当我们学习一个新的编程语言时,首先需要了解其基本语法。下面是 C 语言基本语法的完整使用攻略。 基本语法 C 语言基本语法包括:标识符、数据类型、常量、变量、运算符、表达式、语句和函数。 标识符 标识符是用于表示变量、函数、数组等的名称。在 C 语言中,标识符遵循如下规则: 标识符由字母、下划线和数字组成 第一个字符必须是字母或下划线 标识符大小写敏感 数据…

    C 2023年5月10日
    00
  • 面试题积累_01

    1 如何判断一个数是否为奇数? //常规方法 bool isOdd_Method1(int n) { if (n % 2) return true; else return false; } //高效方法 bool isOdd_Method2(int n) { //奇数的二进制形式最后一位一定是1 return n & 0x1; } 注:二进制除了最…

    C语言 2023年4月18日
    00
  • 一次因信号量引发的tomcat异常退出解决

    下面是一次因信号量引发的Tomcat异常退出解决的完整攻略: 背景 在使用Tomcat时,有时候可能会因为进程无法获取到信号量而导致Tomcat异常退出。这种问题通常会在并发量较大的情况下出现。 解决方法 解决这种问题的方法是通过增加操作系统的信号量来提高并发量。下面是具体的操作步骤: 查看当前信号量的情况: ipcs -ls 在这个命令中,参数 -l 表示…

    C 2023年5月22日
    00
  • C语言字符串替换:字符,字符串,字符数组详解

    C语言字符串替换:字符、字符串、字符数组详解 在C语言中,字符串替换是一个很基础的操作,常用的字符串替换包括用指定字符替换一个字符串中的某个字符,用指定字符串替换一个字符串中的某个子串,以及用另一个字符串替换一个字符数组中的某个子数组等。本文将详细讲解这三种情况的操作方法。 用指定字符替换一个字符串中的某个字符 首先让我们看一个简单的例子。下面的代码将见一个…

    C 2023年5月23日
    00
  • C语言归排与计排深度理解

    C语言归排与计排深度理解 什么是排序算法? 排序算法是计算机程序设计中最常见的问题之一。排序算法是一种将输入元素按特定顺序排列的算法。排序算法分为内部排序和外部排序:- 对于内存(内部)排序,其输入和输出均存储在计算机内存中。- 对于外存(外部)排序,其输入或输出涉及到显式的输入/输出操作,通常通过磁带、磁盘或因特网进行数据传输和存储。 本篇文档主要介绍内部…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部