• 网络编程:CMD命令


    要求:

    写一个客户端程序和服务器程序,客户端程序连接上服务器之后,通过敲命令和服务器进行交互,支持的交互命令包括:

    • pwd:显示服务器应用程序启动时的当前路径。
    • cd:改变服务器应用程序的当前路径。
    • ls:显示服务器应用程序当前路径下的文件列表。
    • quit:客户端进程退出,但是服务器端不能退出,第二个客户可以再次连接上服务器端。

    客户端向服务端发送交互命令,服务端将查询的结果返回给客户端

    开干

    客户端:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <signal.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    #define MAXLINE  1024
    
    int tcp_client(char *address, int port)
    {
        int socket_fd;
        socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    
        struct sockaddr_in server_addr;
        bzero(&server_addr, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(port);
        inet_pton(AF_INET, address, &server_addr.sin_addr);
    
        socklen_t server_len = sizeof(server_addr);
        int connect_rt = connect(socket_fd, (struct sockaddr *)&server_addr, server_len);
        if(connect_rt < 0)
        {
            perror("connect failed");
            return -1;
        }
    
        return socket_fd;
    }
    
    int main(int argc, char *argv[])
    {
        if(argc != 3)
        {
            perror("usage: cmd_client <IPaddress> <port>");
            return -1;
        }
    
        int port = atoi(argv[2]);
        printf("argv1:%s\n",argv[1]);
        int socket_fd = tcp_client(argv[1], port);
    
        char recv_line[MAXLINE],send_line[MAXLINE];
        int n;
    
        fd_set readmask;
        fd_set allreads;
        FD_ZERO(&allreads);
        FD_SET(0, &allreads);
        FD_SET(socket_fd, &allreads);
    
        for(;;)
        {
            readmask = allreads;
            int rc = select(socket_fd+1, &readmask, NULL, NULL, NULL);
            if(rc <= 0)
            {
                perror("select failed");
                return -1;
            }
    
            if(FD_ISSET(socket_fd, &readmask))
            {
                n = read(socket_fd, recv_line, MAXLINE);
                if(n < 0)
                {
                    perror("read error");
                    return -1;
                }
                else if(n == 0)
                {
                    printf("server closed");
                    break;
                }
    
                recv_line[n] = 0;
                fputs(recv_line, stdout);
                fputs("\n",stdout);
            }
    
            if(FD_ISSET(STDIN_FILENO, &readmask))
            {
                if(fgets(send_line, MAXLINE, stdin) != NULL)
                {
                    int i = strlen(send_line);
                    if(send_line[i - 1] == '\n')
                    {
                        send_line[i - 1] = 0;
                    }
    
                    if(strncmp(send_line, "quit", strlen(send_line)) == 0)
                    {
                        if(shutdown(socket_fd, 1))
                        {
                            perror("shutdown failed");
                            return  -1;
                        }
                    }
    
                    size_t rt = write(socket_fd, send_line, strlen(send_line));
                    if(rt < 0)
                    {
                        perror("write failed");
                        return -1;
                    }
    
                }
            }
    
        }
    
        exit(0);
    }
    

    客户端的代码主要考虑的是使用 select 同时处理标准输入和套接字。select 如果发现标准输入有事件,读出标准输入的字符,就会通过调用 write 方法发送出去。如果发现输入的是 quit,则调用 shutdown 方法关闭连接的一端。

    如果 select 发现套接字流有可读事件,则从套接字中读出数据,并把数据打印到标准输出上;如果读到了 EOF,表示该客户端需要退出,直接退出循环,通过调用 exit 来完成进程的退出。
    服务端程序:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <signal.h>
    #include <errno.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    #define SERV_PORT  43211
    #define LISTENQ 1024
    
    static int count;
    
    static void sig_int(int signo) {
        printf("\nreceived %d datagrams\n", count);
        exit(0);
    }
    
    
    char *run_cmd(char *cmd)
    {
        char *data = malloc(16384);
        bzero(data, sizeof(data));
        FILE *fdp;
        const int max_buffer = 256;
        char buffer[max_buffer];
        fdp = popen(cmd, "r");
        char *data_index = data;
    
        if (fdp)
        {
            while(!feof(fdp))
            {
                if(fgets(buffer, max_buffer, fdp) != NULL)
                {
                    int len = strlen(buffer);
                    memcpy(data_index, buffer, len);
                    data_index += len;
                }
            }
            pclose(fdp);
        }
        return data;
    
    }
    
    int main(int argc, char *argv[])
    {
        int listenfd;
        listenfd = socket(AF_INET, SOCK_STREAM, 0);
    
        struct sockaddr_in server_addr;
        bzero(&server_addr, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        server_addr.sin_port = htons(SERV_PORT);
    
        int on = 1;
        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    
        int rt1 = bind(listenfd, (struct sockaddr *) &server_addr, sizeof(server_addr));
        if(rt1 < 0)
        {
            perror("bind failed");
            return -1;
        }
    
        int rt2 = listen(listenfd, LISTENQ);
        if(rt2 < 0)
        {
            perror("listen failed");
            return -1;
        }
    
        signal(SIGPIPE, SIG_IGN);
    
        int connfd;
        struct sockaddr_in client_addr;
        socklen_t client_len = sizeof(client_addr);
    
        char buf[256];
        count = 0;
        while(1)
        {
            if((connfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_len)) < 0)
            {
                perror("accept failed");
                return -1;
            }
    
            while(1)
            {
                bzero(buf, sizeof(buf));
                int n = read(connfd, buf, sizeof(buf));
                if(n < 0)
                {
                    perror("error read message");
                    continue;
                }
                else if(n == 0)
                {
                    printf("client closed ");
                    close(connfd);
                    break;
                }
    
                count++;
                buf[n] = 0;
                if(strncmp(buf, "ls", n) == 0)
                {
                    char *result = run_cmd("ls");
                    if(send(connfd, result, strlen(result), 0) < 0)
                    {
                        return 1;
                    }
                    free(result);
                }
                else if(strncmp(buf, "pwd", n) == 0)
                {
                    char buf[256];
                    char *result = getcwd(buf, 256);
                    if(send(connfd, result, strlen(result), 0) < 0)
                    {
                        return 1;
                    }
                }
                else if(strncmp(buf, "cd ", 3) == 0)
                {
                    char target[256];
                    bzero(target, sizeof(target));
                    memcpy(target, buf+3, strlen(buf) - 3);
                    if(chdir(target) == -1)
                    {
                        printf("change dir failed, %s\n",target);
                    }
                }
                else
                {
                    char *error = "error: unknow input type";
                    if(send(connfd, errno, strlen(errno), 0) < 0)
                    {
                        return 1;
                    }
                }
            }
    
        }
        exit(0);
    
    }
    

    服务器端程序需要两层循环,第一层循环控制多个客户端连接,第二层循环控制和单个连接的数据交互
    在第一层循环里通过 accept 完成了连接的建立,获得连接套接字。在第二层循环里,先通过调用 read 函数从套接字获取字节流。这里处理的方式是反复使用了 buf 缓冲,每次使用之前记得都要调用 bzero 完成初始化,以便重复利用。如果读取数据为 0,则说明客户端尝试关闭连接,这种情况下,需要跳出第二层循环,进入 accept 阻塞调用,等待新的客户连接到来

    在读出客户端的命令之后,就进入处理环节。通过字符串比较命令,进入不同的处理分支。C 语言的 strcmp 或者 strncmp 可以帮助我们进行字符串比较,
    如果命令的格式有错,需要把错误信息通过套接字传给客户端。
    对于“pwd”命令,通过调用 getcwd 来完成的,getcwd 是一个 C 语言的 API,可以获得当前的路径。
    对于“cd”命令,通过调用 chdir 来完成的,cd 是一个 C 语言的 API,可以将当前目录切换到指定的路径。
    对于“ls”命令,通过了 popen 的方法,执行了 ls 的 bash 命令,把 bash 命令的结果通过文件字节流的方式读出,再将该字节流通过套接字传给客户端。

    运行结果

  • 相关阅读:
    利用深度学习网络自动给图像上色的文章和相关工程实现
    Perceptual Losses for Real-Time Style Transfer and Super-Resolution and Super-Resolution 论文笔记
    (转) Dissecting Reinforcement Learning-Part.2
    (转) 多模态机器翻译
    编译caffe的Python借口,提示:ImportError: dynamic module does not define module export function (PyInit__caffe)
    (转)How Hash Algorithms Work
    深度学习 目标检测算法 SSD 论文简介
    (转)能根据文字生成图片的 GAN,深度学习领域的又一新星
    (转) Face-Resources
    (转) AdversarialNetsPapers
  • 原文地址:https://www.cnblogs.com/whiteBear/p/16029333.html
Copyright © 2020-2023  润新知