C++实现简单FTP客户端软件开发

本文实例为大家分享了C++实现简单FTP客户端软件开发的具体实现代码,供大家参考,具体内容如下

题目

简单FTP客户端软件开发(100分)网络环境中的一项基本应用就是将文件从一台计算机中复制到另一台可能相距很远的计算机中。而文件传送协议FTP是因特网上使用得最广泛的文件传送协议。FTP使用客户服务器方式。

设计要求:

1)以命令行形式运行
2)该FTP客户端程序具有以下基本功能:能完成FTP链接的打开和关闭操作;能执行文件的上传和下载;能完成目录的创建、删除等有关操作。
3)设计中加入新的功能:登录,进入上级或下级目录
要求可以传输简单的文本文件。

设计思路

1、分为两个端口,指令交流端口,数据交换端口
2、指令端口连接后一直保持通信,直到获得退出信号
3、数据交换端口在获得相关指令后重新建立连接

使用方法

1、分别创建两个C++项目
2、复制代码进入即可
3、若两个都在本地,使用本地回环测试IP,127.0.0.1
4、登录的部分在客户端中被注释掉了没有开启

Tips:

目前登录以及help部分仍有两个小bug,系由端口的命令断开以及连接部分引起,交由读者自己研究,若不想管可以不使用这两个功能

服务端——>Server

#include "Winsock2.h"
#include "windows.h"
#include 
#include 
using namespace std;

#define RECV_PORT 3312    //接收端口
#define SEND_PORT 4302    //发送端口
#define DATA_PORT 3313  //数据发送端口
#pragma comment(lib, "wsock32.lib")

SOCKET sockClient, sockServer;
SOCKET dataClient, dataServer;

sockaddr_in dataAddr;
sockaddr_in severAddr;//服务器地址
sockaddr_in ClientAddr;//客户端地址
sockaddr_in dataClientAddr;//数据地址

int addrLen;        //地址长度
char fileName[20];    //文件名
char order[20];        //命令
char rbuff[1024];    //接收缓冲区
char sbuff[1024];    //发送缓冲区

char namePassword[1024] = "user 123456";    //用户名和密码

//***************函数声明***************

DWORD startSock();
DWORD createSocket();
int sendFileRecord(SOCKET datatcps, WIN32_FIND_DATA *pfd);
int sendFileList(SOCKET datatcps);
int sendFile(SOCKET datatcps, FILE* file);
DWORD connectProcess();

