2019-2020-1 20175223 20175232 20175233 实验三 实时系统

实验三 并发程序

小组成员

20175223 姚明宇
20175232 司浩楠
20175233 严顺尧


实验三-并发程序-1

  • 学习使用Linux命令wc(1)
  • 基于Linux Socket程序设计实现wc(1)服务器(端口号是你学号的后6位)和客户端
  • 客户端传一个文本文件给服务器
  • 服务器返加文本文件中的单词数
  1. 学习使用Linux命令wc(1)
    1.png

2.png

Linux系统中的wc(Word Count)命令的功能为统计指定文件中的字节数、字数、行数,并将统计结果显示输出。

命令参数:
-c 统计字节数。
-l 统计行数。
-m 统计字符数。这个标志不能与 -c 标志一起使用。
-w 统计字数。一个字被定义为由空白、跳格或换行字符分隔的字符串。
-L 打印最长行的长度。

client.c


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

#define MYPORT 175233

void main(){
int clientfd;
struct sockaddr_in remote_addr;
char buffer[BUFSIZ];
memset(&remote_addr, 0 , sizeof(remote_addr));
remote_addr.sin_family=AF_INET;
remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
remote_addr.sin_port=htons(MYPORT);

  if((clientfd=socket(PF_INET,SOCK_STREAM,0))<0){  
      perror("socket");  
  }

  if(connect(clientfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr))<0){
      perror("connect");
  }

  int len;
  FILE *fp;
  char path[20];
  gets(path);
  fp=fopen(path, "r");
  char readch;
  int i=0;
  while((readch=fgetc(fp))!=EOF){
      if(i<1024){
          buffer[i]=readch;
          i++;
      }
      else{
          i=0;
          int n=send(clientfd, buffer, 1024, 0);
      }
  }
  fclose(fp);
  if(i!=0) send(clientfd, buffer, i, 0);
  long wordscount;
  recv(clientfd, &wordscount, sizeof(long), 0);
  printf("%ld\n", wordscount);
  close(clientfd);
}

server.c

