码迷,mamicode.com
首页 > 其他好文 > 详细

2017-2018-1 20155201 《信息安全系统设计基础》实验三 实时系统

时间:2017-11-19 13:33:44      阅读:195      评论:0      收藏:0      [点我收藏+]

标签:for   解锁   文件内容   访问   监听   缓冲   txt   文件中   换行   

2017-2018-1 20155201 实验三 实时系统

一、实验内容

  1. 基于Linux Socket程序设计实现wc(1)服务器和客户端
  2. 使用多线程实现wc服务器

二、实验步骤

  1. 基于Linux Socket程序设计实现wc(1)服务器和客户端
    • 学习使用Linux命令wc(1)
    • 在终端中输入man wc命令查看wc命令的解释:
    The wc utility displays the number of lines, words, and bytes contained
     in each input file, or standard input (if no file is specified) to the
     standard output. 
     命令统计指定文件中的行数、单词数和字节数,并将统计结果输出,如果没有给出文件名,将从标准输入读取。
    
     命令参数:
         - c 统计字节数
         - l 统计行数
         - m 统计字符数。(不可以与-c一起使用)
         - w 统计字数。(一个字被定义为由空白、跳格或换行字符分隔的字符串。)
    
    >wc test1.txt    //命令显示文件的行数、字数、字节数和文件名
    >wc -w test1.txt    //统计文件字数(单词数)。
    • 客户端传一个文本文件给服务器

    客户端代码实现:

    #include<netinet/in.h>  // sockaddr_in
    #include<sys/types.h>   // socket
    #include<sys/socket.h>  // socket
    #include<stdio.h>   // printf
    #include<stdlib.h>  // exit
    #include<string.h>  // bzero
    #define SERVER_PORT 8000
    #define BUFFER_SIZE 1024
    #define FILE_NAME_MAX_SIZE 512
    int main()
    {
    // 声明并初始化一个客户端的socket地址结构
    struct sockaddr_in client_addr;
    bzero(&client_addr, sizeof(client_addr));
    client_addr.sin_family = AF_INET;
    client_addr.sin_addr.s_addr = htons(INADDR_ANY);
    client_addr.sin_port = htons(0);
    
    // 创建socket,若成功,返回socket描述符
    int client_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(client_fd < 0)
    {
        perror("Create Socket Failed:");
        exit(1);
            }
    // 绑定客户端的socket和客户端的socket地址结构 非必需
    if((bind(clientfd, (struct sockaddr*)&client_addr, sizeof(client_addr)))==-1)
        {
        perror("Client Bind Failed:");
        exit(1);
    }
    // 声明一个服务器端的socket地址结构,并用服务器那边的IP地址及端口对其进行初始化,用于后面的连接
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    if(inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) == 0)
        {
            perror("Server IP Address Error:");
            exit(1);
            }
    server_addr.sin_port = htons(SERVER_PORT);
    socklen_t server_addr_length = sizeof(server_addr);
    // 向服务器发起连接,连接成功后client_socket_fd代表了客户端和服务器的一个socket连接
    if(connect(clientfd, (struct sockaddr*)&server_addr, server_addr_length) < 0)
        {
            perror("Can Not Connect To Server IP:");
            exit(0);}
    // 输入文件名 并放到缓冲区buffer中等待发送
    char file_name[FILE_NAME_MAX_SIZE+1];
    bzero(file_name, FILE_NAME_MAX_SIZE+1);
    printf("Please Input File Name On Server:\t");
    scanf("%s", file_name);
    char buffer[BUFFER_SIZE];
    bzero(buffer, BUFFER_SIZE);
    strncpy(buffer, file_name, strlen(file_name)>BUFFER_SIZE?BUFFER_SIZE:strlen(file_name));
    send(clientfd, buffer, BUFFER_SIZE, 0);
    // 向服务器发送buffer中的数据
          // 打开文件,准备写入
    FILE *fp = fopen(file_name, "r");   
    if(NULL == fp)
        {
            printf("File:\t%s Can Not Open To Write\n", file_name);
            exit(1);
            }
    else
    {
        bzero(buffer, BUFFER_SIZE);
        int file_length = 0;
        while( (file_length = fread(buffer,sizeof(char),BUFFER_SIZE, fp))>0)
        {
    
            if(send(clientfd,buffer,file_length,0)<0)
            {
                printf("Send File:\t%s Failed\n", file_name);
                break;
            }
            bzero(buffer, BUFFER_SIZE);
        }
    }
    close(fp);
    close(client_fd);
    return 0;
    }
    
    • 服务器返回文本文件中的单词数
    #include<netinet/in.h>   // sockaddr_in
    #include<sys/types.h>   // socket
    #include<sys/socket.h>   // socket
    #include<stdio.h>   // printf
    #include<stdlib.h>  // exit
    #include<string.h>  // bzero
    #define SERVER_PORT 8000
    #define LENGTH_OF_LISTEN_QUEUE 20
    #define BUFFER_SIZE 1024
    #define FILE_NAME_MAX_SIZE 512
    #define MAX 10000000
    int main()
    {
    // 声明并初始化一个服务器端的socket地址结构
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htons(INADDR_ANY);
    server_addr.sin_port = htons(SERVER_PORT);
    // 创建socket,若成功,返回socket描述符
    int server_socket_fd = socket(PF_INET, SOCK_STREAM, 0);
    if(server_socket_fd < 0)
    {
    perror("Create Socket Failed:");
    exit(1);
    }
    int opt = 1;
    setsockopt(server_socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    // 绑定socket和socket地址结构
    if((bind(server_socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)))==-1)
        {
        perror("Server Bind Failed:");
        exit(1);
        }
        // socket监听
    if(-1 == (listen(server_socket_fd, LENGTH_OF_LISTEN_QUEUE)))
    {
    perror("Server Listen Failed:");
    exit(1);
    }
    while(1)
    {
    // 定义客户端的socket地址结构
    struct sockaddr_in client_addr;
    socklen_t client_addr_length = sizeof(client_addr);
    // 接受连接请求,返回一个新的socket(描述符),这个新socket用于同连接的客户端通信
    // accept函数会把连接到的客户端信息写到client_addr中
    int new_server_socket_fd = accept(server_socket_fd, (struct sockaddr*)&client_addr, &client_addr_length);
    if(new_server_socket_fd < 0)
    {
    perror("Server Accept Failed:");
    break;
    }
    // recv函数接收数据到缓冲区buffer中
    char buffer[BUFFER_SIZE];
    bzero(buffer, BUFFER_SIZE);
    if(recv(new_server_socket_fd, buffer, BUFFER_SIZE, 0) < 0)
    {
    perror("Server Recieve Data Failed:");
    break;
    }
    // 然后从buffer(缓冲区)拷贝到file_name中
    char file_name[FILE_NAME_MAX_SIZE+1];
    bzero(file_name, FILE_NAME_MAX_SIZE+1);
    strncpy(file_name, buffer, strlen(buffer)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buffer));
    printf("filename:%s\n", file_name);
    // 打开文件并读取文件数据
    FILE *fp = fopen(file_name, "r");
    if(NULL == fp)
    {
    printf("File:%s Not Found\n", file_name);}
    else
    {
    //printf("buffer:%s\n",buffer);//buffer为filename
    char *argv[]={"wc","-w",file_name,0};
    execvp( "wc" ,argv);
    fclose(fp); 
    } 
    // 关闭与客户端的连接 
    close(new_server_socket_fd); 
    }
    // 关闭监听用的socket 
    close(server_socket_fd); 
    return 0; }
    程序截图:
  2. 使用多线程实现wc服务器并使用同步互斥机制保证计数正确
    互斥锁:互斥锁是用加锁的方式来控制对公共资源的操作(一旦开始进行就不会被打断的操作)
    在同一时刻只有一个线程能够对互斥锁进行操作;只有上锁的进程才可以对公共资源进行访问,除该进程之外,其他进程只能等到上锁进程解锁才能对公共资源进行操作。
    之前老师给的课上示例代码有互斥锁的部分:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define NLOOP 5000