//***************函数声明***************
DWORD startSock() {//初始化winsock
    WSADATA WSAData;
    if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0) {
        cout << "初始化失败" << endl;
        return -1;
    }
    return 1;
}
DWORD createSocket() {
    sockClient = socket(AF_INET, SOCK_STREAM, 0);
    dataClient = socket(AF_INET, SOCK_STREAM, 0);
    if (sockClient == SOCKET_ERROR||dataClient == SOCKET_ERROR) {
        cout << "创建失败" << endl;
        WSACleanup();
        return -1;
    }
    dataAddr.sin_family = AF_INET;
    severAddr.sin_family = AF_INET;
    dataAddr.sin_addr.s_addr = htonl(INADDR_ANY);//监听任意地址
    dataAddr.sin_port = htons(DATA_PORT);
    severAddr.sin_addr.s_addr = htonl(INADDR_ANY);//监听任意地址
    //cout< 0) {
                    fwrite(rbuff, sizeof(char), cnt, fd);//把cnt个数据长度为char的数据从rbuff输入到fd指向的文件
                }
                cout << "成功获得文件" << fileName << endl;
                closesocket(dataServer);
                fclose(fd);
            }//put
            else if (strncmp(rbuff, "pwd", 3) == 0) {
                char path[1000];
                GetCurrentDirectory(sizeof(path), path);//找到当前进程的当前目录
                strcpy(sbuff, path);
                send(sockServer, sbuff, sizeof(sbuff), 0);
            }//pwd
            else if (strncmp(rbuff, "ls", 2) == 0) {
                strcpy(sbuff, rbuff);
                send(sockServer, sbuff, sizeof(sbuff), 0);
                sendFileList(dataClient);
            }//dir
            else if(strncmp(rbuff, "mkdir", 5)==0){
                strcpy(fileName, rbuff + 6);
                strcpy(sbuff,rbuff);
                send(sockServer, sbuff, sizeof (sbuff),0);//发送回信息
                mkdir(fileName);
            }//mkdir
            else if(strncmp(rbuff,"del", 3)==0){
                strcpy(fileName, rbuff + 4);//获得要删的文件名
                strcpy(sbuff,rbuff);
                send(sockServer, sbuff, sizeof (sbuff),0);//发送回信息
                delFile(fileName);
            }//del
            else if (strncmp(rbuff, "cd", 2) == 0) {
                strcpy(fileName, rbuff + 3);
                strcpy(sbuff, rbuff);
                send(sockServer, sbuff, sizeof(sbuff), 0);
                char path[1000];
                GetCurrentDirectory(sizeof(path), path);//找到当前进程的当前目录
                strcat(path,"\\");
                strcat(path,fileName);
                SetCurrentDirectory(path);//设置当前目录
            }//cd
            else if (strncmp(rbuff, "user", 4) == 0) {
                char tbuff[1024];
                strcpy(tbuff, rbuff + 5);
                strcat(tbuff, " ");
                memset(rbuff, '\0', sizeof(rbuff));
                strcpy(sbuff, "成功获取用户名\0");
                send(sockServer, sbuff, sizeof(sbuff), 0);

                recv(sockServer, rbuff, sizeof(rbuff), 0);
                cout << endl << "获取并执行的命令:" << rbuff << endl;
                strcat(tbuff, rbuff + 5);
                if (strcmp(tbuff, namePassword) == 0) {//验证是否正确并返回数据给客户端
                    send(sockServer, "right\0", sizeof(sbuff), 0);
                }else {
                    send(sockServer, "wrong\0", sizeof(sbuff), 0);
                }
            }//user pass

        }
        closesocket(sockServer);
    }
}
int sendFile(SOCKET datatcps, FILE* file) {
    int dataAddLen = sizeof (dataClientAddr);
    dataServer = accept(datatcps, (struct sockaddr FAR*)&dataClientAddr, &dataAddLen);
    cout << "正在发送文件…" << endl;
    memset(sbuff, '\0', sizeof(sbuff));
    while(1) {//从文件中循环读取数据并发送至客户端
        int len = fread(sbuff, 1, sizeof(sbuff), file);//把file指针指向的文件中的内容读取到sbuff中
       //cout<<"sbuff内容:"< ftLastWriteTime, &ft);//Converts a file time to a local file time.

    SYSTEMTIME lastWriteTime;
    FileTimeToSystemTime(&ft, &lastWriteTime);

    const char *dir = pfd -> dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? "" : " ";
    sprintf(fileRecord, "%04d-%02d-%02d %02d:%02d %5s %10d   %-20s\n",
        lastWriteTime.wYear,
        lastWriteTime.wMonth,
        lastWriteTime.wDay,
        lastWriteTime.wHour,
        lastWriteTime.wMinute,
        dir,
        pfd -> nFileSizeLow,
        pfd -> cFileName
    );
    if (send(datatcps, fileRecord, strlen(fileRecord), 0) == SOCKET_ERROR) {
        //通过datatcps接口发送fileRecord数据,成功返回发送的字节数
        cout << "发送失败" << endl;
        return 0;
    }
    return 1;
}
int main(){

    if (startSock() == -1 || createSocket() == -1 || connectProcess() == -1) {
        return -1;
    }
    return 1;
}

客户端——>Client

#include 
#include 
#include 
#include 
#include 
using namespace std;

#define RECV_PORT 3312    //接收端口
#define SEND_PORT 4302    //发送端口
#define DATA_PORT 3313  //数据接受端口

#pragma comment(lib, "wsock32.lib")    //加载ws2_32.dll,它是Windows Sockets应用程序接口, 用于支持Internet和网络应用程序。

SOCKET sockClient;        //客户端对象
sockaddr_in serverAddr;    //服务器地址
sockaddr_in dataAddr;
SOCKET dataClient;      //数据对象

char inputIP[20];        //存储输入的服务器IP
char fileName[20];        //文件名
char rbuff[1024];        //接收缓冲区
char sbuff[1024];        //发送缓冲区
bool checkFlag = false;            //标志是否通过登陆

//***********************函数声明***********************
DWORD startSock();                            //启动winsock并初始化
DWORD createSocket(SOCKET &mySocket);                        //创建socket
DWORD callServer(SOCKET &mySocket,sockaddr_in addr);                            //发送连接请求

int command();                             //执行命令
void help();                                //菜单
void list(SOCKET &sockfd);                    //列出远方当前目录
DWORD sendTCP(char data[]);                    //发送要执行的命令至服务端
int user();                                    //上传用户名
int pass();                                    //上传密码
int sendFile(SOCKET &datatcps, FILE* file);    //put 传送给远方一个文件
//***********************函数声明***********************


//***********************函数定义***********************
DWORD startSock() { //启动winsock并初始化
    WSADATA WSAData;
    char a[20];
    memset(a, 0, sizeof(a));
    if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0) { //加载winsock版本
        cout << "sock初始化失败" << endl;
        return -1;
    }
    if (strncmp(inputIP, a, sizeof(a)) == 0) {
        cout << "请输入要连接的服务器IP:";
        cin >> inputIP;
    }
    //设置地址结构
    serverAddr.sin_family = AF_INET;                    //表明底层是使用的哪种通信协议来递交数据的,AF_INET表示使用 TCP/IPv4 地址族进行通信
    serverAddr.sin_addr.s_addr = inet_addr(inputIP);    //指定服务器IP,十进制转化成二进制IPV4地址
    serverAddr.sin_port = htons(RECV_PORT);                //设置端口号,htons用于将主机字节序改为网络字节序

    dataAddr.sin_family = AF_INET;
    dataAddr.sin_addr.s_addr = inet_addr(inputIP);
    dataAddr.sin_port = htons(DATA_PORT);//数据端口和控制端口不一样

    return 1;
}
DWORD createSocket(SOCKET &mySocket) { //创建socket
    //要使用套接字,首先必须调用socket()函数创建一个套接字描述符,就如同操作文件时,首先得调用fopen()函数打开一个文件。
    mySocket = socket(AF_INET, SOCK_STREAM, 0);//当scoket函数成功调用时返回一个新的SOCKET(Socket Descriptor) //SOCK_STREAM(流式套接字):Tcp连接,提供序列化的、可靠的、双向连接的字节流。支持带外数据传输
    if (mySocket == SOCKET_ERROR) {
        cout << "创建socket失败" << endl;
        WSACleanup();//终止Ws2_32.dll 的使用
        return -1;
    }
    return 1;
}
DWORD callServer(SOCKET &mySocket,sockaddr_in addr) { //发送连接请求
    createSocket(mySocket);
    if (connect(mySocket, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {//connect()创建与指定外部端口的连接
        cout << "连接失败" << endl;
        memset(inputIP, 0, sizeof(inputIP));
        return -1;
    }
    return 1;
}
void help() { //帮助菜单
    cout << "        ___________________________________________  " << endl
         << "       |                FTP帮助菜单                 |   " << endl
         << "       | 1、get 下载文件 [输入格式: get 文件名 ]       |   " << endl
         << "       | 2、put 上传文件 [输入格式:put 文件名]         |   " << endl
         << "       | 3、pwd 显示当前文件夹的绝对路径                |    " << endl
         << "       | 4、ls 显示远方当前目录的文件                 |   " << endl
         << "       | 5、mkdir 新建文件夹 [输入格式:mkdir 文件名]   |   " << endl
         << "       | 6、del 删除文件夹 [输入格式:del 文件名]       |   " << endl
         << "       | 7、cd  改变远方当前目录和路径                 |   " << endl
         << "       |         进入下级目录: cd 路径名              |   " << endl
         << "       |         进入上级目录: c ..                  |   " << endl
         << "       | 8、? 或者 help 进入帮助菜单                  |   " << endl
         << "       | 9、quit 退出FTP                            |   " << endl
         << "       |___________________________________________|    " << endl;
}
DWORD sendTCP(char data[]) { //发送要执行的命令至服务端
    int length = send(sockClient, data, strlen(data), 0);
    if (length <= 0) {
        cout << "发送命令至服务端失败" << endl;
        closesocket(sockClient);//当不使用socket()创建的套接字时,应该调用closesocket()函数将它关闭,就如同调用fclose()函数关闭一个文件,用来进行套接字资源的释放。
        WSACleanup();
        return -1;
    }
    return 1;
}
int sendFile(SOCKET &datatcps, FILE* file) { //put 传送给远方一个文件
    callServer(datatcps,dataAddr);
    cout << "正在传输文件…" << endl;
    memset(sbuff, '\0', sizeof(sbuff));
    while (1) { //从文件中循环读取数据并发送
        int len = fread(sbuff, 1, sizeof(sbuff), file); //fread从file文件读取sizeof(sbuff)长度的数据到sbuff,返回成功读取的数据个数

        if (send(datatcps, sbuff, sizeof(rbuff), 0) == SOCKET_ERROR) {
            cout << "与客户端的连接中断" << endl;
            closesocket(datatcps);
            return 0;
        }
        if (len < sizeof(sbuff)) {
            break;
        }
    }
    closesocket(datatcps);
    cout << "传输完成" << endl;
    return 1;
}
void list(SOCKET &sockfd) { //列出远方当前目录
    callServer(sockfd,dataAddr);
    int nRead;
    memset(sbuff, '\0', sizeof(sbuff));
    while (1) {
        nRead = recv(sockfd, rbuff, sizeof(rbuff), 0);
        //recv通过sockClient套接口接受数据存入rbuff缓冲区,返回接收到的字节数
        if (nRead == SOCKET_ERROR) {
            cout << "读取时发生错误" << endl;
            exit(1);
        }
        if (nRead == 0) { //数据读取结束
            break;
        }
        cout<<"nRead长度"<> name;
    strcat(order, operation), strcat(order, " "), strcat(order, name);
    sprintf(buff, order);
    sendTCP(buff);                                    //发送指令
    recv(sockClient, rbuff, sizeof(rbuff), 0);        //接收信息
    cout << rbuff << endl;
    return 1;
}
int pass() {
    char operation[10], name[20];        //操作与文件名
    char order[30] = "\0";                //输入的命令
    char buff[80];                        //用来存储经过字符串格式化的order
    cout << "请输入密码指令(pass 密码):" ;
    strcpy(operation,"pass");
    cin >> name;
    strcat(order, operation), strcat(order, " "), strcat(order, name);
    sprintf(buff, order);
    sendTCP(buff);                                    //发送指令
    recv(sockClient, rbuff, sizeof(rbuff), 0);        //接收信息
    cout << rbuff << endl;
    if (strcmp(rbuff, "wrong") == 0) {
        return 0;
    }
    return 1;
}
//***********************函数定义***********************

int command(){
    char operation[10], name[20];        //操作与文件名
    char order[30] = "\0";                //输入的命令
    char buff[80];                        //用来存储经过字符串格式化的order
    FILE *fd1, *fd2;                    //File协议主要用于访问本地计算机中的文件,fd指针指向要访问的目标文件
    int cnt;
    //发送连接请求成功,初始化数据
    memset(operation, 0, sizeof(operation));
    memset(name, 0, sizeof(name));
    memset(order, 0, sizeof(order));
    memset(buff, 0, sizeof(buff));
    memset(rbuff, 0, sizeof(rbuff));
    memset(sbuff, 0, sizeof(sbuff));
        cout << endl << "请输入要执行的指令: ";
        cin >> operation;
//        cout<<"why:"<> name;
        } else if (strncmp(operation, "quit", 4) == 0) { ///退出功能
            cout << "感谢您的使用" << endl;
            return 1;
        } else if (strncmp(operation, "?", 1) == 0 || strncmp(operation, "help", 4) == 0) { ///帮助菜单功能
            help();
        }else if(strncmp(operation,"ls",2)==0|| strncmp(operation,"pwd",3)==0){

        }
        else{
            cout<<"非法输入"< 0) {
               // cout<<"缓冲区"< 
 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

你可能感兴趣的:(C++实现简单FTP客户端软件开发)