• 2017-2018-1 20155222 201552228 实验三-并发程序


    2017-2018-1 20155222 201552228 实验三-并发程序

    实验内容

    实验三-并发程序-1

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

    上方提交代码
    附件提交测试截图,至少要测试附件中的两个文件

    实验三-并发程序-2

    使用多线程实现wc服务器并使用同步互斥机制保证计数正确
    上方提交代码
    下方提交测试
    对比单线程版本的性能,并分析原因

    实验三-并发程序-3

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

    实验要求

    • 提交实验报告博客,一组写一篇,实验中贡献小的写博客,贡献多的可以给出同组同学的博客链接。

    • 博客标题:2017-2018-1 学号1 学号2 实验三 实时系统

    • 实验目的,实验步骤

    • 实验中的问题及解决过程

    • 新学到的知识点

    实验步骤

    Linux系统中的wc(Word Count)命令

    功能为统计指定文件中的字节数、字数、行数,并将统计结果显示输出。

    1. 命令格式:
    wc [选项]文件...
    
    1. 命令功能:统计指定文件中的字节数、字数、行数,并将统计结果显示输出。该命令统计指定文件中的字节数、字数、行数。如果没有给出文件名,则从标准输入读取。wc同时也给出所指定文件的总统计数。

    2. 命令参数

    • -c 统计字节数。

    • -l 统计行数。

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

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

    • -L 打印最长行的长度。

    • -help 显示帮助信息

    • --version 显示版本信息

    字数统计函数

    int getnumberofwords(char readfile[63335])
    //函数返回readfile字符串中的字数
    {
    
        int i,n=0,flag=1;
    
        for(i = 0; readfile[i]; i ++)//字符串不为空就继续循环
    
        {
    
            if(flag == 1)//如果上个字符不是空格
    
            {
    
                if(readfile[i] != ' ')//如果这个字符为空格
    
                {
    
                    n++;//字数统计加1
    
                    flag = 0;
    
                }
    
            }
            else if(readfile[i] == ' ')
            //如果上个字符是空格而且这个字符也是空格
    
                flag = 1;
    
        }
    
        return n;//返回字符个数
    
    }
    
    

    文件读写函数

    void readfile (char *readfile,char address[1024])
    //将address的内容作为地址打开文件读取到readfile中
    
    {
    
        FILE *fp;
    
        char ch;
    
        int i,count;
    
        if((fp=fopen(address,"rb"))==NULL)//文件打开
    
        {
    
            printf("failed to open file!
    ");//文件打开失败
    
            exit(0);
    
        }
    
        count=0;
    
        while((ch=fgetc(fp))!=EOF && count<1024)//读取文件内容
    
        {
    
            if(isprint(ch))
    
            {
    
                readfile[count]=ch;
    
                count++;
    
            }
    
        }
    
        fclose(fp);//关闭文件
    
        return 0;
    
    }
    

    Socket编程基本框架(服务器)

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <Winsock2.h>
    #define MY_PORT 3434
    
    int main()
    {
        char *buffer1="connect to server successful!";
        char buffer2[1024]="no client connected to server!";
        char buffer3[1024]="";
        char buffer4[1024]="";
        int bytes_recvd;
        int m,n;
        m=1;
    //打开服务
        WSADATA wsaData;
        WSAStartup(MAKEWORD(1,1),&wsaData);
    //初始化
        struct sockaddr_in my_addr;
        my_addr.sin_family = AF_INET;
        my_addr.sin_port = htons(MY_PORT);//htons:将主机的无符号短整型数字节顺序转换成网络字节顺序
        my_addr.sin_addr.s_addr = htonl(INADDR_ANY);//htonl:将主机的无符合长整型数字节顺序转换成网络字节顺序。
    //指定协议
        SOCKET listen_sock, new_sock;
        listen_sock = socket(AF_INET, SOCK_STREAM, 0);
        //根据指定的地址族、数据类型和协议来生成一个套接字的描述字(listen_sock )
        //地址描述:AP_INET,指定socket类型:SOCK_STREAM,函数返回值为整型socket描述符。
        bind(listen_sock, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));//将一个套接字和一个本地地址与绑定在一起
        //listen_sock:socket描述符,(struct sockaddr *)&my_addr:指向sockaddr类型的指针
        listen(listen_sock, 5);
        //5:在请求队列中允许的最大请求数为5
    //定义监听套接字
        new_sock = accept(listen_sock, NULL, NULL);
        //listen_sock:被监听的socket描述符
    //发送数据
        send(new_sock, buffer1, strlen(buffer1), 0);
        //new_sock:用于传输数据的socket描述符,buffer:是一个指向要发送数据的指针,strlen(buffer):以字节为单位的数据的长度。
    //文件接收
        bytes_recvd = recv(new_sock, buffer2, sizeof(buffer2), 0);
        printf("Server received message(%d bytes): %s
    ", bytes_recvd, buffer2);
    
        memset(buffer3, 0, sizeof(buffer3));
        bytes_recvd = recv(new_sock, buffer3, sizeof(buffer3), 0);
        printf("client:%s
    ",buffer3);
    
        memset(buffer4, 0, sizeof(buffer4));
        printf("server:");
        gets(buffer4);
        send(new_sock, buffer4, strlen(buffer4), 0);
    
        printf("server disconnected to client successful!");
    //关闭套接字
        closesocket(new_sock);
        closesocket(listen_sock);
    
        WSACleanup();
    
        while(1)
        {
    
        }
    
        return 0;
    }
    
    

    服务器和客户端代码

    //服务器
    
    
    #include <stdio.h>
    
    
    
    #include <stdlib.h>
    
    
    
    #include <sys/types.h>
    
    
    
    #include <sys/socket.h>
    
    
    
    #include <netinet/in.h>
    
    
    
    #include <arpa/inet.h>
    
    
    
    #define MY_PORT 155228
    
    
    
    int readfilewords(char readfile[65536]);
    
    
    
    int getnumberofwords(char readfile[63335]);
    
    
    
    void main()
    
    
    
    {
    
    
    
        char *buffer1="connect to server successful!";
    
    
    
        char buffer2[1024]="no client connected to server!";
    
    
    
        char buffer3[65536]="";
    
    
    
        char buffer4[1024]="";
    
    
    
        int bytes_recvd;
    
    
    
        int m,n;
    
    
    
        //WSADATA wsaData;
    
    
    
        //WSAStartup(MAKEWORD(1,1),&wsaData);
    
    
    
        struct sockaddr_in my_addr;
    
    
    
        my_addr.sin_family = AF_INET;
    
    
    
        my_addr.sin_port = htons(MY_PORT);//htons:将主机的无符号短整型数字节顺序转换成网络字节顺序
    
    
    
        my_addr.sin_addr.s_addr = htonl(INADDR_ANY);//htonl:将主机的无符合长整型数字节顺序转换成网络字节顺序。
    
    
    
        int listen_sock, new_sock;
    
    
    
        listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    
    
    
        //根据指定的地址族、数据类型和协议来生成一个套接字的描述字(listen_sock )
    
    
    
        //地址描述:AP_INET,指定socket类型:SOCK_STREAM,函数返回值为整型socket描述符。
    
    
    
        bind(listen_sock, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));//将一个套接字和一个本地地址与绑定在一起
    
    
    
        //listen_sock:socket描述符,(struct sockaddr *)&my_addr:指向sockaddr类型的指针
    
    
    
        listen(listen_sock, 5);
    
    
    
        //5:在请求队列中允许的最大请求数为5
    
    
    
        while(1)
    
    
    
        {
    
    
    
            new_sock = accept(listen_sock, NULL, NULL);
    
    
    
            //listen_sock:被监听的socket描述符
    
    
    
    
    
    
    
            send(new_sock, buffer1, strlen(buffer1), 0);
    
    
    
            //new_sock:用于传输数据的socket描述符,buffer:是一个指向要发送数据的指针,strlen(buffer):以字节为单位的数据的长度。
    
    
    
    
    
            bytes_recvd = recv(new_sock, buffer2, sizeof(buffer2), 0);
    
    
    
            printf("Server received message(%d bytes): %s
    ", bytes_recvd, buffer2);
    
    
    
            memset(buffer3, 0, sizeof(buffer3));
    
    
    
            bytes_recvd = recv(new_sock, buffer3, sizeof(buffer3), 0);
    
    
    
            m=getnumberofwords(buffer3);
    
    
    
            memset(buffer4, 0, sizeof(buffer4));
    
    
    
            sprintf(buffer4,"%d",m);
    
    
    
            send(new_sock, buffer4, strlen(buffer4), 0);
    
    
    
        }
    
    
    
    close(new_sock);
    
    
    
    close(listen_sock);
    
    
    
    //WSACleanup();
    
    }
    
    
    
    
    
    
    
    int getnumberofwords(char readfile[65536])
    
    {
    
        int i,n=0,flag=1;
    
        for(i = 0; readfile[i]; i ++)
    
        {
    
            if(flag == 1)
    
            {
    
                if(readfile[i] != ' ')
    
                {
    
                    n++;
    
                    flag = 0;
    
                }
    
            }
    
            else if(readfile[i] == ' ')
    
                flag = 1;
    
        }
    
        return n;
    
    }
    
    
    
    //客户端
    #include <sys/types.h>  
    
    
    
    #include <sys/socket.h>  
    
    
    
    #include <netinet/in.h>  
    
    
    
    #include <arpa/inet.h>  
    
    
    
    #include <stdio.h>
    
    
    
    #include <stdlib.h>
    
    
    
    #define MY_PORT 155228
    
    
    
    void readfile (char readfile[65536]);
    
    
    
    
    
    int main() {
    
    
    
    
    
    
    
        char buffer1[1024]="connect to server failed!";
    
    
    
        char *buffer2;
    
    
    
        char buffer3[65536]="";
    
    
    
        char buffer4[1024]="";
    
    
    
        int n;
    
    
    
        int bytes_recvd;
    
    
    
    
    
    
    
        //WSADATA wsaData;
    
    
    
        //WSAStartup(MAKEWORD(1,1),&wsaData);
    
    
    
    
    
    
    
        struct sockaddr_in remote_addr;
    
    
    
    	remote_addr.sin_family = AF_INET;
    
    
    
    	remote_addr.sin_port = htons(MY_PORT);
    
    
    
    	remote_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    
    
    
    
    
    
    	int conn_sock,new_sock;
    
    
    
    
    
    
    
    	conn_sock = socket(AF_INET, SOCK_STREAM, 0);
    
    
    
    	connect(conn_sock, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr));
    
    
    
    
    
    
    
    	 bytes_recvd = recv(conn_sock, buffer1, sizeof(buffer1), 0);
    
    
    
         printf("Client received message(%d bytes): %s
    ", bytes_recvd, buffer1);
    
    
    
    
    
    
    
         buffer2="client connected to server successful!";
    
    
    
         send(conn_sock, buffer2, strlen(buffer2), 0);
    
    
    
         //conn_sock:用于传输数据的socket描述符,buffer:是一个指向要发送数据的指针,strlen(buffer):以字节为单位的数据的长度。
    
         
    
    
    
             memset(buffer3, 0, sizeof(buffer3));
    
             
    
             readfile(buffer3);
    
    
    
             //printf("client:");
    
    
    
             //gets(buffer3);
    
    
    
             send(conn_sock, buffer3, strlen(buffer3), 0);
    
    
    
             memset(buffer4, 0, sizeof(buffer4));
    
    
    
             bytes_recvd = recv(conn_sock, buffer4, sizeof(buffer4), 0);
    
    
    
             printf("number of words:%s
    ", buffer4);
    
    
    
    	close(new_sock);
    
    
    
    	close(conn_sock);
    
    
    
    	//WSACleanup();
    
    
    
    	return 0;
    
    
    
    }
    
    
    
    void readfile (char readfile[65536])
    
    {
    
        FILE *fp;
    
        char ch;
    
        int i,count;
    
        if((fp=fopen("/home/besti20155228/20155228代码备份/1114exp3/test1.txt","rb"))==NULL)
    
        {
    
            printf("failed to open file!
    ");
    
            exit(0);
    
        }
    
        count=0;
    
        while((ch=fgetc(fp))!=EOF && count<1024)
    
        {
    
            if(isprint(ch))
    
            {
    
                readfile[count]=ch;
    
                count++;
    
            }
    
        }
    
        fclose(fp);
    
        return 0;
    
    }
    
    

    运行结果截图

    多线程同步互斥机制

    • 在主线程中初始化锁为解锁状态
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, NULL);
    
    • 在编译时初始化锁为解锁状态
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//锁初始化 
    
    • 访问对象时的加锁操作与解锁操作
    pthread_mutex_lock(&mutex)//加锁
    
    pthread_mutex_unlock(&mutex)//释放锁
    

    锁保护的并不是共享变量(或者说是共享内存),对于共享的内存而言,用户是无法直接对其保护的,因为那是物理内存,无法阻止其他程序的代码访问。事实上,锁之所以对关键区域进行了保护,是因为所有线程都遵循了一个规则,那就是在进入关键区域钱加同一把锁,在退出关键区域钱释放同一把锁

    加锁是会带来额外的开销的,加锁的代码其运行速度,明显比不加锁的要慢一些,所以,在使用锁的时候,要合理,在不需要对关键区域进行保护的场景下,不要画蛇添足,为其加锁了

    信号量

    锁有一个很明显的缺点,那就是它只有两种状态:锁定与不锁定。

    信号量本质上是一个非负数的整数计数器,它也被用来控制对公共资源的访问。当公共资源增加的时候,调用信号量增加函数sem_post()对其进行增加,当公共资源减少的时候,调用函数sem_wait()来减少信号量。其实是可以把锁当作一个0-1信号量的。

    它们是在/usr/include/semaphore.h中进行定义的,信号量的数据结构为sem_t, 本质上,它是一个long型整数

    在使用semaphore之前,我们需要先引入头文件#include <semaphore.h>

    初始化信号量: int sem_init(sem_t *sem, int pshared, unsigned int value);
    成功返回0,失败返回-1

    参数

    • sem:指向信号量结构的一个指针
    • pshared: 不是0的时候,该信号量在进程间共享,否则只能为当前进程的所有线程们共享
    • value:信号量的初始值
    • 信号量减1操作,当sem=0的时候该函数会堵塞 int sem_wait(sem_t *sem);
      成功返回0,失败返回-1

    参数

    • sem:指向信号量的一个指针
      信号量加1操作 int sem_post(sem_t *sem);
      参数与返回同上
      销毁信号量 int sem_destroy(sem_t *sem);
      参数与返回同上
    
    #include <stdio.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <semaphore.h>
    
    #define MAXSIZE 10
    
    int stack[MAXSIZE];
    int size = 0;
    sem_t sem;
    
    // 生产者
    void provide_data(void) {
        int i;
        for (i=0; i< MAXSIZE; i++) {
            stack[i] = i;
            sem_post(&sem); //为信号量加1
        }
    }
    
    // 消费者
    void handle_data(void) {
        int i;
        while((i = size++) < MAXSIZE) {
            sem_wait(&sem);
            printf("乘法: %d X %d = %d
    ", stack[i], stack[i], stack[i]*stack[i]);
            sleep(1);
        }
    }
    
    int main(void) {
    
        pthread_t provider, handler;
    
        sem_init(&sem, 0, 0); //信号量初始化
        pthread_create(&provider, NULL, (void *)handle_data, NULL);
        pthread_create(&handler, NULL, (void *)provide_data, NULL);
        pthread_join(provider, NULL);
        pthread_join(handler, NULL);
        sem_destroy(&sem); //销毁信号量
    
        return 0;
    }
    

    多线程服务器代码

    #include <stdio.h>  
    #include <stdlib.h>  
    #include <string.h>  
    #include <unistd.h>  
    #include <sys/types.h>  
    #include <sys/socket.h>  
    #include <netinet/in.h>  
    #include <arpa/inet.h>  
    #include <pthread.h>  
      
    #define PORT 5228
    #define BACKLOG 5  
    #define MAXDATASIZE 1000  
      
    void process_cli(int connfd, struct sockaddr_in client);  
    void *function(void* arg);  
    struct ARG {  
    int connfd;  
    struct sockaddr_in client;  
    }; 
      
    main()  
    {  
    int listenfd,connfd;  
    pthread_t  tid;  
    struct ARG *arg;  
    struct sockaddr_in server;  
    struct sockaddr_in client;  
    socklen_t  len;  
      
    if ((listenfd =socket(AF_INET, SOCK_STREAM, 0)) == -1) {  
    perror("Creatingsocket failed.");  
    exit(1);  
    }  
      
    int opt =SO_REUSEADDR;  
    setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));  
      
    bzero(&server,sizeof(server));  
    server.sin_family=AF_INET;  
    server.sin_port=htons(PORT);  
    server.sin_addr.s_addr= htonl (INADDR_ANY);  
    if (bind(listenfd,(struct sockaddr *)&server, sizeof(server)) == -1) {  
    perror("Bind()error.");  
    exit(1);  
    }  
      
    if(listen(listenfd,BACKLOG)== -1){  
    perror("listen()error
    ");  
    exit(1);  
    }  
      
    len=sizeof(client);  
    while(1)  
    {  
    if ((connfd =accept(listenfd,(struct sockaddr *)&client,&len))==-1) {  
    perror("accept() error
    ");  
    exit(1);  
    }  
    arg = (struct ARG *)malloc(sizeof(struct ARG));  
    arg->connfd =connfd;  
    memcpy((void*)&arg->client, &client, sizeof(client));  
      
    if(pthread_create(&tid, NULL, function, (void*)arg)) {  
    perror("Pthread_create() error");  
    exit(1);  
    }  
    }  
    close(listenfd);  
    }  
      
    void process_cli(int connfd, struct sockaddr_in client)  
    {  
    int num;  
    char recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];  
      
    printf("Yougot a connection from %s. 
     ",inet_ntoa(client.sin_addr) );  
    num = recv(connfd,cli_name, MAXDATASIZE,0);  
    if (num == 0) {  
    close(connfd);  
    printf("Clientdisconnected.
    ");  
    return;  
    }  
    cli_name[num - 1] ='';  
    printf("Client'sname is %s.
    ",cli_name);  
      
    while (num =recv(connfd, recvbuf, MAXDATASIZE,0)) {  
    recvbuf[num] ='';  
    printf("Receivedclient( %s ) message: %s",cli_name, recvbuf);  
    int i;  
    for (i = 0; i <num - 1; i++) {  
    if((recvbuf[i]>='a'&&recvbuf[i]<='z')||(recvbuf[i]>='A'&&recvbuf[i]<='Z'))  
    {  
    recvbuf[i]=recvbuf[i]+ 3;  
    if((recvbuf[i]>'Z'&&recvbuf[i]<='Z'+3)||(recvbuf[i]>'z'))  
    recvbuf[i]=recvbuf[i]- 26;  
    }  
    sendbuf[i] =recvbuf[i];  
    }  
    sendbuf[num -1] = '';  
    send(connfd,sendbuf,strlen(sendbuf),0);  
    }  
    close(connfd);  
    }  
      
    void *function(void* arg)  
    {  
    struct ARG *info;  
    info = (struct ARG*)arg;  
    process_cli(info->connfd,info->client);  
    free (arg);  
    pthread_exit(NULL);  
    }  
    

    运行结果截图

    参考资料

    linux中wc命令用法
    C语言统计单词个数
    Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

  • 相关阅读:
    进阶新的阶段--LCD
    UART的调试
    s5pv210的定时器
    s5pv210的外部中断
    按键的轮询
    点亮指路灯
    队列里面的二级指针
    链表实现学生成绩管理系统
    链表基本功能
    new的用法
  • 原文地址:https://www.cnblogs.com/besti20155228/p/7858382.html
Copyright © 2020-2023  润新知