/*server*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

#define MYPORT 175233

void main(){
    int serverfd, clientfd;
    struct sockaddr_in my_addr;
    struct sockaddr_in remote_addr;

    char buffer[BUFSIZ];
    memset(&my_addr, 0, sizeof(my_addr));
    my_addr.sin_family=AF_INET;
    my_addr.sin_addr.s_addr=INADDR_ANY;
    my_addr.sin_port=htons(MYPORT);

    if((serverfd=socket(PF_INET, SOCK_STREAM, 0))==-1){
        perror("socket");
    }

    if(bind(serverfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))<0){
        perror("bind");
    }
    listen(serverfd, 5);
    int addrlen=sizeof(struct sockaddr_in); 
    while(1){
        if((clientfd=accept(serverfd, (struct sockaddr *)&remote_addr, &addrlen))<0){
            perror("accept");
        }
        printf("accept client %s\n", inet_ntoa(remote_addr.sin_addr));
        int len, i;
        long wordscount=0;
        int flag=1;
        while(1){
            if((len=recv(clientfd, buffer, 1024, 0))>0){
                for(i=0; i<len; i++){
                    if(flag==0){
                        switch(buffer[i]){
                            case ' ':
                                wordscount++;
                                break;
                            case '\n':
                                wordscount++;
                                break;
                            case '\r':
                                wordscount++;
                                break;
                            default:
                                break;
                        }
                    }
                    if(buffer[i]== ' ' || buffer[i]=='\n' || buffer[i]=='\r') flag=1;
                    else flag=0;
                }
            }
            if(len<1024) break;
        }
        send(clientfd, &wordscount, sizeof(long), 0);
        close(clientfd);
    }
    close(serverfd);
}

运行结果

11.png


实验三-并发程序-2

实验步骤

  • 使用多线程实现wc服务器并使用同步互斥机制保证计数正确
  • 上方提交代码
  • 下方提交测试
  • 对比单线程版本的性能,并分析原因
    client2.c
#include<netinet/in.h>                         // for sockaddr_in  
#include<sys/types.h>                          // for socket  
#include<sys/socket.h>                         // for socket  
#include<stdio.h>                              // for printf  
#include<stdlib.h>                             // for exit  
#include<string.h>                             // for bzero  

#define HELLO_WORLD_SERVER_PORT 175233
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512

int mywc(char file_name[], int choose);

int main(int argc, char **argv) {
    char *ipaddr = "127.0.0.1";
    FILE *fp;
    if (argc != 1) {
        printf("Usage: ./%s ServerIPAddress\n", ipaddr);
        exit(1);
    }

// 设置一个socket地址结构client_addr, 代表客户机的internet地址和端口  
    struct sockaddr_in client_addr;
    bzero(&client_addr, sizeof(client_addr));
    client_addr.sin_family = AF_INET; // internet协议族  
    client_addr.sin_addr.s_addr = htons(INADDR_ANY); // INADDR_ANY表示自动获取本机地址  
    client_addr.sin_port = htons(0); // auto allocated, 让系统自动分配一个空闲端口  

// 创建用于internet的流协议(TCP)类型socket,用client_socket代表客户端socket  
    int client_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (client_socket < 0) {
        printf("Create Socket Failed!\n");
        exit(1);
    }

// 把客户端的socket和客户端的socket地址结构绑定   
    if (bind(client_socket, (struct sockaddr *) &client_addr, sizeof(client_addr))) {
        printf("Client Bind Port Failed!\n");
        exit(1);
    }

// 设置一个socket地址结构server_addr,代表服务器的internet地址和端口  
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;

// 服务器的IP地址来自程序的参数   
    if (inet_aton(ipaddr, &server_addr.sin_addr) == 0) {
        printf("Server IP Address Error!\n");
        exit(1);
    }
    server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
    socklen_t server_addr_length = sizeof(server_addr);
    // 向服务器发起连接请求,连接成功后client_socket代表客户端和服务器端的一个socket连接  
    if (connect(client_socket, (struct sockaddr *) &server_addr, server_addr_length) < 0) {
        printf("Can Not Connect To %s!\n", ipaddr);
        exit(1);
    }

    char file_name[FILE_NAME_MAX_SIZE + 1];
    bzero(file_name, sizeof(file_name));
    printf("Please Input File Name.\t");
    scanf("%s", file_name);
    if ((fp = fopen(file_name, "r")) == NULL) {
        printf("Failure to open %s\n", file_name);
        exit(0);
    }

    char buffer[BUFFER_SIZE];
    bzero(buffer, sizeof(buffer));
    strcpy(buffer, file_name);
    if (send(client_socket, buffer, BUFFER_SIZE, 0) == -1) {
        printf("发送文件名失败\n");
    }
    char ch;
    int i = 0;
    while ((ch = fgetc(fp)) != EOF) {
        buffer[i++] = ch;
        if (i >= BUFFER_SIZE) {
            if ((send(client_socket, buffer, BUFFER_SIZE, 0)) == -1) {
                printf("发送文件失败\n");
            }
            bzero(buffer, sizeof(buffer));
            i = 0;
        }
    }
    if (i < BUFFER_SIZE) {
        if ((send(client_socket, buffer, i, 0)) == -1) {
            printf("发送文件失败\n");
        }
    }
    printf("发送%s完毕\n", file_name);
    mywc(file_name, 2);
    // 向服务器发送buffer中的数据,此时buffer中存放的是客户端需要接收的文件 
    //以下接收服务器发来的单词个数
    bzero(buffer, sizeof(buffer));

    fclose(fp);
    close(client_socket);
    return 0;

}

int mywc(char file_name[], int choose) {
    FILE *fp;
    char ch;
    int flag = 0, num = 0;

    if ((fp = fopen(file_name, "r")) == NULL) {
        printf("Failure to open %s\n", file_name);
        exit(0);
    }
    
    while ((ch = fgetc(fp)) != EOF) {
        if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r')
            flag = 0;
        else {
            if (flag == 0) {
                flag = 1;
                num++;
            }
        }
    }
    printf("单词个数为:%d\n", num);
    fclose(fp);
    return num;
}

server2.c

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

#define HELLO_WORLD_SERVER_PORT 175233
#define LENGTH_OF_LISTEN_QUEUE 20
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512

void *process_client(void *new_server_socket);

int mywc(char file_name[]) {
    char ch;
    int flag = 0, num = 0;
    FILE *fp;
    printf("实现“wc -w”:\n");
    if ((fp = fopen(file_name, "r")) == NULL) {
        printf("Failure to open %s\n", file_name);
        exit(0);
    }

    while ((ch = fgetc(fp)) != EOF) {
        if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r')
            flag = 0;
        else {
            if (flag == 0) {
                flag = 1;
                num++;
            }
        }
    }

    printf("单词个数为:%d\n", num);
    fclose(fp);
    return num;
}

int main(int argc, char **argv) {
// set socket's address information   
// 设置一个socket地址结构server_addr,代表服务器internet的地址和端口  
    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(HELLO_WORLD_SERVER_PORT);
// create a stream socket   
// 创建用于internet的流协议(TCP)socket,用server_socket代表服务器向客户端提供服务的接口  
    int server_socket = socket(PF_INET, SOCK_STREAM, 0);
    if (server_socket < 0) {
        printf("Create Socket Failed!\n");
        exit(1);
    }

// 把socket和socket地址结构绑定   
    if (bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr))) {
        printf("Server Bind Port: %d Failed!\n", HELLO_WORLD_SERVER_PORT);
        exit(1);
    }

// server_socket用于监听   
    if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE)) {
        printf("Server Listen Failed!\n");
        exit(1);
    }
// 服务器端一直运行用以持续为客户端提供服务   

    while (1) {
// 定义客户端的socket地址结构client_addr,当收到来自客户端的请求后,调用accept  
// 接受此请求,同时将client端的地址和端口等信息写入client_addr中  
        struct sockaddr_in client_addr;
        socklen_t length = sizeof(client_addr);


        int new_server_socket = accept(server_socket, (struct sockaddr *) &client_addr, &length);
        printf("连接到客户端\n");
        if (new_server_socket < 0) {
            printf("Server Accept Failed!\n");

        }
        //添加进程相关代码
        pthread_t pid;
        if (pthread_create(&pid, NULL, process_client, (void *) &new_server_socket) < 0) {
            printf("pthread_create error\n");
        }

    }
//  close(server_socket);
}

void *process_client(void *new_server_socket) {
    int sockid = *(int *) new_server_socket;
    FILE *fp;
    //接受来自客户端的文件
    char buffer[BUFFER_SIZE];
    char file_name[FILE_NAME_MAX_SIZE];
    bzero(buffer, sizeof(buffer));
    int length = 0;
    if (recv(sockid, buffer, BUFFER_SIZE, 0) == -1) {
        printf("接受文件名%s失败\n", buffer);
    }
    strcpy(file_name, buffer);
    strcat(file_name, "-server");
    if ((fp = fopen(file_name, "w")) == NULL) {
        printf("Failure to open %s\n", file_name);
        exit(0);
    }
    while (length = recv(sockid, buffer, BUFFER_SIZE, 0)) {
        if (length < 0) {
            printf("接受文件出错\n");
            exit(0);
        }

        if (fwrite(buffer, sizeof(char), length, fp) < length) {
            printf("写文件失败\n");
        }
        bzero(buffer, BUFFER_SIZE);
    }
    fclose(fp);
    printf("接受文件完毕\n");
    int number = 0;
    number = mywc(file_name);
    bzero(buffer, BUFFER_SIZE);
    buffer[0] = number + 48;


    bzero(buffer, sizeof(buffer));


    printf("File Transfer Finished!\n");
    close(new_server_socket);
} 

分析:
多线程处理可以同时运行多个过程,一定程度上提高响应速度,在多核的情况下还是更能充分利用CPU资源的。


实验三-并发程序-3

  • 要求:交叉编译多线程版本服务器并部署到实验箱中
  • PC机作客户端测试wc服务器
  • 提交测试截图

运行截图

result.png


学习到的知识点:

多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU而只和I/O,OEMBIOS等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。每个程序执行时都会产生一个进程,而每一个进程至少要有一个主线程。这个线程其实是进程执行的一条线索,除了主线程外还可以给进程增加其它的线程,也即增加其它的执行线索,由此在某种程度上可以看成是给一个应用程序增加了多任务功能。

posted @ 2019-11-24 13:41  20175233  阅读(404)  评论(0编辑  收藏  举报