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

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

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

實驗三-併發程式-1

任務詳情
  • 學習使用Linux命令wc(1)
  • 基於Linux Socket程式設計實現wc(1)伺服器(埠號是你學號的後6位)和客戶端
  • 客戶端傳一個文字檔案給伺服器
  • 伺服器返加文字檔案中的單詞數
  • 上方提交程式碼
  • 附件提交測試截圖,至少要測試附件中的兩個檔案

    學習使用Linux命令wc(1)
  • 基於Linux Socket程式設計實現wc(1)伺服器(埠號是你學號的後6位)和客戶端
  • 客戶端傳一個文字檔案給伺服器;伺服器返加文字檔案中的單詞數
  • 實驗三-併發程式-2

    任務詳情
    使用多執行緒實現wc伺服器並使用同步互斥機制保證計數正確
    上方提交程式碼
    下方提交測試
    對比單執行緒版本的效能,並分析原因

  • server.c:
    #include<stdlib.h>

    #include<pthread.h>

    #include<sys/socket.h>

    #include<sys/types.h> //pthread_t , pthread_attr_t and so on.

    #include<stdio.h>

    #include<netinet/in.h> //structure sockaddr_in

    #include<arpa/inet.h> //Func : htonl; htons; ntohl; ntohs

    #include<assert.h> //Func :assert

    #include<string.h> //Func :memset bzero

    #include<unistd.h> //Func :close,write,read

    #define SOCK_PORT 165329

    #define BUFFER_LENGTH 1024

    #define MAX_CONN_LIMIT 512 //MAX connection limit

    static void Data_handle(void * sock_fd); //Only can be seen in the file

    int CountWordsOfEuropeanTxtFile(char *szFileName);

    int CountWordsInOneLine(const char *szLine);

    int main()

    {

    int sockfd_server;

    int sockfd;

    int fd_temp;

    struct sockaddr_in s_addr_in;

    struct sockaddr_in s_addr_client;

    int client_length;

    sockfd_server = socket(AF_INET,SOCK_STREAM,0); //ipv4,TCP

    assert(sockfd_server != -1);

    //before bind(), set the attr of structure sockaddr.

    memset(&s_addr_in,0,sizeof(s_addr_in));

    s_addr_in.sin_family = AF_INET;

    s_addr_in.sin_addr.s_addr = htonl(INADDR_ANY); //trans addr from uint32_t host byte order to network byte order.

    s_addr_in.sin_port = htons(SOCK_PORT); //trans port from uint16_t host byte order to network byte order.

    fd_temp = bind(sockfd_server,(const struct sockaddr *)(&s_addr_in),sizeof(s_addr_in));

    if(fd_temp == -1)

    {

    fprintf(stderr,"bind error!\n");

    exit(1);

    }

    fd_temp = listen(sockfd_server,MAX_CONN_LIMIT);

    if(fd_temp == -1)

    {

    fprintf(stderr,"listen error!\n");

    exit(1);

    }

    while(1)

    {

    printf("waiting for new connection...\n");

    pthread_t thread_id;

    client_length = sizeof(s_addr_client);

    //Block here. Until server accpets a new connection.

    sockfd = accept(sockfd_server,(struct sockaddr * restrict)(&s_addr_client),(socklen_t *)(&client_length));

    if(sockfd == -1)

    {

    fprintf(stderr,"Accept error!\n");

    continue; //ignore current socket ,continue while loop.

    }

    printf("A new connection occurs!\n");

    if(pthread_create(&thread_id,NULL,(void *)(&Data_handle),(void *)(&sockfd)) == -1)

    {

    fprintf(stderr,"pthread_create error!\n");

    break; //break while loop

    }

    }

    //Clear

    int ret = shutdown(sockfd_server,SHUT_WR); //shut down the all or part of a full-duplex connection.

    assert(ret != -1);

    printf("Server shuts down\n");

    return 0;

    }

    static void Data_handle(void * sock_fd)

    {

    int fd = *((int *)sock_fd);

    int i_recvBytes;

    char data_recv[BUFFER_LENGTH];

    const char * data_send = "Server has received your request!\n";

    while(1)

    {

    printf("waiting for file_name...\n");

    //Reset data.

    memset(data_recv,0,BUFFER_LENGTH);

    i_recvBytes = read(fd,data_recv,BUFFER_LENGTH);

    if(i_recvBytes == 0)

    {

    printf("Maybe the client has closed\n");

    break;

    }

    if(i_recvBytes == -1)

    {

    fprintf(stderr,"read error!\n");

    break;

    }

    if(strcmp(data_recv,"quit")==0)

    {

    printf("Quit command!\n");

    break; //Break the while loop.

    }

    printf("read from client : %s\n",data_recv);

     

    char buffer[BUFFER_LENGTH];

    int count=0;

    bzero(buffer, BUFFER_LENGTH);

    count = CountWordsOfEuropeanTxtFile(data_recv);

    sprintf(buffer,"%d", count);

    send(fd, buffer, sizeof(buffer), 0);

    if(write(fd,data_send,strlen(data_send)) == -1)

    {

    break;

    }

    }

    //Clear

    printf("terminating current client_connection...\n");

    close(fd); //close a file descriptor.

    pthread_exit(NULL); //terminate calling thread!

    }

    int CountWordsOfEuropeanTxtFile(char *szFileName)

    {

    int nWords = 0;//詞計數變數,初始值為0

    FILE *fp; //檔案指標

    char carrBuffer[1024];//每行字元緩衝,每行最多1024個字元

    //開啟檔案

    if ((fp = fopen(szFileName, "r")) == NULL)

    {

    return -1; //檔案開啟不成功是返回-1

    }

    while (!feof(fp))//如果沒有讀到檔案末尾

    {

    //從檔案中讀一行

    if (fgets(carrBuffer, sizeof(carrBuffer),fp) != NULL)

    //統計每行詞數

    nWords += CountWordsInOneLine(carrBuffer);

    }

     

    //關閉檔案

    fclose(fp);

    return nWords;

    }

    int CountWordsInOneLine(const char *szLine)

    {

    int nWords = 0;

    int i=0;

    for (;i<strlen(szLine);i++)

    {

    if (*(szLine+i)!=' ')

    {

    nWords++;

    while ((*(szLine+i)!=' ')&&(*(szLine+i)!='\0'))

    {

    i++;

    }

    }

     

    }

    //printf("%d\t",nWords);

     

    return nWords;

    }

    client.c
    #include<stdlib.h>
    #include<sys/socket.h>
    #include<sys/types.h> //pthread_t , pthread_attr_t and so on.
    #include<stdio.h>
    #include<netinet/in.h> //structure sockaddr_in
    #include<arpa/inet.h> //Func : htonl; htons; ntohl; ntohs
    #include<assert.h> //Func :assert
    #include<string.h> //Func :memset bzero
    #include<unistd.h> //Func :close,write,read
    #define SOCK_PORT 165329
    #define BUFFER_LENGTH 1024
    int main()
    {
    int sockfd;
    int tempfd;
    struct sockaddr_in s_addr_in;
    char data_send[BUFFER_LENGTH];
    char data_recv[BUFFER_LENGTH];
    memset(data_send,0,BUFFER_LENGTH);
    memset(data_recv,0,BUFFER_LENGTH);
    sockfd = socket(AF_INET,SOCK_STREAM,0); //ipv4,TCP
    if(sockfd == -1)
    {
    fprintf(stderr,"socket error!\n");
    exit(1);
    }
    //before func connect, set the attr of structure sockaddr.
    memset(&s_addr_in,0,sizeof(s_addr_in));
    s_addr_in.sin_addr.s_addr = inet_addr("127.0.0.1"); //trans char * to in_addr_t
    s_addr_in.sin_family = AF_INET;
    s_addr_in.sin_port = htons(SOCK_PORT);
    tempfd = connect(sockfd,(struct sockaddr *)(&s_addr_in),sizeof(s_addr_in));
    if(tempfd == -1)
    {
    fprintf(stderr,"Connect error! \n");
    exit(1);
    }
    while(1)
    {
    printf("Please Input File Name On Server(input \"quit\" to quit):\t");
    scanf("%s", data_send);
    //gets(data_send);
    //scanf("%[^\n]",data_send); //or you can also use this
    tempfd = write(sockfd,data_send,BUFFER_LENGTH);
    if(tempfd == -1)
    {
    fprintf(stderr,"write error\n");
    exit(0);
    }

    if(strcmp(data_send,"quit") == 0) //quit,write the quit request and shutdown client
    {
    break;
    }
    else
    {
    tempfd = read(sockfd,data_recv,BUFFER_LENGTH);
    assert(tempfd != -1);
    printf("%s\n",data_recv);
    memset(data_send,0,BUFFER_LENGTH);
    memset(data_recv,0,BUFFER_LENGTH);
    }
    char buffer[BUFFER_LENGTH];
    int length=0;
    bzero(buffer, BUFFER_LENGTH);
    length = recv(sockfd, buffer, BUFFER_LENGTH, 0);
    buffer[length] = '\0';
    printf("count=%s\n", buffer);
    bzero(buffer, BUFFER_LENGTH);
    }
    int ret = shutdown(sockfd,SHUT_WR); //or you can use func close()--<unistd.h> to close the fd
    assert(ret != -1);
    return 0;
    }

  • 對比單執行緒版本的效能,並分析原因
    • 執行緒本身由於建立和切換的開銷,採用多執行緒不會提高程式的執行速度,反而會降低速度,但是對於頻繁IO操作的程式,多執行緒可以有效的併發。
    • 單執行緒比較穩定易於實現,執行穩定。而多執行緒由於建立和切換的開銷,採用多執行緒可能不會提高程式的執行速度,反而會降低速度,但是對於頻繁IO操作的程式,多執行緒可以有效的併發。
    • 對於包含不同任務的程式,可以考慮每個任務使用一個執行緒。這樣的程式在設計上相對於單執行緒做所有事的程式來說,更為清晰明瞭,比如生產、消費者問題
    • 在實際的開發中對於效能優化的問題需要考慮到具體的場景來考慮是否使用多執行緒技術。