一步一步創建聊天程式1-利用進程和共用記憶體來創建簡易聊天程式

来源:https://www.cnblogs.com/JsonZhangAA/archive/2019/11/21/11904768.html
-Advertisement-
Play Games

最近學習了linux關於進程間通信的相關知識,所以決定藉助進程和共用記憶體,並按照生產者消費者模型來創建一個簡易聊天程式。下麵簡單的說一下程式的思路。 首先是服務端程式,服務端會創建兩個進程,進程1負責接收客戶端傳送過來的消息,並存儲起來。進程2負責讀取進程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\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;
}

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。本文鏈接:https://blog.csdn.net/wzt_007/article/details/78622698通信分為四大步驟,握手、數據交換、準備結束、正式結束 如以下場景: 兩個陌生人 ...
  • 使用man手冊的方式,能大大加快開發速度,可能安裝的時候有些安裝不完整,下麵結合網路上搜索信息進行補充: $ sudo apt-get install manpages $ sudo apt-get install manpages-dev $ sudo apt-get install manpag ...
  • 軟鏈接的創建,刪除,修改 創建軟鏈接:ln -s【目標文件或目錄】【軟鏈接地址】 解釋:軟鏈接地址相當於快捷方式,目標文件或目錄才是真正的內容。【軟鏈接地址】指“快捷鍵”文件名稱,該文件是被指令創建的。【目標目錄】指軟連接指向的目標目錄。 [root@localhost ~]# ln -s /usr ...
  • 常操作linux系統的都會用到:ps -ef 命令,是一個非常強大的進程查看命令。 在使用Nginx時,那麼我想要看這個Nginx相關的進程,可以使用如下命令”: ps -ef | fgrep nginx 可以看到其安裝位置和配置文件。 讓你對當前應用的進程有詳細的瞭解,你可以經常使用該命令進行查看 ...
  • 1、ifconfig命令臨時配置IP地址 2、setup工具永久配置IP地址 3、修改網路配置文件 4、圖形界面配置IP地址 ifconfig命令臨時配置IP地址 主要的作用是查看網路信息,也可以臨時設置網卡IP地址 輸出的第一行信息: 首先標明瞭是乙太網和當前電腦的網卡的MAC地址 第二行信息: ...
  • OSI/ISO七層模型和TCP/IP四層模型 網路層協議和IP劃分 OSI的七層框架 物理層:設備之間的比特流的傳輸、物理介面、電氣特性等。 數據鏈路層:成幀、用MAC地址訪問媒介、錯誤檢測與修正。 網路層:提供邏輯地址、選路。 傳輸層:可靠與不可靠的傳輸、傳輸前的錯誤檢測、流量控。 會話層:對應用 ...
  • Ctrl + Alt + Fx Linux多用戶、多任務切換 ...
  • 一 Pod生命周期管理 1.1 Pod生命周期 Pod在整個生命周期過程中被系統定義瞭如下各種狀態。 狀態值 描述 Pending API Server已經創建該Pod,且Pod內還有一個或多個容器的鏡像沒有創建,包括正在下載鏡像的過程。 Running Pod內所有容器均已創建,且至少有一個容器處 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...