基于Internet的Linux客户机/服务器系统通讯设计与实现

这是我们这学期操作系统的课程设计,上周末的时候把文档和代码都交给老师了,在这里也贴一下吧,我的文档的部分内容和代码,还有老师的要求

设计要求

功能要求

利用Linux套接字进程通信原理设计和实现基于Internet的客户机-服务器应用系统,系统结构如图1所示。系统包括客户机程序和服务器程序两个部分,客户机程序可以通过IP地址和端口号登录到服务器程序,向服务器提交一些Linux命令。服务器执行命令,并将输出结果返回到客户机程序。

基于Internet的Linux客户机/服务器系统通讯设计与实现_第1张图片

性能要求

在程序中加上响应时间的统计,观察每个客户的响应时间是否受客户机数量的影响。

文档内容及格式要求

文档内容分层次组织,包含设计方案、程序结构说明、重要算法、源程序、反映功能及性能要求的运行证据(运行结果)等值得说明的项目的图文说明。内容编排顺序上遵循从抽象到具体的结构顺序,体现人类解决复杂问题时由浅入深逐步细化的认识原则。语言表达清晰无误、简洁明快、富有逻辑。
文档格式规范,字体大小合适,以5号字为主,标题文字可稍大到4号,字体风格要一致。行间距适宜,不要过大。段落间的间距要适宜,随意在行间或段落间出现空行是不规范的,影响文档外观的整洁性。封面格式要统一。

设计方案

整体设计方案

上图是我做课程设计的整体设计的一个方案。客户机向服务器发送一条指令,服务器每接受到一个服务器发来的命令时,则创建一个线程,并通过system() 函数执行。通过创建管道或者运用重定向来存放命令执行的结果,之后服务器再通过从文件或者管道中取出执行的结果,发送给客户机,客户机把执行的结果显示在屏幕上。

在客户机发送命令时,记录下一个时间,当客户机接收到服务器返回的命令结果时,再记录下一个时间,两个时间之差就是响应时间。

其中响应时间我分别记录了服务器端的和客户机端的。服务器端的响应时间是从接收到客户机发来的命令到服务器执行完命令的时间,不包括向客户机返回结果的时间差;客户机端的响应时间是从客户机向服务器发送命令的时间到客户机收到服务器返回的命令结果的时间差。

在服务器端,我分别采用管道和重定向两种方法来完成课程设计。

发送命令与接收结果

通过write()函数来发送命令或者结果。
通过read()函数来接受命令或者结果。

服务器执行命令

服务器每接收到一条客户机发来的指令,则创建一个线程来执行。通过system()函数来执行客户机发来的命令。通过重定向或管道技术把命令的执行结果存入其中。

重定向

通过strcat()函数在给要执行的命令串后面加上” > resultfile ”,通过system()函数执行命令的同时,把结果放入resultfile文件当中,当服务器给客户机返回执行结果的时候从结果文件中取结果发给客户机。

管道

使用popen()函数,这个函数是对管道应用的一个封装。思路是,当服务器接收到客户机发来的一条命令时,用system()来执行(popen()函数已经把system()函数的执行过程封装了起来,在具体写的时候直接调用popen()就可以,不需要再手动去写system()),同时创建一个管道,把执行的结果放到管道里面,当把结果发给客户端的时候,再从管道中取结果发给客户机。

响应时间的计算

使用gettimeofday()函数来获得当前时间,其可精确到微秒。

客户机端在给服务器发送命令之后记录下一个时间,在客户机收到服务器发来的命令结果之后,再记录下一个时间,两个时间之差就是客户机端的响应时间,单位为毫秒。
服务器端当接收到一台客户机发来的命令后记录下一个时间,当客户机执行完这个命令后再记录下一个时间,两个时间之差就是服务器执行命令的响应时间,单位为毫秒。

主要函数说明

以下是自己写的部分用到的函数,老师之前给的代码中与socket有关的函数不再添加。

