FTP 编写 4:命令解析

原创
2017/07/05 15:30
阅读数 139

FTP 编写 4:命令解析


    我们知道在现实生活中使用的 FTP 是应答式的,客户端和服务端按照一定的规定进行交流,不是随便弄的,在上几篇中的 FTP 没有人机交互的功能。所以这篇文章的主要内容是按照平时 FTP 的交流规则重构一下整个工程的代码,并加入命令解析功能。

命令解析

    这部分命令解析只是简单模拟一下 FTP 的,在 FTP 的规则中还有应答返回值的规定,现在把它省略,简单定义一下服务端和客户端的交流规则

    现在的交流过程是这样的:在服务端和客户端启动以后,客户端向服务端发起连接,然后服务端向客户端发送一条欢迎信息;然后等待客户端发送相应的指令,根据指令进行答复和操作

//命令解析函数:将受到的命令进行分解,用字符分割函数分成指令和参数两部分
int Client::commandParse(char * instruck, std::string & paramter)
{
    std::string string(buf);
    std::cout << string;
    std::vector<std::string> command;
    //以空格为分隔符进行分割
    Tool::splitString(string, command, std::string(" "));

    //判断传入的参数个数是否正确
    if (command.size() != 2) {
        paramter = "";
        return -1;
    }

    //返回相应指令对应的数值
    if (command[0] == "STOR") {
        paramter = command[1];
        return 1;
    }
    if (command[0] == "RETR") {
        paramter = command[1];
        return 2;
    }

    //默认返回错误指令
    return 0;
}

//字符分割函数
void Tool::splitString(const std::string & input, std::vector<std::string>& output, std::string &delim)
{
    std::string::size_type pos1, pos2;
    pos1 = 0;
    pos2 = input.find(delim);

    while (std::string::npos != pos2)
    {
        output.push_back(input.substr(pos1, pos2 - pos1));

        pos1 = pos2 + delim.size();
        pos2 = input.find(delim, pos1);
    }
    if (pos1 != input.length())
        output.push_back(input.substr(pos1));
}

服务端重构

    服务端的重构主要是将服务端运行的部分抽离出来写成一个类,类中包含了服务端的所有功能函数,并且在多线程中运行,服务端中的运行函数如下:

void Server::running()
{
    //与客户端进行连接以后就发送一个欢迎信息
    welcome();

    //然后进入无限循环,等待客户端的命令进行下一步操作
    while (true) {
        if (recv(server, buf, maxLen, 0) == 0) {
            std::cout << "recv() Faied!\n";
        }

        std::string paramter;
        int cmd = commandParse(buf, paramter);

        switch (cmd) {
        case 0:
            std::cout << "Command is invalid\n";
            break;

        case 1:
            std::cout << "Start receive the file from client.\n";
            receiveFile(paramter);
            break;

        case 2:
            std::cout << "Start send file to client.\n";
            sendFile(paramter);
            break;

        default:
            std::cout << "Command is invalid\n";
        }
    }
}

客户端重构

    客户端也是将功能封装到一个类里面,但不需要多线程,运行函数如下:

