zoukankan      html  css  js  c++  java
  • 一步一步创建聊天程序1-利用进程和共享内存来创建简易聊天程序

        最近学习了linux关于进程间通信的相关知识,所以决定借助进程和共享内存,并按照生产者消费者模型来创建一个简易聊天程序。下面简单的说一下程序的思路。
        首先是服务端程序,服务端会创建两个进程,进程1负责接收客户端传送过来的消息,并存储起来。进程2负责读取进程1存取的消息。这里使用到了生产者和消费者编程模型,并声明了如下的消息存放结构体。

    struct message
    {
        int target_id;
        char buf[100];
    };
    
    //消息仓库
    struct canku
    {
        struct message recv_message[10];
        int read_pos,write_pos;
    }messages;
     

    可以看到,我在这里强制消息仓库最多只能存放10条消息(其实只能存放9条,和生产者消费者模型有关)。
        生产者和消费者模型:利用read_pos和write_pos来分别标记读取和写入的位置,每次读取和写入后,read_pos或write_pos都会递增1,当read_pos或write_pos到达数组末尾时,就将其重置为0,类似于一个环形链表,可这会出现一个问题:如何判断链表是否为空,或链表是否已经被写满了?
        利用read_pos(消息读取的位置)和write_pos(消息写入的位置)来判断消息仓库是否为空和已满。消息仓库在空和满两种状态时,read_pos与write_pos都相等,这样明显不满足我们的要求。所以,空出一个消息位,来利用如下条件判断消息是否空或满。

    1. 消息为空时
      if((messages_ptr->read_pos)%10==messages_ptr->write_pos)
      {
          //printf("buff is empty
      ");
          shmdt(messages_ptr);
          continue;
      }
      即当read_pos和write_pos值相等时,则表示消息仓库为空状态。
    2. 消息仓库已满时
      if((messages_ptr->write_pos+1)%10==messages_ptr->read_pos)
      {
          shmdt(messages_ptr);
          continue;
      }
       
      即当write_pos下一个位置为read_pos时,此时仓库已满。可以看到,该消息空间无法存储数据,被浪费了。这样的方法就解决了如何判断消息仓库空状态和满状态的问题,但是却浪费了一个消息空间。

        同时,我们还借助于共享内存,将消息仓库映射到共享内存上,这样,进程1和进程2都可以访问消息仓库。

        客户端比服务端简单,我创建了一个子进程用来读取服务端转发的其他客户端发来的消息。父进程则用来读取客户输入,将消息发送到服务器。

        代码如下:
      服务端:

      

    #include <stdio.h>
    #include <pthread.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <errno.h>
    
    #define MAX_LISTEN 10
    
    struct message
    {
        int target_id;
        char buf[100];
    };
    
    //消息仓库
    struct canku
    {
        struct message recv_message[10];
        int read_pos,write_pos;
    }messages;
    
    //messages初始化
    void init()
    {
        messages.read_pos=0;
        messages.write_pos=0;
    }
    
    //messages销毁
    void finish()
    {
        messages.read_pos=0;
        messages.write_pos=0;
    }
    
    int sd;
    
    int main()
    {
        init();
        struct sockaddr_in server_ip,customer_ip;
        int err;
        
        sd=socket(AF_INET,SOCK_STREAM,0);
        if(sd==-1)
        {
            printf("socket failed
    ");
            close(sd);
            return -1;
        }
    
        //server_ip初始化
        server_ip.sin_family=AF_INET;
        server_ip.sin_port=htons(5678);
        server_ip.sin_addr.s_addr=htonl(INADDR_ANY);
        memset(server_ip.sin_zero,0,8);
    
        err=bind(sd,(struct sockaddr *)(&server_ip),sizeof(struct sockaddr));
        if(err==-1)
        {
            printf("bind failed
    ");
            close(sd);
            return -1;
        }
    
        err=listen(sd,MAX_LISTEN);
        if(err==-1)
        {
            printf("listen failed
    ");
            close(sd);
            return -1;
        }
    
        int length=sizeof(customer_ip);
    
        //初始化共享变量,大小等于canku by luke
        int shmid=shmget(IPC_PRIVATE,sizeof(struct canku),IPC_CREAT|0777);
        if(shmid<0)
        {
            printf("shmget failed
    ");
            return -1;;
        }
    
        struct canku * messages_ptr=&messages;
    
        while(1)
        {
            int temp_cd=accept(sd,(struct sockaddr *)(&customer_ip),&length);
            if(temp_cd==-1)
            {
                printf("accept failed,ereno: %d
    ",temp_cd);
                close(sd);
                return -1;
            }
    
            printf("user %d online
    ",temp_cd);
    
            
            pid_t pid=fork();
            if(pid==0)//子进程 by luke
            {
                while(1)
                {
                    messages_ptr=shmat(shmid,NULL,0);
                    if((messages_ptr->write_pos+1)%10==messages_ptr->read_pos)
                    {
                       shmdt(messages_ptr);
                       continue;
                    }
    
                    struct message temp_message;
                    err=recv(temp_cd,&temp_message,sizeof(struct message),0);
                    //err=read(temp_cd,&(recv_message[count-1]),sizeof(struct message));
                    if(err!=-1)
                    {
                        messages_ptr->recv_message[messages_ptr->write_pos].target_id=temp_message.target_id;
                        strcpy(messages_ptr->recv_message[messages_ptr->write_pos].buf,temp_message.buf);
                        printf("recv: read_pos: %d, write_pos: %d, target_id: %d, buf: %s
    ",messages_ptr->read_pos,messages_ptr->write_pos+1,messages_ptr->recv_message[messages_ptr->write_pos].target_id,messages_ptr->recv_message[messages_ptr->write_pos].buf);
                        messages_ptr->write_pos++;
                        if(messages_ptr->write_pos==9)
                            messages_ptr->write_pos=0;
                    }
    
                    shmdt(messages_ptr);
                }
            }
            
            //防止主线程被while住,无法接受新的连接请求。
            pid_t pid1=fork();
            if(pid1==0)
            {
                while(1)
                {
                    messages_ptr=shmat(shmid,NULL,0);
                   if((messages_ptr->read_pos)%10==messages_ptr->write_pos)
                   {
                       //printf("buff is empty
    ");
                       shmdt(messages_ptr);
                       continue;
                   }
                    
                    //strcpy(messages_ptr->recv_message[messages_ptr->read_pos].buf,"hello");
                    err=send(messages_ptr->recv_message[messages_ptr->read_pos].target_id,messages_ptr->recv_message[messages_ptr->read_pos].buf,100,0);
                    if(err==-1)
                    {
                        //printf("send failed
    ");
                    }
                    else
                    {
                        printf("send: read_pos: %d, write_pos: %d ,message.target_id: %d, message.buf: %s
    ",messages_ptr->read_pos+1,messages_ptr->write_pos,messages_ptr->recv_message[messages_ptr->read_pos].target_id,messages_ptr->recv_message[messages_ptr->read_pos].buf);
                        messages_ptr->read_pos++;
                        if(messages_ptr->read_pos==9)
                            messages_ptr->read_pos=0;
                    }
                    shmdt(messages_ptr);
                }
            }
        }
    
        close(sd);
        shmctl(shmid,IPC_RMID,NULL);
        finish();
        return 0;
    }

    客户端:

    #include <stdio.h>
    #include <pthread.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    struct message
    {
        int target_id;
        char buf[100];
    };
    
    int sd;
    struct message send_message;
    
    void * read_message(void * argv)
    {
        while(1)
        {
            //读服务器发来的消息
            char revBuf[100];
            read(sd,revBuf,100);
            printf("recevice from server: %s",revBuf);
        }
    }
    
    void * write_message(void * argv)
    {
        while(1)
        {
            printf("input message: 
    ");
            memset(send_message.buf,0,128);
            send_message.target_id=-1;
            scanf("%d %s",&send_message.target_id,send_message.buf);
    
            write(sd,&send_message,sizeof(send_message));
            sleep(3);
        }
    }
    
    int main()
    {
        struct sockaddr_in server_ip,customer_ip;
        int err;
    
        sd=socket(AF_INET,SOCK_STREAM,0);
        if(sd==-1)
        {
            printf("socket failed
    ");
            close(sd);
            return -1;
        }
    
        //server_ip初始化
        server_ip.sin_family=AF_INET;
        server_ip.sin_port=htons(5678);
        server_ip.sin_addr.s_addr=htonl(INADDR_ANY);
        //err=inet_aton("115.157.201.179",&server_ip.sin_addr.s_addr);
        memset(server_ip.sin_zero,0,8);
    
        err=connect(sd,(struct sockaddr *)(&server_ip),sizeof(server_ip));
        if(err==-1)
        {
            printf("connect failed
    ");
            close(sd);
            return -1;
        }
    
        pid_t pid=fork();
        if(pid==0)
        {
            while(1)
            {
                //读服务器发来的消息
                //printf("read message: 
    ");
                char revBuf[100];
                recv(sd,revBuf,100,0);
                //read(sd,revBuf,100);
                printf("recevice from server: %s
    ",revBuf);
            }
        }
        while(1)
        {
            printf("input message: 
    ");
            memset(send_message.buf,0,128);
            send_message.target_id=-1;
            scanf("%d %s",&send_message.target_id,send_message.buf);
    
            if(send_message.target_id!=-1&&(strcmp(send_message.buf,"")!=0))
            {
    
                err=send(sd,&send_message,sizeof(send_message),0);
                if(err==-1)
                {
                    printf("send failed
    ");
                }
                //write(sd,&send_message,sizeof(send_message));
    
                send_message.target_id=-1;
                memset(send_message.buf,0,sizeof(send_message.buf));
            }
    
            sleep(3);
        }
    
        close(sd);
        return 0;
    }
  • 相关阅读:
    【C/C++】【类和对象】计算类所占的字节数
    【算法】【单调栈】单调栈
    【算法】【字符串】C语言常用库函数实现
    【算法】【字符串】Leetcode哈希表相关高频面试题
    ubuntu20.04安装测试uhttpd
    华为Mate14上安装Ubuntu20.04纪要
    shell判断参数值是否在数组内的方法
    降低PDF质量
    Ubuntu 16.04上安装Global阅读源代码工具
    Linux下好用的屏幕录像软件kazam及截图软件shutter
  • 原文地址:https://www.cnblogs.com/JsonZhangAA/p/11904768.html
Copyright © 2011-2022 走看看