read()函数

  1. 头文件:#include <unistd.h>
    函数定义:ssize_t read (int fd, void *buf, size_t count);
  2. 返回值:
    成功返回读取的字节数,出错返回-1并设置errno,如果在调read()之前已到达文件末尾,则这次read()返回0。
  3. 函数说明:
    read()会把参数fd所指的文件传送nbyte个字节到buf指针所指的内存中。若参数nbyte为0,则read()不会有作用并返回0。返回值为实际读取到的字节数,如果返回0,表示已到达文件尾或无可读取的数据。错误返回-1,并将根据不同的错误原因适当的设置错误码。

write()函数

  1. 头文件:#include <stdio.h>
  2. 函数定义:int write(int handle, void *buf, int nbyte);
  3. 函数说明:write()会把参数buf 所指的内存写入count 个字节到参数fd 所指的文件内。当然,文件读写位置也会随之移动。
  4. 返回值:如果顺利write()会返回实际写入的字节数。当有错误发生时,则返回-1,错误代码存入errno 中。

popen()函数

  1. 头文件:#include <stdio.h>
  2. 函数定义:
    FILE * popen ( const char * command , const char * type );
    int pclose ( FILE * stream );
  3. 函数说明:
    popen()函数通过创建一个管道,调用fork产生一个子进程,执行一个shell以运行命令来开启一个进程。这个进程必须由pclose()函数关闭,而不是fclose()函数。pclose()函数关闭标准I/O流,等待命令执行结束,然后返回shell的终止状态。如果shell不能被执行,则pclose()返回的终止状态与shell已执行exit一样。
    type参数只能是读或者写中的一种,得到的返回值(标准 I/O 流)也具有和type相应的只读或只写类型。如果type是 “r” 则文件指针连接到command的标准输出;如果type是”w”则文件指针连接到command的标准输入。
    command参数是一个指向以NULL结束的shell命令字符串的指针。这行命令将被传到bin/sh并使用-c 标志,shell将执行这个命令。
    popen的返回值是个标准 I/O 流,必须由pclose来终止。前面提到这个流是单向的。所以向这个流写内容相当于写入该命令的标准输入;命令的标准输出和调用 popen的进程相同。与之相反的,从流中读数据相当于读取命令的标准输出;命令的标准输入和调用popen的进程相同。

gettimeofday()函数

  1. 头文件:#include<sys/time.h>
  2. 函数定义:int gettimeofday(struct timeval*tv,struct timezone *tz )
  3. 函数说明:
    在C语言中可以使用函数gettimeofday()函数来得到时间,它的精度可以达到微秒(μs)。函数执行成功后返回0,失败后返回-1,错误代码存于errno中。

代码

以下分别用了两种方法做课程设计,其中,客户端代码都是相同的,服务器端一种方法是使用管道来存放和读取命令的执行结果,另外一种是使用重定向方法,用文件来存储和读取命令的执行结果。

管道

服务器端

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LEN 1025