void Client::running()
{
    memset(buf, 0, sizeof(buf));
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cout << "Failed to load Winsock\n";
        system("pause");
        return;
    }

    //char addr[20] = "113.54.167.16";
    char addr[20] = "192.168.1.119";

    //创建Socket
    ser.sin_family = AF_INET;
    ser.sin_port = htons(iPort);
    inet_pton(AF_INET, addr, (void*)&ser.sin_addr.s_addr);
    client = socket(AF_INET, SOCK_STREAM, 0);
    if (client == INVALID_SOCKET) {
        std::cout << "socket() Failed\n";
        system("pause");
        return;
    }

    //连接并进行简单的操作
    if (connect(client, (struct sockaddr*)&ser, sizeof(ser)) == INVALID_SOCKET) {
        std::cout << "connect() Failed\n";
        system("pause");
        return;
    }
    else {
        //接收服务端发送的数据
        iLen = recv(client, buf, sizeof(buf), 0);
        if (iLen == 0) {
            system("pause");
            return;
        }
        else if (iLen == SOCKET_ERROR) {
            std::cout << "recv() Failed\n";
            system("pause");
            return;
        }
        std::cout << "recv() data from server:" << buf << std::endl;
    }

    while (true) {
        //输入相应的数据发送给服务端
        std::cout << ">>";
        std::cin.getline(buf, 1024);

        std::string paramter;
        int cmd = commandParse(buf, paramter);

        switch (cmd) {
        case 0:
            std::cout << "Command is invalid\n";
            break;

        case 1:
            std::cout << "Start receive the file from client.\n";
            downloadFile(paramter);
            break;

        case 2:
            std::cout << "Start send file to client.\n";
            uploadFile(paramter);
            break;

        default:
            std::cout << "Command is invalid\n";
        }
    }
}

源代码

客户端

//main.cpp
#pragma comment(lib,"ws2_32.lib")

#include "Client.h"

void main()
{
    Client client;
    client.running();
    system("pause");
}

//Client.h
#pragma once
#include <WinSock2.h>
#include <string>

class Client
{
public:
    Client();
    ~Client();

    void running();
    bool uploadFile(std::string file);
    bool downloadFile(std::string file);
    int commandParse(char* instruck, std::string &paramter);

private:
    SOCKET client;
    char buf[1024];
    //定义相应的数据
    WSADATA wsaData;
    int iPort = 5050;//对应的服务端的端口
    int iLen, iSend;
    struct sockaddr_in ser;

};

//Client.cpp
#include "Client.h"
#include "Tool.h"

#include <iostream>
#include <WS2tcpip.h>

Client::Client()
{
}


Client::~Client()
{
    //关闭连接并退出
    closesocket(client);
    //关闭 Winsock
    WSACleanup();
}

void Client::running()
{
    memset(buf, 0, sizeof(buf));
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cout << "Failed to load Winsock\n";
        system("pause");
        return;
    }

    //char addr[20] = "113.54.167.16";
    char addr[20] = "192.168.1.119";

    //创建Socket
    ser.sin_family = AF_INET;
    ser.sin_port = htons(iPort);
    inet_pton(AF_INET, addr, (void*)&ser.sin_addr.s_addr);
    client = socket(AF_INET, SOCK_STREAM, 0);
    if (client == INVALID_SOCKET) {
        std::cout << "socket() Failed\n";
        system("pause");
        return;
    }

    //连接并进行简单的操作
    if (connect(client, (struct sockaddr*)&ser, sizeof(ser)) == INVALID_SOCKET) {
        std::cout << "connect() Failed\n";
        system("pause");
        return;
    }
    else {
        //接收服务端发送的数据
        iLen = recv(client, buf, sizeof(buf), 0);
        if (iLen == 0) {
            system("pause");
            return;
        }
        else if (iLen == SOCKET_ERROR) {
            std::cout << "recv() Failed\n";
            system("pause");
            return;
        }
        std::cout << "recv() data from server:" << buf << std::endl;
    }

    while (true) {
        //输入相应的数据发送给服务端
        std::cout << ">>";
        std::cin.getline(buf, 1024);

        std::string paramter;
        int cmd = commandParse(buf, paramter);

        switch (cmd) {
        case 0:
            std::cout << "Command is invalid\n";
            break;

        case 1:
            std::cout << "Start receive the file from client.\n";
            downloadFile(paramter);
            break;

        case 2:
            std::cout << "Start send file to client.\n";
            uploadFile(paramter);
            break;

        default:
            std::cout << "Command is invalid\n";
        }
    }
}

bool Client::uploadFile(std::string file)
{
    return false;
}

bool Client::downloadFile(std::string file)
{
    return false;
}

