一步一步建立聊天程序1-利用進程和共享內存來建立簡易聊天程序

    最近學習了linux關於進程間通訊的相關知識,因此決定藉助進程和共享內存,並按照生產者消費者模型來建立一個簡易聊天程序。下面簡單的說一下程序的思路。
    首先是服務端程序,服務端會建立兩個進程,進程1負責接收客戶端傳送過來的消息,並存儲起來。進程2負責讀取進程1存取的消息。這裏使用到了生產者和消費者編程模型,並聲明瞭以下的消息存放結構體。linux

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\n");
        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\n");
        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\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);

    //初始化共享變量,大小等於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)//子進程 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);
            }
        }
        
        //防止主線程被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\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;
}

客戶端:服務器

#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: \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初始化
    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)
        {
            //讀服務器發來的消息
            //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;
}
相關文章
相關標籤/搜索