int main()
{   
    int server_sockfd = -1; 
    int client_sockfd = -1; 
    int client_len = 0; 
    struct sockaddr_in server_addr; 
    struct sockaddr_in client_addr; //创建流套接字 
    char buf[LEN];
    char result[LEN];
    struct timeval t_start, t_end;

    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);   
    //设置服务器接收的连接地址和监听的端口 
    server_addr.sin_family = AF_INET;//指定网络套接字

    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//接受所有IP地址的连接 
    server_addr.sin_port = htons(9736);//绑定到9736端口 
    //绑定(命名)套接字 
    bind(server_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));   
    //创建套接字队列,监听套接字 
    listen(server_sockfd, 5);   
    //忽略子进程停止或退出信号 
    signal(SIGCHLD, SIG_IGN);

    while(1){   
        client_len = sizeof(client_addr);       
        printf("Server waiting\n"); //接受连接,创建新的套接字 
        client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_addr, &client_len);

        if(fork() == 0){    
            printf("客户机%d连接服务器\n", client_sockfd);      
            //子进程中,读取客户端发过来的信息,处理信息,再发送给客户端
            while(1){   
                FILE *fp;

                memset(buf, '\0', sizeof(buf));
                read(client_sockfd, &buf, sizeof(buf)); 

               gettimeofday( &t_start, NULL );
               //计算服务器收到客户机发来命令的时间
                long start = ((long)t_start.tv_sec)*1000+(long)t_start.tv_usec/1000;
                //printf("Start time: %ld ms\n", start);


                if(0 == strcmp(buf, "quit")){
                    printf("客户机%d退出服务器\n", client_sockfd);
                    break;
                }       

                printf("收到了命令:%s\n", buf);  

                fp = popen(buf, "r");
                if(fp == NULL){
                    printf("Failed to run command\n" );
                    exit(1);
                }

               gettimeofday( &t_end, NULL );
               //计算服务器执行完命令之后的时间
                long end = ((long)t_end.tv_sec)*1000+(long)t_end.tv_usec/1000;
                //printf("End time: %ld ms\n", end);

                long cost_time = end - start;
                printf("Cost time: %ld ms\n\n", cost_time);


                while (fgets(result, sizeof(result)-1, fp) != NULL){
                    write(client_sockfd, &result, sizeof(result));
                }
                write(client_sockfd, "end", 4);

                pclose(fp); 

            }           
            close(client_sockfd);           
            exit(0);        
        }       
        else{           
            //父进程中,关闭套接字 
            close(client_sockfd);       
        }   
    }
}

客户机端

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <arpa/inet.h>
#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>

#define LEN 1025

int main()
{
    int sockfd = -1;
    int len = 0;
    struct sockaddr_in address;
    int result;
    char buf[LEN];
    struct timeval t_start, t_end;

    //创建流套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);     //设置要连接的服务器的信息
    address.sin_family = AF_INET;     //使用网络套接字
    address.sin_addr.s_addr = inet_addr("127.0.0.1");    //服务器地址
    address.sin_port = htons(9736);      //服务器所监听的端口
    len = sizeof(address);
    result = connect(sockfd, (struct sockaddr*)&address, len);    //连接到服务器

    if(result == -1)
    {
        perror("ops:client\n");
        exit(1);
    }

    printf("客戶端已连接...\n");

    while(1){
        memset(buf, '\0', sizeof(buf));
        fgets(buf,sizeof(buf), stdin);
        //gets(buf);

        int len = strlen(buf);
        buf[len-1] = '\0';

        write(sockfd, &buf, sizeof(buf));
        //time(&start);

        gettimeofday( &t_start, NULL );
        //计算向客户端发送命令的时间
        long start = ((long)t_start.tv_sec)*1000+(long)t_start.tv_usec/1000;
        //printf("Start time: %ld ms\n", start);

        if(0 == strcmp("quit", buf)){
            break;
        }

        while((read(sockfd, &buf, sizeof(buf))) > 0){
            if(strcmp(buf, "end")==0)
                break;
            printf("%s", buf);
        }

        //sleep(2);
        //usleep(5000);//5毫秒

        gettimeofday( &t_end, NULL );
        //计算收到结果后的时间
        long end = ((long)t_end.tv_sec)*1000+(long)t_end.tv_usec/1000;
        //printf("End time: %ld ms\n", end);

        long cost_time = end - start;
        printf("Cost time: %ld ms\n\n", cost_time);

        //printf("get the result !\n\n");
    }

    close(sockfd);
    return 0;
}

重定向

服务器端

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

#define LEN 1025