int Client::commandParse(char * instruck, std::string & paramter)
{
    std::string string(buf);
    std::cout << string;
    std::vector<std::string> command;
    Tool::splitString(string, command, std::string(" "));

    if (command.size() != 2) {
        paramter = "";
        return -1;
    }

    if (command[0] == "STOR") {
        paramter = command[1];
        return 1;
    }
    if (command[0] == "RETR") {
        paramter = command[1];
        return 2;
    }

    return 0;
}

服务端

//main.cpp
#include <Winsock2.h>
#include <Ws2tcpip.h>
#include <iostream>
#include <thread>
#include <vector>
#pragma comment(lib,"ws2_32.lib")

#include "Server.h"

//多线程函数,创建的多线程运行此函数
void server(SOCKET s);

void main()
{
    //定义相关的数据
    int iPort = 5050;//定义其端口
    WSADATA wsaData;//Winsock 的启动参数
    SOCKET sListen, sAccept;//套接口关键字,分别用于监听和接收连接
    int iLen;
    int iSend;
    char buf[] = "I am a server";
    struct sockaddr_in ser, cli;//网络地址
    //定义多线程指针,用于创建线程
    std::thread* t;
    //用于线程的管理,保存创建的多线程指针,程序结束时释放占用的内存
    std::vector<std::thread*> tManage;
    Server* server = NULL;


    std::cout << "----------------------------\n";
    std::cout << "Server waitting\n";
    std::cout << "----------------------------\n";

    //启动winSocket
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cout << "Failed to load Winsock.\n";
        return;
    }

    //创建Socket
    sListen = socket(AF_INET, SOCK_STREAM, 0);
    if (sListen == INVALID_SOCKET) {
        std::cout << "socket() Failed:" << WSAGetLastError() <<"\n";
        return;
    }

    //绑定IP地址
    ser.sin_family = AF_INET;
    ser.sin_port = htons(iPort);
    ser.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sListen, (LPSOCKADDR)&ser, sizeof(ser)) == SOCKET_ERROR) {
        std::cout << "bind() Failed\n";
        return;
    }

    //监听
    if (listen(sListen, 5) == SOCKET_ERROR) {
        std::cout << "listen() Failed\n";
        return;
    }

    iLen = sizeof(cli);//获取客户端网络地址的长度

    //接受连接和发送欢迎信息
    //用循环使程序一直运行
    while (true) {
        //接收连接
        sAccept = accept(sListen, (struct sockaddr*)&cli, &iLen);
        if (sAccept == INVALID_SOCKET) {
            std::cout << "accept() Failed\n";
            break;
        }

        //创建新的线程,并加入容器中,并将线程后台运行
        //t = new std::thread(server, sAccept);
        //tManage.push_back(t);
        //t->detach();
        server = new Server(sAccept);
        t = new std::thread(&Server::running, server);
        tManage.push_back(t);
        t->detach();
    }

    //释放指针占用的内存
    for (int i = 0; i < tManage.size(); i++) {
        delete(tManage[i]);
    }

    //关闭监听
    closesocket(sListen);
    //关闭 Winsock
    WSACleanup();
}

//多线程函数,创建的多线程运行此函数
void server(SOCKET s) {
    SOCKET socket = s;
    struct sockaddr_in ser, cli;//网络地址
    int iSend, iRecv;
    char buf[1024] = "I am a server";

    //显示客户端的 IP 信息
    char clibuf[20] = { '\0' };
    inet_ntop(AF_INET, (void*)&cli.sin_addr, clibuf, 16);
    std::cout << "Accept client IP:" << clibuf << ":" << ntohs(cli.sin_port) << std::endl;

    //发送信息给客户端
    iSend = send(socket, buf, sizeof(buf), 0);
    if (iSend == SOCKET_ERROR) {
        std::cout << "send() Failed\n";
    }
    else if (iSend == 0) {
        std::cout << "send() Zero\n";
    }
    else {
        std::cout << "Send byte:" << iSend << std::endl;
        std::cout << "----------------------------------\n";
    }

    //使用循环不断接受客户端发送来的信息并显示
    while (true) {
        iRecv = recv(socket, buf, sizeof(buf), 0);
        if (iRecv == 0) {
            //std::cout << "recv() Zero\n";
        }
        else if (iRecv == SOCKET_ERROR) {
            std::cout << "recv() Failed\n";
        }
        else {
            std::cout << "recv() data from server:" << buf << std::endl;
        }
    }
}

