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及多线程的同步与互斥)

原文地址:https://www.cnblogs.com/besti20155228/p/7858382.html