C++_文件传输2_客户端与服务器端编程

#define UPFILE 1
#define DOWNFILE 2
#define COMMAND 3

typedef struct Command
{
    long type;
    char text[128];
}S_Command;

class FileTrans
{
public:
    void SendFile(char *filename, int sockfd, int flag);
    void RecvFile(char *filename, int sockfd, int flag);
};

服务器:

class ServerSocket : public FileTrans 
{
public:
    ServerSocket(char *ip="127.0.0.1", short port=6000);
    ~ServerSocket();
    void Start();

private:
    void GetClientLink();
    void DealClientData(int sockfd);
    void DeleteClentLink(int sockfd);
    void DealCommand(int sockfd, char *cmd);

private:
    int listenfd;
    int epfd;
};

const int maxevents = 100;
//构造函数,创建用于监听的socket&epoll的内核事件表
ServerSocket::ServerSocket(char *ip, short port)
{
    signal(SIGCHLD, SIG_IGN);
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    assert(listenfd != -1);

    struct sockaddr_in ser
    memset(&ser, 0, sizeof(ser));
    ser.sin_family = AF_INET;
    ser.sin_port = htons(port);
    ser.sin_addr.s_addr = inet_addr(ip);

    int res = bind(listenfd, (struct sockaddr*)&ser, sizeof(ser));
    assert(-1 != res);

    listen(listenfd, 5);

    epfd = epoll_create(5);

    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = listenfd;

    // 将监听socket添加到内核事件表中
    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &event);
}

ServerSocket::~ServerSocket()
{
    close(listenfd);
    close(epfd);
}

void ServerSocket::Start()
{
    while(1)
    {
        struct epoll_event events[maxevents];
        int n = epoll_wait(epfd, events, maxevents, -1);
        if(n == -1 || n == 0)
        {
            cout<<"Server Start Fail"<<endl;
            break;
        }

        for(int i = 0; i < n; ++i)
        {
            int fd = events[i].data.fd;
            if(fd == listenfd)
            {
                GetClientLink();         // 获取一个客户端连接
                continue;
            }
            if(events[i].events & EPOLLRDHUP)
            {
                DeleteClentLink(fd); // 关闭一个客户端连接
                continue;
            }
            if(events[i].events & EPOLLIN)
            {
                DealClientData(fd);     // 处理客户端发送的数据
            }
        }
    }
}

void ServerSocket::GetClientLink()
{
    struct  sockaddr_in cli;
    socklen_t len = sizeof(cli);
    int c = accept(listenfd, (struct sockaddr*)&cli, &len);
    if(c == -1)
    {
        cout<<"One Client Link Fail"<<endl;
        return;
    }
    struct epoll_event event;
    event.events = EPOLLIN | EPOLLRDHUP;
    event.data.fd = c;

    epoll_ctl(epfd, EPOLL_CTL_ADD, c, &event);
}

void ServerSocket::DeleteClentLink(int sockfd)
{
    close(sockfd);
    epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
}

void ServerSocket::DealClientData(int sockfd)
{
    S_Command data;
    memset(&data, 0, sizeof(data));

    int n = recv(sockfd, &data, sizeof(data), 0);
    if(n <= 0)
    {
        DeleteClentLink(sockfd);
        return;
    }
    char *p = NULL;
    switch(data.type)
    {
        case UPFILE:  // 服务器处理客户端上传文件请求
            p = strtok(data.text, " ");
            p = strtok(NULL, " ");
            send(sockfd, "OK", 2, 0);
            RecvFile(p, sockfd, 0);
            break;
        case DOWNFILE: // 服务器处理客户端下载文件请求
            p = strtok(data.text, " ");
            p = strtok(NULL, " ");
            SendFile(p, sockfd, 0);
            break;
        case COMMAND:  //  服务器处理客户端执行命令请求
            DealCommand(sockfd, data.text);
            break;
        default:
            break;
    }
}

