20155322 2017-2018-1《信息安全系统设计》实验三:实时系统


实验三 实时系统

任务一

任务详情:

  • 学习使用Linux命令wc

  • 基于Linux Socket程序设计实现wc服务器(端口号是你学号的后6位)和客户端

  • 客户端传一个文本文件给服务器,服务器返加文本文件中的单词数

任务过程:

实现wc命令

  • Linux系统中的wc(Word Count)命令的功能为统计指定文件中的字节数、字数、行数,并将统计结果显示输出。
    • 命令格式:wc [选项]文件...
    • 命令功能:统计指定文件中的字节数、字数、行数,并将统计结果显示输出。该命令统计指定文件中的字节数、字数、行数。如果没有给出文件名,则从标准输入读取。wc同时也给出所指定文件的总统计数。
  • 在这里我简单的设计实现了wc的统计字节数、字数、行数功能的函数。
    • 用数组存储字节数、字数、行数功能
    • 通过fgetc读取字符,通过My_wc函数实现判断
void excute(FILE *fp,char file_name[]){
	//FILE *fp;
	char c;
	int i,j;
        int res_current[3];                                          
        int res_total[3];

        for(i=0;i<3;i++)
        {
                res_current[i]=0;
                res_total[i]=res_current[i];
        }
       	j=0;
        while(++j<2)
        {
                        if((fp=fopen(file_name,"r"))==NULL)
                        {
                               printf("Can not open the file %s\n",file_name);
                               exit(0);
                        }
                        c=fgetc(fp);
                        while(c!=EOF)
                        {
                                my_wc(res_current,c);
                                c=fgetc(fp);
                        }
                        fclose(fp);
                        printf(" %d %d %d %s\n",res_current[0],res_current[1],res_current[2],file_name);
                        for(i=0;i<3;i++)
                        {
                                res_total[i]+=res_current[i];
                                res_current[i]=0;
                        }
                }
                printf(" %d %d %d total\n",res_total[0],res_total[1],res_total[2]);
        
}

void my_wc(int res_current[],char c)
{
        int flag;
        res_current[2]++;
        if(c=='\n')
        {
                res_current[0]++;
        }
        if(c=='\t' || c== ' ' || c=='\n')
        {
              if(flag==0)
               {
                     res_current[1]++;
                     flag=1;
               }
        }
        else
           flag=0;       
       
}

实现服务器/客户端

这里我直接在网上选择了一个合适的服务器和客户端代码进行实现wc功能的改造:
服务器的实现:

#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 155322 
#define LENGTH_OF_LISTEN_QUEUE 20 
#define BUFFER_SIZE 1024 
#define FILE_NAME_MAX_SIZE 512 
  
