Create chat programs step by step 1 - Create simple chat programs using processes and shared memory

Posted by sirfartalot on Thu, 21 Nov 2019 05:44:13 +0100

linux recently learned about interprocess communication, so decided to create a simple chat program with the help of processes and shared memory and following the producer-consumer model.The following is a brief description of the program ideas.
The first is the server-side program, which creates two processes. Process 1 receives messages from clients and stores them.Process 2 is responsible for reading messages accessed by process 1.Producer and consumer programming models are used here, and the following message storage structures are declared.

struct message
{
    int target_id;
    char buf[100];
};

//Message Warehouse
struct canku
{
    struct message recv_message[10];
    int read_pos,write_pos;
}messages;

 

 

As you can see, here I'm forcing the message warehouse to store up to 10 messages (actually only 9, related to the producer-consumer model).
Producer and consumer models: Read_pos and write_pos are used to mark read and write locations, respectively. After each read and write, read_pos or write_pos is incremented by 1. When read_pos or write_pos reaches the end of the array, it is reset to 0, similar to a ring chain table, which presents a problem: how to determine if the chain table is empty or if the chain table has beenIs it full?
* Use read_pos (where messages are read) and write_pos (where messages are written) to determine if the message warehouse is empty and full.When the message warehouse is empty or full, both read_pos and write_pos are equal, which obviously does not meet our requirements.Therefore, a message bit is empty to use the following criteria to determine whether the message is empty or full.

  1. Message is empty
    if((messages_ptr->read_pos)%10==messages_ptr->write_pos)
    {
        //printf("buff is empty\n");
        shmdt(messages_ptr);
        continue;
    }

     

    That is, when the read_pos and write_pos values are equal, the message warehouse is empty.
  2. When the message warehouse is full
    if((messages_ptr->write_pos+1)%10==messages_ptr->read_pos)
    {
        shmdt(messages_ptr);
        continue;
    }

     

     
    That is, when the next location of write_pos is read_pos, the repository is full.As you can see, this message space is wasted because it cannot store data.This method solves the problem of how to judge whether the message warehouse is empty or full, but it wastes a message space.

At the same time, we also map the message warehouse to shared memory with the help of shared memory, so that process 1 and process 2 can access the message warehouse.

The client is simpler than the server, and I created a sub-process to read messages from other clients forwarded by the server.The parent process reads the client input and sends the message to the server.

The code is as follows:
Server:

  

#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];
};

//Message Warehouse
struct canku
{
    struct message recv_message[10];
    int read_pos,write_pos;
}messages;

//messages Initialization
void init()
{
    messages.read_pos=0;
    messages.write_pos=0;
}

//messages Destroy
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\n");
        close(sd);
        return -1;
    }

    //server_ip Initialization
    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\n");
        close(sd);
        return -1;
    }

    err=listen(sd,MAX_LISTEN);
    if(err==-1)
    {
        printf("listen failed\n");
        close(sd);
        return -1;
    }

    int length=sizeof(customer_ip);

    //Initialize shared variables,Size equals canku by luke
    int shmid=shmget(IPC_PRIVATE,sizeof(struct canku),IPC_CREAT|0777);
    if(shmid<0)
    {
        printf("shmget failed\n");
        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\n",temp_cd);
            close(sd);
            return -1;
        }

        printf("user %d online\n",temp_cd);

        
        pid_t pid=fork();
        if(pid==0)//Subprocess 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\n",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);
            }
        }
        
        //Prevent the main thread from being while Live, unable to accept new connection request.
        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\n");
                   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\n");
                }
                else
                {
                    printf("send: read_pos: %d, write_pos: %d ,message.target_id: %d, message.buf: %s\n",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;
}

Client:

#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)
    {
        //Read messages from server
        char revBuf[100];
        read(sd,revBuf,100);
        printf("recevice from server: %s",revBuf);
    }
}

void * write_message(void * argv)
{
    while(1)
    {
        printf("input message: \n");
        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\n");
        close(sd);
        return -1;
    }

    //server_ip Initialization
    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\n");
        close(sd);
        return -1;
    }

    pid_t pid=fork();
    if(pid==0)
    {
        while(1)
        {
            //Read messages from server
            //printf("read message: \n");
            char revBuf[100];
            recv(sd,revBuf,100,0);
            //read(sd,revBuf,100);
            printf("recevice from server: %s\n",revBuf);
        }
    }
    while(1)
    {
        printf("input message: \n");
        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\n");
            }
            //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;
}

Topics: Linux socket Programming