//Server.h
#pragma once

#include <Winsock2.h>
#include <string>

class Server
{
public:
    Server(SOCKET s);
    ~Server();

    //欢迎函数
    bool welcome();
    //服务端运行函数
    void running();
    //命令解析函数
    int commandParse(char* instruck, std::string &paramter);
    //文件接收函数
    bool receiveFile(std::string filename);
    //文件发送
    bool sendFile(std::string filename);

private:
    SOCKET server;
    int maxLen;
    char buf[1024];
};

//Server.cpp
#include "Server.h"
#include "Tool.h"

#include <iostream>
#include <vector>

Server::Server(SOCKET s)
    :server(s)
{
    maxLen = 1024;
}


Server::~Server()
{
}

bool Server::welcome()
{
    char welcome[1024] = "Welcome, my friend\n";

    //发送信息给客户端
    int iSend = send(server, welcome, sizeof(welcome), 0);
    if (iSend == SOCKET_ERROR) {
        std::cout << "send() Failed\n";
        return false;
    }

    return true;
}

void Server::running()
{
    welcome();

    while (true) {
        if (recv(server, buf, maxLen, 0) == 0) {
            std::cout << "recv() Faied!\n";
        }

        std::string paramter;
        int cmd = commandParse(buf, paramter);

        switch (cmd) {
        case 0:
            std::cout << "Command is invalid\n";
            break;

        case 1:
            std::cout << "Start receive the file from client.\n";
            receiveFile(paramter);
            break;

        case 2:
            std::cout << "Start send file to client.\n";
            sendFile(paramter);
            break;

        default:
            std::cout << "Command is invalid\n";
        }
    }
}

int Server::commandParse(char* instruck, std::string &paramter)
{
    std::string string(buf);
    std::cout << string;
    std::vector<std::string> command;
    Tool::splitString(string, command, std::string(" "));
    //std::cout << command[0] << " " << command[1] << std::endl;

    if (command.size() != 2) {
        paramter = "";
        return -1;
    }

    if (command[0] == "STOR") {
        paramter = command[1];
        return 1;
    }
    if (command[0] == "RETR") {
        paramter = command[1];
        return 2;
    }

    return -1;
}

bool Server::receiveFile(std::string filename)
{
    return false;
}

bool Server::sendFile(std::string filename)
{
    return false;
}

工具类(字符分割方法,服务端与客户端完全一样)

//Tool.h
#pragma once
#include <string>
#include <vector>

class Tool
{
public:
    Tool();
    ~Tool();

    //字符串分割函数
    static void splitString(const std::string &input, std::vector<std::string> &output, std::string &delim);
};

//Tool.cpp
#include "Tool.h"

#include <sstream>
#include <istream>


Tool::Tool()
{
}


Tool::~Tool()
{
}

void Tool::splitString(const std::string & input, std::vector<std::string>& output, std::string &delim)
{
    std::string::size_type pos1, pos2;
    pos1 = 0;
    pos2 = input.find(delim);

    while (std::string::npos != pos2)
    {
        output.push_back(input.substr(pos1, pos2 - pos1));

        pos1 = pos2 + delim.size();
        pos2 = input.find(delim, pos1);
    }
    if (pos1 != input.length())
        output.push_back(input.substr(pos1));
}

源代码地址

https://github.com/lw1243925457/FTP

展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部
返回顶部
顶部