int main()
{   
    int server_sockfd = -1; 
    int client_sockfd = -1; 
    int client_len = 0; 
    struct sockaddr_in server_addr; 
    struct sockaddr_in client_addr; //创建流套接字 
    char buf[LEN];
    char result[LEN];
    struct timeval t_start, t_end;

    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);   
        //设置服务器接收的连接地址和监听的端口 
    server_addr.sin_family = AF_INET;//指定网络套接字

    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//接受所有IP地址的连接 
    server_addr.sin_port = htons(9736);//绑定到9736端口 
    //绑定(命名)套接字 
    bind(server_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));   
    //创建套接字队列,监听套接字 
    listen(server_sockfd, 5);   
    //忽略子进程停止或退出信号 
    signal(SIGCHLD, SIG_IGN);

    while(1){   
        client_len = sizeof(client_addr);       
        printf("Server waiting\n"); //接受连接,创建新的套接字 
        client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_addr, &client_len);

        if(fork() == 0){    
            printf("客户机%d连接服务器\n", client_sockfd);      
            //子进程中,读取客户端发过来的信息,处理信息,再发送给客户端
            while(1){   
                int fd = 0, reclen = 0;

                memset(buf, '\0', sizeof(buf));
                memset(result, '\0', sizeof(result));
                read(client_sockfd, &buf, sizeof(buf)); 

               gettimeofday( &t_start, NULL );
               //计算服务器收到客户机发来命令的时间
                long start = ((long)t_start.tv_sec)*1000+(long)t_start.tv_usec/1000;
                //printf("Start time: %ld ms\n", start);

                if(0 == strcmp(buf, "quit")){
                    printf("客户机%d退出服务器\n", client_sockfd);
                    break;
                }       

                printf("收到了命令:%s\n", buf);  

                strcat(buf, " > resultfile");
                system(buf);

               gettimeofday( &t_end, NULL );
               //计算服务器执行完命令之后的时间
                long end = ((long)t_end.tv_sec)*1000+(long)t_end.tv_usec/1000;
                //printf("End time: %ld ms\n", end);

                long cost_time = end - start;
                printf("Cost time: %ld ms\n\n", cost_time);


                if((fd = open("./resultfile", O_RDONLY)) < 0){
                    perror("文件打开失败");
                    exit(-1);
                }

                while ((reclen = read(fd, &result, LEN)) > 0){
                    result[reclen] = '\0';
                    write(client_sockfd, &result, sizeof(result));
                }
                write(client_sockfd, "end", 4);

                close(fd);  

            }           
            close(client_sockfd);           
            exit(0);        
        }       
        else{           
            //父进程中,关闭套接字 
            close(client_sockfd);       
        }   
    }
}

客户机端

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <arpa/inet.h>
#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>

#define LEN 1025

int main()
{
    int sockfd = -1;
    int len = 0;
    struct sockaddr_in address;
    int result;
    char buf[LEN];
    struct timeval t_start, t_end;

    //创建流套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);     //设置要连接的服务器的信息
    address.sin_family = AF_INET;     //使用网络套接字
    address.sin_addr.s_addr = inet_addr("127.0.0.1");    //服务器地址
    address.sin_port = htons(9736);      //服务器所监听的端口
    len = sizeof(address);
    result = connect(sockfd, (struct sockaddr*)&address, len);    //连接到服务器

    if(result == -1)
    {
        perror("ops:client\n");
        exit(1);
    }

    printf("客戶端已连接...\n");

    while(1){
        memset(buf, '\0', sizeof(buf));
        fgets(buf,sizeof(buf), stdin);
        //gets(buf);

        int len = strlen(buf);
        buf[len-1] = '\0';

        write(sockfd, &buf, sizeof(buf));
        //time(&start);

        gettimeofday( &t_start, NULL );
        //计算向客户端发送命令的时间
        long start = ((long)t_start.tv_sec)*1000+(long)t_start.tv_usec/1000;
        //printf("Start time: %ld ms\n", start);

        if(0 == strcmp("quit", buf)){
            break;
        }

        while((read(sockfd, &buf, sizeof(buf))) > 0){
            if(strcmp(buf, "end")==0)
                break;
            printf("%s", buf);
        }

        //sleep(2);
        //usleep(5000);//5毫秒

        gettimeofday( &t_end, NULL );
        //计算收到结果后的时间
        long end = ((long)t_end.tv_sec)*1000+(long)t_end.tv_usec/1000;
        //printf("End time: %ld ms\n", end);

        long cost_time = end - start;
        printf("Cost time: %ld ms\n\n", cost_time);

        //printf("get the result !\n\n");
    }

    close(sockfd);
    return 0;
}

你可能感兴趣的:(linux,通信,服务器,操作系统,客户机)