2017-2018-1 20155239 实验三:并发程序

2017-2018-1 20155239 实验三:并发程序

实验三-并发程序-1

实验要求

  • 学习使用Linux命令wc(1)

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

  • 客户端传一个文本文件给服务器

  • 服务器返加文本文件中的单词数

  • wc命令的学习

  • wc即word count

-c 统计字节数

-l 统计行数

-m 统计字符数。这个标志不能与 -c 标志一起使用

-w 统计字数。一个字被定义为由空白、跳格或换行字符分隔的字符串

-L 打印最长行的长度

-help 显示帮助信息

--version 显示版本信息

wc统计字符命令的C语言实现:

 #include <stdio.h>
 #include <string.h>
 int *getCharNum(char *filename, int *totalNum);
 int main()
{
char filename[30];      // totalNum[0]: 总行数  totalNum[1]: 总字符数  totalNum[2]: 总单词数
int totalNum[3] = {0, 0, 0};
printf("Input file name: ");
scanf("%s", filename);
if(getCharNum(filename, totalNum))
{
    printf("Total: %d lines, %d words, %d chars\n", totalNum[0], totalNum[2], totalNum[1]);
}
else
{
    printf("Error!\n");
}
return 0;
}
int *getCharNum(char *filename, int *totalNum)
{
FILE *fp;  // 指向文件的指针
char buffer[1003];  //缓冲区,存储读取到的每行的内容
int bufferLen;  // 缓冲区中实际存储的内容的长度
int i;  // 当前读到缓冲区的第i个字符
char c;  // 读取到的字符
int isLastBlank = 0;  // 上个字符是否是空格
int charNum = 0;  // 当前行的字符数
int wordNum = 0; // 当前行的单词数
if( (fp=fopen(filename, "rb")) == NULL )
{
    perror(filename);
    return NULL;
}
printf("line   words  chars\n");      // 每次读取一行数据,保存到buffer,每行最多只能有1000个字符
while(fgets(buffer, 1003, fp) != NULL)
{
    bufferLen = strlen(buffer);         // 遍历缓冲区的内容
    for(i=0; i<bufferLen; i++)
    {
        c = buffer[i];
        if( c==' ' || c=='\t')
        {  // 遇到空格
            !isLastBlank && wordNum++;  // 如果上个字符不是空格,那么单词数加1
            isLastBlank = 1;
        }
        else if(c!='\n'&&c!='\r')
        {  // 忽略换行符
            charNum++;  // 如果既不是换行符也不是空格,字符数加1
            isLastBlank = 0;
        }
    }
    !isLastBlank && wordNum++;  // 如果最后一个字符不是空格,那么单词数加1
    isLastBlank = 1;  // 每次换行重置为1          // 一行结束,计算总字符数、总单词数、总行数
    totalNum[0]++;  // 总行数
    totalNum[1] += charNum;  // 总字符数
    totalNum[2] += wordNum;  // 总单词数
    printf("%-7d%-7d%d\n", totalNum[0], wordNum, charNum);         // 置零,重新统计下一行
    charNum = 0;
    wordNum = 0;
    }
    return totalNum;
 }

实现结果:

实现wc服务器

基于C语言实现,将代码整合至socket中。

服务器接受文件与读取文件关键代码:

int main(int argc, char *argv[])  
{  
// 设置输出缓冲  
setvbuf(stdout, NULL, _IONBF, 0);  
fflush(stdout);  

int sockfd,new_fd;  
struct sockaddr_in server_addr;  
struct sockaddr_in client_addr;  
int sin_size,portnumber;  
char hello[]="Hello! Are You Fine?\n";  

if((portnumber=atoi("155216"))<0)  
{  
    fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);  
    exit(1);  
}  

/* 服务器端开始建立socket描述符 */  
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) {  
    fprintf(stderr,"Socket error:%s\n\a",strerror(errno));  
    exit(1);  
}  

/* 服务器端填充 sockaddr结构  */  
bzero(&server_addr,sizeof(struct sockaddr_in));  
server_addr.sin_family=AF_INET;  
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);  
server_addr.sin_port=htons(portnumber);  

/* 捆绑sockfd描述符  */  
if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) {  
    fprintf(stderr,"Bind error:%s\n\a",strerror(errno));  
    exit(1);  
}  

/* 监听sockfd描述符  */  
if(listen(sockfd,5)==-1) {  
    fprintf(stderr,"Listen error:%s\n\a",strerror(errno));  
    exit(1);  
}  

