不同计算机之间实现生产者-消费者问题

     问题描述:针对的是操作系统中的生产者-消费者问题,方式是在不同计算机间实现(这里实在linux下实现的)

     涉及的主要知识有网络间通信、信号量和pv操作;

    

     agui1_2produce.c一台计算机中运行,agui1_2consume.c可以在其他计算机中运行 (注:一台计算机也是可以的)

    编译命令 :gcc agui1_2produce.c -o agui1_2produce -lpthread

                         gcc agui1_2consume.c -o agui1_2consume -lpthread

    运行命令:假设有两台计算机:computerA,computerB

                        1.在computerA(假设ip地址是192.168.10.57)下运行:./agui1_2produce 8888           

                        2.在computerB下运行::./agui1_2consume 192.168.10.57 8888

                        当只有一台计算机时:运行上面两个命令时,可以把ip地址改成127.0.0.1 

                        查看ip的命令ip -s addr

     实验结果:可以比较生成者的生成和消费者的消费是否满足生产者-消费者问题的要求     

   下面是实验代码,更加详细的实验代码和实验结果描述在:    http://pan.baidu.com/s/1dDs3uHb             

agui1_2produce.c


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<errno.h>
#include<pthread.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/sem.h>
#include<signal.h>


#define Maxbuf 10//存放产品的缓冲区的大小


struct Circlebuf{
  int read; //读取产品的位置
  int write;//存放产品的位置
  int buf[Maxbuf];//存放产品的数据空间
} circlebuf;


//向 Circlebuf *c中写入 *value的值
//即使用中想circlebuf中存放产品,注意点有write的加一
void writeCirclebuf(struct Circlebuf*c,int *value){
     c->buf[c->write]=*value;
     c->write=(c->write+1)%Maxbuf;
}
//从Circlebuf *c中取出产品
//注意的是read的加一
int readCirclebuf(struct Circlebuf* c){
   int value;
   value=c->buf[c->read];
   c->buf[c->read]=0;
   c->read=(c->read+1)%Maxbuf;
   return value;
}
//遍历一下Circlebuf*c中产品的数值,不改变read write 的值
void overCirclebuf(struct Circlebuf*c){
   int i;
   printf("********************缓冲区各单元的值:");
   for(i=0;i<Maxbuf;i++){
        printf("%d ",c->buf[i]);
   }
   printf("\n");  
}


#define SEM_KEY 4009
struct sembuf semaphore;
int semid;
//信号量的初始化
//初始化三个信号量


int initSembuf(){
    int sem=0;
    if((semid=semget(SEM_KEY,4,IPC_CREAT|0666))>=0){
        sem=1;    
        semctl(semid,0,SETVAL,sem); //第一个信号量 mutex 初始值1
        sem=Maxbuf;
        semctl(semid,1,SETVAL,sem);//第二个信号量 empth 初始值10
        sem=0;
        semctl(semid,2,SETVAL,sem);//第三个信号量 full 初始值 0
        printf("initsembuf success!!\n");
        return 1;
    }else{   
        return 0;
    }
}   
void pmutex(){
    semaphore.sem_num=0;
    semaphore.sem_op=-1;
    semaphore.sem_flg=SEM_UNDO;
    semop(semid,&semaphore,1);
}
void vmutex(){
    semaphore.sem_num=0;
    semaphore.sem_op=1;
    semaphore.sem_flg=SEM_UNDO;
    semop(semid,&semaphore,1);
}
void pempty(){
    semaphore.sem_num=1;
    semaphore.sem_op=-1;
    semaphore.sem_flg=SEM_UNDO;
    semop(semid,&semaphore,1);
}
void vempty(){
    semaphore.sem_num=1;
    semaphore.sem_op=1;
    semaphore.sem_flg=SEM_UNDO;
    semop(semid,&semaphore,1);
}
void pfull(){
    semaphore.sem_num=2;
    semaphore.sem_op=-1;
    semaphore.sem_flg=SEM_UNDO;
    semop(semid,&semaphore,1);
}
void vfull(){
    semaphore.sem_num=2;
    semaphore.sem_op=1;
    semaphore.sem_flg=SEM_UNDO;
    semop(semid,&semaphore,1);
}
//删除信号量
void sigend(int sig){
    semctl(semid,IPC_RMID,0);
    exit(0);
}
//生产者
//注意的是while(1)循环 可以改成循环30次 
//只要保证问题能够说明即可,实际中不会使用那么多循环的,否则浪费端口资源
void *produce(void *i){
    int *n=i;    
    while(1){
       pempty();
       pmutex();
         writeCirclebuf(&circlebuf,n);
         printf("+++++++++++++++生产者%d写入缓冲区的值=%d\n",*n,*n);
         overCirclebuf(&circlebuf);
         int j;
         for(j=0;j<50000000;j++){
         }
       vmutex();
       vfull();
    }
}
//消费者
//这个函数在是消费者线程中实现的
//目的是向发起链接的进程发送产品,每次发送12个产品
//发送的方式是使用文件描述符,这个文件描述符是从TCP通信的accept函数中得到的(参数*temp就是那个文件描述符)
void *consume(void *temp){
   int *newsockfd=temp;
   int i;
   char str[100];
   char receivedNum[100];
   int nbytes;
   int num; 
   if((nbytes=read(*newsockfd,receivedNum,100))==-1){
      printf("read error:%s\n",strerror(errno));
      exit(1);
   }
   if((num=atoi(receivedNum))<0){
         puts("receivedNum error!!");
         exit(1);
   }   
   
   int value=0;
   
   for(i=0;i<num;i++){
   pfull();
   pmutex();
    printf("||| one |||"); 
     value=readCirclebuf(&circlebuf);
      printf("--------------消费者取走的产品值为:%d\n",value);
      memset(str,0,sizeof(str));
      if(0!=value){
         sprintf(str,"%d",value);
         if(write(*newsockfd,str,sizeof(str))==-1){
            printf("write error:%s\n",strerror(errno));
            exit(1);
         }
      }else{
         puts("none");
      }
   vmutex();
   vempty();
   } 
   close(*newsockfd);
}


