1. 程式人生 > >2018-2019-1 《資訊安全系統設計基礎》實驗三:併發程式

2018-2019-1 《資訊安全系統設計基礎》實驗三:併發程式

2018-2019-1 《資訊安全系統設計基礎》實驗三:併發程式


任務一
  • 要求:

  • 基於Linux Socket程式設計實現wc(1)伺服器(埠號是你學號的後6位)和客戶端

  • 客戶端傳一個文字檔案給伺服器

  • 伺服器返加文字檔案中的單詞數

  • wc命令引數:
-c:統計位元組數
-l:統計行數
-m:統計字元數。這個標誌不能與 -c 標誌一起使用。
-w:統計字數。一個字被定義為由空白、跳格或換行字元分隔的字串
-L:列印最長行的長度
-help:顯示幫助資訊
--version:顯示版本資訊
  • image

    網上找的程式碼:
  • 這是之前做簡單對話時找的程式碼

server:

#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 
   
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; 
}

client:

#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_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); 
  } 
   
  // 接收成功後,關閉檔案,關閉socket 
  printf("Receive File:\t%s From Server IP Successful!\n", file_name); 
  close(fp); 
  close(client_socket_fd); 
  return 0; 
}
  • 根據要求改成了讀取檔案:
 printf("%s\n", file_name);  


    FILE *fp = fopen(file_name, "w");  
  • 以及讀取字數:
int words=0;
char s[100];
FILE *fp2;
if((fp2=fopen(file_name,"r"))==NULL){
    printf("ERROR!\n");
    exit(0);
}
while(fscanf(fp2,"%s",s)!=EOF)
    words++;
fclose(fp2);
printf("%d words.\n",words);
修改後實驗程式碼:

server.c:

#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 155323 
#define LENGTH_OF_LISTEN_QUEUE 20  
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512  
#define BEGIN 1; 

int main(void)  
{  

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);  

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));  

if(-1 == (bind(server_socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr))))  
{  
    perror("Server Bind Failed:");  
    exit(1);  
}  
    
if(-1 == (listen(server_socket_fd, LENGTH_OF_LISTEN_QUEUE)))  
{  
    perror("Server Listen Failed:");  
    exit(1);  
}  

while(1)  
{  
    struct sockaddr_in client_addr;  
    socklen_t client_addr_length = sizeof(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;  
    }  


    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;  
    }  


    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, "w");  
    if(NULL == fp)  
    {  
        printf("File:\t%s Can Not Open To Write\n", file_name);  
        exit(1);  
    }  


    bzero(buffer, BUFFER_SIZE);  
    int length = 0;  
    while((length = recv(new_server_socket_fd, buffer, BUFFER_SIZE, 0)) > 0)  
    {  
    if(strcmp(buffer,"OK")==0) break;
        if(fwrite(buffer, sizeof(char), length, fp) < length)  
        {  
            printf("File:\t%s Write Failed\n", file_name);  
            break;  
        }  
        bzero(buffer, BUFFER_SIZE);  
    }  

    printf("Receive File:\t%s From Client IP Successful!\n", file_name);  
    fclose(fp);

int words=0;
char s[100];
FILE *fp2;
if((fp2=fopen(file_name,"r"))==NULL){
    printf("ERROR!\n");
    exit(0);
}
while(fscanf(fp2,"%s",s)!=EOF)
    words++;
fclose(fp2);
printf("%d words.\n",words);

char sendbuf[50];
sprintf(sendbuf,"%d",words);

send(new_server_socket_fd,sendbuf,50,0);

    close(new_server_socket_fd);  
}  

close(server_socket_fd);  
return 0;  
}

client.c

#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 155323  
#define BUFFER_SIZE 1024  
#define FILE_NAME_MAX_SIZE 512  
#define BEGIN 1;

int main()  
{  

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);  

int client_socket_fd = socket(AF_INET, SOCK_STREAM, 0);  
if(client_socket_fd < 0)  
{  
    perror("Create Socket Failed:");  
    exit(1);  
}  

if(-1 == (bind(client_socket_fd, (struct sockaddr*)&client_addr, sizeof(client_addr))))  
{  
    perror("Client Bind Failed:");  
    exit(1);  
}  

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);  

if(connect(client_socket_fd, (struct sockaddr*)&server_addr, server_addr_length) < 0)  
{  
    perror("Can Not Connect To Server IP:");  
    exit(0);  
}  

char file_name[FILE_NAME_MAX_SIZE+1];  
bzero(file_name, FILE_NAME_MAX_SIZE+1);  

printf("Please Input File Name On Client:\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));  
  
if(send(client_socket_fd, buffer, BUFFER_SIZE, 0) < 0)  
{  
    perror("Send File Name Failed:");  
    exit(1);  
}  

    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(client_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);  
    }  

char s[50];
scanf("%s",s);
send(client_socket_fd,"OK",50,0);

char recvdata[sizeof(int)+1];
recv(client_socket_fd,recvdata,sizeof(int),0);
recvdata[sizeof(int)]='\0';
int words=atoi(recvdata);
close(client_socket_fd);  
return 0;  
}
  • 結果截圖:

  • image

思考題
  • 對比單執行緒的版本的效能,並分析原因。

原因:

  • 多執行緒技術使程式的響應速度更快,因為使用者介面可以在進行其它工作的同時一直處於活動狀態;

  • 當前沒有進行處理的任務時可以將處理器時間讓給其它任務;

  • 佔用大量處理時間的任務可以定期將處理器時間讓給其它任務;

  • 可以隨時停止任務;

  • 可以分別設定各個任務的優先順序以優化效能。

  • 是否需要建立多個執行緒取決於各種因素。在以下情況下,最適合採用多執行緒處理:
  • 耗時或大量佔用處理器的任務阻塞使用者介面操作;

  • 各個任務必須等待外部資源 (如遠端檔案或 Internet連線)。