while(1)  
{  
    fprintf(stderr, "server is listening!\n");  

    /* 服务器阻塞,直到客户程序建立连接  */  
    sin_size=sizeof(struct sockaddr_in);  
    if( ( new_fd = accept(sockfd,(struct sockaddr *)(&client_addr),(socklen_t*)&sin_size ) ) == -1) {  
        fprintf(stderr,"Accept error:%s\n\a",strerror(errno));  
        exit(1);  
    }  

    fprintf(stderr,"Server get connection from %s\n",  
        inet_ntoa(client_addr.sin_addr));  
    if(write(new_fd,hello,strlen(hello))==-1) {  
        fprintf(stderr,"Write Error:%s\n",strerror(errno));  
        exit(1);  
    }  

    long int read_size = 0;  
    unsigned long file_len  = 0;  
    int order_id  = 0;  
    char file_name[128] = {'\0'};  
    char file_info[1024] = {'\0'};  

    // 读取指令  
    printf("\n\nWaiting for read file info!\n");  
    int nn = 0;  
    if(nn = read(new_fd, file_info, 1024))   
    {  
        // 指令ID  
        int id_h = (int)file_info[0]<<8;  
        order_id = id_h + (int)file_info[1];  

        // 文件长度  
        // 高16位  
        unsigned long len_hig_1 = 0;  
        memcpy(&len_hig_1, &file_info[2], sizeof(file_info[2]));  

        unsigned long len_hig_2 = 0;  
        memcpy(&len_hig_2, &file_info[3], sizeof(file_info[3]));  

        unsigned long len_hig = len_hig_1 * 256 + len_hig_2;  

        // 低16位  
        unsigned long len_low_1 = 0;  
        memcpy(&len_low_1, &file_info[4], sizeof(file_info[4]));  

        unsigned long len_low_2 = 0;  
        memcpy(&len_low_2, &file_info[5], sizeof(file_info[5]));  

        int len_low = len_low_1 * 256 + len_low_2;  
        file_len = len_hig * 256 * 256 + len_low;  

        // 文件名称  
        strncpy(file_name, &file_info[6], strlen(&file_info[6]));  

        printf("order = %d, %lu, %s\n", order_id, file_len, file_name);  

        if((strlen(file_name) == 0) || (file_len == 0))  
        {  
            printf("Read file info error!\n File_name or file_len is zero!\n");  
            close(new_fd);  
            continue;  
        }  
    }  
    else {  
        printf("Read file info error!\n");  
        close(new_fd);  
        close(sockfd);  
        exit(0);  
    }  

    // 写入文件  
    printf("\n\nWaiting for read file content!\n");  
    FILE* pf = fopen(file_name, "wb+");  
    if(pf == NULL)  
    {  
        printf("Open file error!\n");  
        close(new_fd);  
        continue;  
    }

客户端传输文件关键代码:

 int main(int argc, char *argv[])  
{  
if(argc < 2)  
{  
    printf("please input:<ip> <port> <filePath>.\n");  
    return 0;  
}  

    // 设置输出缓冲  
    setvbuf(stdout, NULL, _IONBF, 0);  
    fflush(stdout);  

char* filePath = argv[3];  
if(access(filePath, F_OK) != 0)  
{  
    printf("file not existed!\n");  
    return 0;  
}  

    int sockfd;  
    char buff[1024] = {'\0'};  
    struct sockaddr_in server_addr;  
    struct hostent *host;  
    int portnumber,nbytes;  

const char* ip = argv[1];  
    if((host=gethostbyname(ip))==NULL)  
    {  
            fprintf(stderr,"Gethostname error\n");  
            exit(1);  
    }  

const char* port = argv[2];  
    if((portnumber=atoi(port))<0)  
    {  
            fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);  
            exit(1);  
    }  

    /* 客户程序开始建立 sockfd描述符  */  
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)  
    {  
            fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));  
            exit(1);  
    }  

    /* 客户程序填充服务端的资料       */  
    bzero(&server_addr,sizeof(server_addr));  
    server_addr.sin_family=AF_INET;  
    server_addr.sin_port=htons(portnumber);  
    server_addr.sin_addr=*((struct in_addr *)host->h_addr);  

    /* 客户程序发起连接请求         */  
    if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)  
    {  
            fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));  
            exit(1);  
    }  

    /* 连接成功了           */  
    if((nbytes=read(sockfd,buff,1024))==-1)  
    {  
            fprintf(stderr,"Read Error:%s\n",strerror(errno));  
            exit(1);  
    }  
    buff[nbytes]='\0';  
    printf("I have received:%s\n",buff);  

/******* 发送指令 ********/  
bzero(buff,1024);  
// 指令ID  
int order = 0x0010;  
int order_h = order >> 8;  
buff[0] = (char)order_h;  
buff[1] = (char)order;  

// 文件长度  
unsigned long len = get_file_size(filePath);  
printf("file size = %lu\n", len);  

// 高16位  
int len_h = len >> 16;  
int len_h_1 = len_h >> 8;  
buff[2] = (char)len_h_1;  
buff[3] = (char)len_h;  

// 低16位  
int len_l = len;  
int len_l_1 = len_l >> 8;  
buff[4] = (char)len_l_1;  
buff[5] = (char)len_l;  

// 文件名称  
char* fileName = get_file_name(filePath);  
printf("file name = %s\n", fileName);  
strncpy(&buff[6], fileName, strlen(fileName));  

write(sockfd,buff,1024);      
  
/******* 发送文件 ********/  
printf("file path = %s\n", filePath);  
FILE* pf = fopen(filePath, "rb");  
if(pf == NULL) {  
    printf("open file failed!\n");  
    exit(0);  
}

实验三-并发程序-2

实验要求:

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

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

  • 多线程实现wc服务器

  • 实现结果(同时对test1和test2测试):

posted on 2017-11-19 22:41  吕宇轩  阅读(174)  评论(0编辑  收藏  举报