int counter;

pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;  

void *doit( void * );

int main(int argc, char **argv)
{
    pthread_t tidA, tidB;

    pthread_create( &tidA ,NULL, &doit, NULL );      //创建进程
    pthread_create( &tidB ,NULL, &doit, NULL );

    pthread_join( tidA, NULL );     //一直阻塞调用线程
    pthread_join( tidB, NULL );

    return 0;
}

void * doit( void * vptr)
{
    int i, val;

    for ( i=0; i<NLOOP; i++ ) {
        pthread_mutex_lock( &counter_mutex );   //对进程上锁
        val = counter++;
        printf("%x: %d \n", (unsigned int) pthread_self(), val + 1);
        counter = val + 1;
        pthread_mutex_unlock( &counter_mutex );  //解锁
    }
    return NULL;
}

因此修改服务器代码,使之成为多线程服务器。

    #include<netinet/in.h>   // sockaddr_in
    #include<sys/types.h>   // socket
    #include<sys/socket.h>   // socket
    #include<stdio.h>   // printf
    #include<stdlib.h>  // exit
    #include<string.h>  // bzero
    #define SERVER_PORT 8000
    #define LENGTH_OF_LISTEN_QUEUE 20
    #define BUFFER_SIZE 1024
    #define FILE_NAME_MAX_SIZE 512
    #define MAX 10000000
    
    
    
    
    int main()
    {
    // 声明并初始化一个服务器端的socket地址结构
    ……
    // 绑定socket和socket地址结构
    if((bind(server_socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)))==-1)
        {
        perror("Server Bind Failed:");
        exit(1);
        }
        // socket监听
    if(-1 == (listen(server_socket_fd, LENGTH_OF_LISTEN_QUEUE)))
    {
    perror("Server Listen Failed:");
    exit(1);
    }
    while(1)//调用pthread_create不断创建新进程接受客户端连接请求
    {
    // 定义客户端的socket地址结构
    struct sockaddr_in client_addr;
    socklen_t client_addr_length = sizeof(client_addr);
    // 接受连接请求,返回一个新的socket(描述符),这个新socket用于同连接的客户端通信
    // accept函数会把连接到的客户端信息写到client_addr中
    int new_server_socket_fd = accept(server_socket_fd, (struct sockaddr*)&client_addr, &client_addr_length);
    if(new_server_socket_fd < 0)
    {
    perror("Server Accept Failed:");
    break;
    }
    
    int client_p=pthread_create(&pid, NULL, process_client,(void *) &new_server_socket);
    pthread_t pid;
        if(pthread_create(&pid, NULL, process_client,(void *) &new_server_socket) < 0){
              exit(1);
        }
    
    // recv函数接收数据到缓冲区buffer中
    char buffer[BUFFER_SIZE];
    bzero(buffer, BUFFER_SIZE);
    if(recv(new_server_socket_fd, buffer, BUFFER_SIZE, 0) < 0)
    {
    perror("Server Recieve Data Failed:");
    break;
    }
    // 然后从buffer(缓冲区)拷贝到file_name中
    /*打开文件并计数代码部分省略*/
    …………
    // 关闭与客户端的连接 
    close(new_server_socket_fd); 
    }
    // 关闭监听用的socket 
    close(server_socket_fd); 
    return 0; }

程序运行结果:
[2file]

三、实验代码调试中遇到的问题

  • 问题1:在完成服务器计算文件单词总数时,每次执行程序后显示字数总是零,后来发现只要一执行客户端程序,文件内容就被清空。
  • 问题1解决方案:在客户端打开文件的过程中,期初定义的操作方式是"w"(写),因bzero()函数清空缓冲区内存,实际上将空的内容写入了要打开的文件,所以文件每次都被清空。把打开方式写成"r"(读)后,不存在文件被清空问题,程序可以正常计数。
FILE *fp = fopen(file_name, "r");

2017-2018-1 20155201 《信息安全系统设计基础》实验三 实时系统

标签:for   解锁   文件内容   访问   监听   缓冲   txt   文件中   换行   

原文地址:http://www.cnblogs.com/zhuohua/p/7859418.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!