int main(void) 
{ 
  // 声明并初始化一个服务器端的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(-1 == (bind(server_socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)))) 
  { 
    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("%s\n", file_name); 
  
    // 打开文件并读取文件数据 
    FILE *fp = fopen(file_name, "r"); 
    if(NULL == fp) 
    { 
      printf("File:%s Not Found\n", file_name); 
    } 
    else
    { 
      bzero(buffer, BUFFER_SIZE); 
      int length = 0; 
      // 每读取一段数据,便将其发送给客户端,循环直到文件读完为止 
      while((length = fread(buffer, sizeof(char), BUFFER_SIZE, fp)) > 0) 
      { 
        if(send(new_server_socket_fd, buffer, length, 0) < 0) 
        { 
          printf("Send File:%s Failed./n", file_name); 
          break; 
        } 
        bzero(buffer, BUFFER_SIZE); 
      } 
  
      // 关闭文件 
      fclose(fp); 
      printf("File:%s Transfer Successful!\n", file_name); 
    } 
    // 关闭与客户端的连接 
    close(new_server_socket_fd); 
  } 
  // 关闭监听用的socket 
  close(server_socket_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 155322 
#define BUFFER_SIZE 1024 
#define FILE_NAME_MAX_SIZE 512 
extern int flag=1;
void excute(FILE *fp,char file_name[]);
void my_wc(int res_current[],char c);

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_socket_fd = socket(AF_INET, SOCK_STREAM, 0); 
  if(client_socket_fd < 0) 
  { 
    perror("Create Socket Failed:"); 
    exit(1); 
  } 
  
  // 绑定客户端的socket和客户端的socket地址结构 非必需 
  if(-1 == (bind(client_socket_fd, (struct sockaddr*)&client_addr, sizeof(client_addr)))) 
  { 
    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(client_socket_fd, (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)); 
    
  // 向服务器发送buffer中的数据 
  if(send(client_socket_fd, buffer, BUFFER_SIZE, 0) < 0) 
  { 
    perror("Send File Name Failed:"); 
    exit(1); 
  } 
  
  // 打开文件,准备写入 
  FILE *fp = fopen(file_name, "w"); 
  if(NULL == fp) 
  { 
    printf("File:\t%s Can Not Open To Write\n", file_name); 
    exit(1); 
  } 
  
  // 从服务器接收数据到buffer中 
  // 每接收一段数据,便将其写入文件中,循环直到文件接收完并写完为止 
  bzero(buffer, BUFFER_SIZE); 
  int length = 0; 
  while((length = recv(client_socket_fd, buffer, BUFFER_SIZE, 0)) > 0) 
  { 
    if(fwrite(buffer, sizeof(char), length, fp) < length) 
    { 
      printf("File:\t%s Write Failed\n", file_name); 
      break; 
    } 
    bzero(buffer, BUFFER_SIZE); 
  } 
	printf("%s\n",file_name);
  	
  // 接收成功后,关闭文件,关闭socket 
  printf("Receive File:\t%s From Server IP Successful!\n", file_name); 
  close(fp); 
  close(client_socket_fd); 


  return 0; 
} 

结合二者完成实验
  • 将wc函数加入客户端,当客户端收到文件的时候,调用excute函数执行mywc功能:
前面代码略……

  while((length = recv(client_socket_fd, buffer, BUFFER_SIZE, 0)) > 0) 
  { 
    if(fwrite(buffer, sizeof(char), length, fp) < length) 
    { 
      printf("File:\t%s Write Failed\n", file_name); 
      break; 
    } 
    bzero(buffer, BUFFER_SIZE); 
  } 
	printf("%s\n",file_name);
  	excute(fp,file_name);
  // 接收成功后,关闭文件,关闭socket 
  printf("Receive File:\t%s From Server IP Successful!\n", file_name); 
  close(fp); 
  close(client_socket_fd); 


  return 0; 
} 

实现截图

任务二

任务详情:

  • 使用多线程实现wc服务器并使用同步互斥机制保证计数正确

  • 对比单线程版本的性能,并分析原因

任务过程:

理解同步和互斥机制概念:
  • 同步:又称直接制约关系,是指多个线程(或进程)为了合作完成任务,必须严格按照规定的 某种先后次序来运行。
  • 实例:线程 T2 中的语句 y 要使用线程 T1 中的语句 x 的运行结果,所以只有当语句 x 执行完成之后语句 y 才可以执行。我们可以使用信号量进行同步:
    semaphore S=0;   // 初始化信号量

T1() {
    ...
    x;           // 语句x
    V(S);        // 告诉线程T2,语句x已经完成
    ...
}

T2() {
    ...
    P(S);        // 检查语句x是否运行完成
    y;           // 检查无误,运行y语句
    ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 互斥:又称间接制约关系,是指系统中的某些共享资源,一次只允许一个线程访问。当一个线程正在访问该临界资源时,其它线程必须等待。
  • 实例:打印机就是一种临界资源,而访问打印机的代码片段称为临界区,故每次只允许一个线程进入临界区。—— 我们同样可以使用信号量解决互斥问题,只需把临界区置于 P(S) 和 V(S) 之间,即可实现两线程对临界资源的互斥访问。
    semaphore S=1;   // 初始化信号量

T1() {
    ...
    P(S);
    线程T1的临界区;  // 访问临界资源
    V(S);
    ...
}

T2() {
    ...
    P(S);
    线程T2的临界区;  // 访问临界资源
    V(S);
    ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 多线程:
    • pthread学习:man pthread

实现代码

稍后贴

代码调试中的问题和解决过程

  • openssl头文件无法使用

本周结对学习情况

-20155302

  • 结对学习内容:
    实验二 固件程序设计

代码托管

参考资料

posted @ 2017-11-17 21:51  blackay03  阅读(222)  评论(0编辑  收藏  举报
Live2D
*/