void ServerSocket::DealCommand(int sockfd, char *cmd)
{
    char *Argv[128] = {0};
    int count = 0;
    char *p = strtok(cmd, " ");
    while(p != NULL)
    {
        Argv[count++] = p;
        p = strtok(NULL, " ");
    }

    if(strncmp(cmd, "cd", 2) == 0)
    {
        chdir(Argv[1]);          //cd xxxx路径    chdir修改工作路径 cd为Argv[0] ,xxxx路径为Argv[1]
        send(sockfd, "Command Success\n", strlen("Command Success\n"), 0);
        return;
    }

    int fds[2];
    pipe(fds);             //管道    一端fds[0]  一端fds[1]
    
    pid_t pid = fork();  
    assert(-1 != pid);   

    if(0 == pid)          //子进程执行
    {
        close(fds[0]); //关闭读操作
        close(1);        //关闭标准输出
        close(2);        //关闭标准错误

dup返回新的文件描述符,和原文件描述符指向相同的文件、管道或网络连接,返回文件描述符是当前可用文件描述符中最小数值,关闭1:标准输出,dup返回当前最小的可用文件描述符,1,这样可以使服务器输出到标准输出里的内容直接发送到与客户端连接的文件描述符上,因此,printf调用的输出将被客户端获得,而不是显示在服务器上

        dup(fds[1]); 
        dup(fds[1]); 

        char path[128] = "/bin/";    //设定路径,调用系统的命令操作
        strcat(path, Argv[0]);         //链接字符串,/bin/ls
        execv(path, Argv);            //进程替换

        write(fds[1], "Comand Not Fount\n", strlen("Comand Not Fount\n"));
        exit(0);
    }
    else                //父进程执行
    {
        close(fds[1]);//关闭写操作
        while(1)
        {
            char buff[1024] = {0};
            int n = read(fds[0], buff, 1023);
            if(n <= 0)
            {
                strcpy(buff, "Command Success\n");
                send(sockfd, buff, strlen(buff), 0);
                break;
            }

            send(sockfd, buff, strlen(buff), 0);
        }
    }
}

客户端:

class ClientSocket : public FileTrans
{
public:
    ClientSocket(char *ip = "127.0.0.1", short port = 6000);
    ~ClientSocket();
    void Start();
private:
    void PrintTipsInfo();
private:
    int sockfd;
};


ClientSocket::ClientSocket(char *ip, short port)
{
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    assert(-1 != sockfd);

    struct sockaddr_in ser;
    memset(&ser, 0, sizeof(ser));
    ser.sin_family = AF_INET;
    ser.sin_port = htons(port);
    ser.sin_addr.s_addr = inet_addr(ip);

    int res = connect(sockfd, (struct sockaddr*)&ser, sizeof(ser));
    assert(-1 != res);
}
ClientSocket::~ClientSocket()
{
    close(sockfd);
}
void ClientSocket::Start()
{
    while(1)
    {
        PrintTipsInfo();
        char command[128] = {0};
        fgets(command, 128, stdin);
        command[strlen(command) - 1] = 0;

        if(strlen(command) == 0)
        {
            continue;
        }

        if(strncmp(command, "exit", 4) == 0)
        {
            exit(0);
        }

        S_Command data; 
        memset(&data, 0, sizeof(data));
        strcpy(data.text, command);
        if(strncmp(command, "up", 2) == 0)
        {
            data.type = UPFILE;
            send(sockfd, &data, strlen(data.text) + sizeof(data.type), 0);
            char buff[128] = {0};
            recv(sockfd, buff, 127, 0);  
            char *p = strtok(data.text, " ");
            p = strtok(NULL, " ");
            SendFile(p, sockfd, 1);
        }
        else if(strncmp(command, "down", 4) == 0)
        {
            data.type = DOWNFILE;
            send(sockfd, &data, strlen(data.text) + sizeof(data.type), 0);
            char *p = strtok(data.text, " ");
            p = strtok(NULL, " ");
            RecvFile(p, sockfd, 1);
        }
        else
        {
            data.type = COMMAND;
            /*if(strncmp(data.text, "ls", 2) == 0)
            {
                strcat(data.text, " -l");
            }*/

            send(sockfd, &data, strlen(data.text) + sizeof(data.type), 0);

            while(1)
            {
                char buff[1024] = {0};
                int n = recv(sockfd, buff, 1023, 0);
                if(n <= 0)
                {
                    exit(0);
                }
                cout<<buff;

                if(strstr(buff, "Command Success") != NULL)
                {
                    break;
                }
            }
        }
    }
}

void ClientSocket::PrintTipsInfo()
{
    cout<<"**********************File  Trans *********************"<<endl;
    cout<<"********* ls  显示服务器上的文件列表命令 *********"<<endl;
    cout<<**"****** cd  切换服务器上的当前工作目录 *********"<<endl;
    cout<<"**** pwd 显示服务器当前工作目录的绝对路径*****"<<endl;
    cout<<"***************** down 下载一个文件 ****************"<<endl;
    cout<<"****************** up 上传一个文件 ******************"<<endl;
    cout<<"InPut: ";
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值