int main(int argc,char *argv[]){
   //网络通信准备
   // 主要步骤是socket(),bind(),listen(),另外accept()是取得申请者的文件描述符,如果没有申请进程,那么运行到那里时会阻塞,直到有申请进程发起链接
    int portnumber;//生产者使用的端口号
    int sockfd,newsockfd;//文件描述符,sockfd是服务的使用的,newsockfd是针对申请进程的文件描述符,
    struct sockaddr_in server_addr,client_addr;
    int addr_len=sizeof(struct sockaddr_in);
   
    if(argc!=2){
         puts("argument error!!");
         exit(1);
    }
    //把argv[1]字符串转换成数值(端口号)
    if((portnumber=atoi(argv[1]))<0){
         puts("portnumber error!!");
         exit(1);
    }  
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
         printf("socket error:%s\n",strerror(errno));
         exit(1);
    } 
    printf("create server socket success!! socket id:%d\n",sockfd); 
    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);
    if(bind(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr))==-1){
          printf("bind error:%s\n",strerror(errno));
          exit(1);
    }
    printf("bind port success!! local port:%d\n",portnumber); 
    if(listen(sockfd,10)==-1){
          printf("listen error:%s\n",strerror(errno));
          exit(1);
    }
    printf("listening....\n");
   


   //pv操作准备
    while(!initSembuf());
    signal(SIGINT,sigend);
    signal(SIGTERM,sigend);
   //pro
    int i; 
    pthread_t proid;
    printf("进行生产\n");
    int *addr_data=(int*)malloc(22*sizeof(int));
    //开启了20个生产者线程,其中addr_data[]是传送给produce()函数的参数
    for(i=1;i<21;i++){
         addr_data[i-1]=i;
        pthread_create(&proid,NULL,produce,&addr_data[i-1]);
    }  
   //consu
   //不断的接受申请进程的链接
   //接受的时候创建消费者线程,取产品发送给申请进程
  //没有申请进程发起链接时,accept()阻塞,等待申请
   while(1){
      sleep(4);   
   puts("begin accept:::::");   
   if((newsockfd=accept(sockfd,(struct sockaddr*)(&client_addr),&addr_len))==-1){
                printf("accept error:%s\n",strerror(errno));
                exit(1);
         }
         printf("server get connection from:%s %d\n",inet_ntoa(client_addr.sin_addr),client_addr.sin_port);  
         pthread_t id;
          //newsockfd是针对申请进程的文件描述符
         pthread_create(&id,NULL,consume,&newsockfd);
         
         for(i=0;i<5000000;i++){
         }
      sleep(3);
   }
    sleep(8);

    close(sockfd);
    return 0;
}



agui1_2consume.c


/*
  编译时带上-lpthread 例如:gcc pcwNew.c -o pcwNew -lpthread
  使用方式 : ./pcwNew localhost 8888
  (localhost 是你要访问的服务机 8888是服务机端口号)
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<errno.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<netdb.h>
int main(int argc,char*argv[]){
   int sockfd;
   struct hostent *hostname;
   int portnumber;
   struct sockaddr_in server_addr;
   char buffer[1024];
   
   if(argc!=3){
     puts("argument error!!");
     exit(1);
   }
   //获取主机
   if((hostname=gethostbyname(argv[1]))==NULL){
       printf("get hostname error!!");
       exit(1);
   }
   //获取要访问的端口号
   if((portnumber=atoi(argv[2]))<0){
       printf("portnumber error!!");
       exit(1);
   }
  
   if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
       printf("create socket error:%s\n",strerror(errno));
       exit(1);
   }
   printf("create client socket success! socket id:%d\n",sockfd);


   
   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*)hostname->h_addr);
    //获取文件描述符 
   if(connect(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr))==-1){
      printf("conncet error:%s\n",strerror(errno));
      exit(1);
   }
   int nbytes;
   int i=0;
  
  char charnums[100]; 
  puts("please input how much products you want:");
  scanf("%s",charnums);
  int num;
  if((num=atoi(charnums))<0){
    puts("charnums error");
    exit(1);
  }
  if(write(sockfd,charnums,sizeof(charnums))==-1){
     printf("write error:%s\n",strerror(errno));
     exit(1);
  }
  
 for(i=0;i<num;i++){  
   if((nbytes=read(sockfd,buffer,100))==-1){
         printf("read error:%s\n",strerror(errno));
         exit(1);
      }
      buffer[nbytes]='\0';
      printf("received:%s\n",buffer);
      int j;
      for(j=0;j<500000;j++){
      } 
      sleep(1);
  }   
   
   close(sockfd); 
   return 0;
}






















































  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值