Linux 命名管道 聊天室

歡迎大家點擊鏈接,查看本項目的 ✌說明文檔

一、功能說明

實現的功能

  1. 客戶端之間的羣聊功能已經實現
  2. 實現了使用用戶名標識的私聊功能
  3. 某個客戶端退出後,其餘客戶端依然可以正常通信
  4. 優化了客戶端界面
  5. 錄製了測試視頻並投稿至B站

具體操作內容概述
本程序代碼繼承自上一次嵌入式作業,主要做了以下改動

  • 客戶端
  1. 將握手函數拆分爲數據發送函數和數據接受函數,提高函數複用率。
  2. 使用父子進程的方式實現了羣聊功能,實現了避免產生孤兒進程的功能。
  3. 編寫數據過濾函數,將私信數據的目標PID和私信消息過濾。
  • 服務器
  1. 服務器將現存客戶端數量、客戶端的PID以及用戶名都做記錄
  2. 編寫服務器的數據發送函數,能夠實現不將數據發給發來數據的客戶端,此部分代碼已被註釋(覺得暫時沒用)。實現了私發功能。

二、Bug集中營

2.1 關於羣聊的問題

  • 問題描述
    剛剛遇到一個問題: 當第二個客戶端連接進來之後,服務器就會依次向所有客戶端發送相關信息。但是,此時第一個客戶端正處在發送數據的情況下。也就是說,此時如果客戶端1不能發送數據給服務器,那麼此時服務器發給客戶端1的信息就會被阻塞。這就導致其他所有客戶端都不能接收到服務器的此條信息了。

  • 問題原因
    該問題最根本的原因是,客戶端的讀寫是順序執行的,他只能寫完數據之後纔能有機會讀數據。這就導致他寫(公有管道)數據的時候,讀(私有管道)被阻塞了。

  • 解決方案
    如果要解決該問題,我覺得 必須 有必要採用父子進程的方式。這樣就會解決讀寫順序的問題。

PS: 本以爲該問題很棘手,需要大量修改代碼 😵
沒想到,加上父子進程後,輕鬆解決 😜~~~

  • 需要注意的問題
    在父子進程的程序中,需要注意避免產生孤兒進程和殭屍進程。本程序中,父進程用來向服務器寫入數據,子進程用來從服務器讀出數據。如果客戶端想要退出程序,父進程首先向服務器發送CLIENT_QUIT消息,然後箱子進程發送SIGSTOP信號,終止子進程執行。這樣可以避免子進程變爲孤兒進程或殭屍進程。

下面附一張子進程變成孤兒進程的截圖:
孤兒進程

2.2 關於私聊的問題

  • 如何實現兩個客戶端的私聊?
    若要實現客戶端之間的私聊,可以在客戶端發送的數據前加入報頭,比如(to: [Client_PID] [Client_Message])。這樣,就有兩種處理方式:

    • 方式一、在客戶端判斷是否爲私聊
      客戶端自身判斷一下前幾個字符是否爲固定報頭。如果是的話,就截取目標PID和待發送的消息,同時用自己結構體中的成員標誌記錄下目標PID。服務器收到消息後,判斷結構體的成員標記。如果是私聊,就單獨發送;否則就向所有客戶端循環廣播發送數據。
    • 方式二、在服務器判斷是否爲私聊
      客戶端加上包頭之後,直接將所有數據發送至服務器。服務器接收到消息後,判斷報頭是否爲私聊報頭,進而作出相應處理。
      爲了減輕服務器的數據處理負擔,我選擇在客戶端處理數據。
  • 關鍵問題
    問題的關鍵在於如何正確地切分數據。看了看C語言的正則表達式。。。我還是自己寫函數吧。這裏的問題還是很多的,主要是一些細節問題。比如,進程的PID最多隻有5位數,如果用戶輸入了錯誤的PID(長度超過5位數),如何進行位數限制;還有,假如用戶沒有嚴格按照規定格式輸入,而是輸入了一些無效的空格,怎樣提高數據分割的容錯性。等等問題。我寫了個函數,實現了以下功能:

    • 能夠按照規定格式正確切分客戶端PID和客戶端的有效消息
    • 能夠忽略客戶端所有無效的空格字符
    • 能夠限制目標PID在5位數以內
      客戶端函數的目的,是能將私信目標PID和有效數據分隔開,只要能實現數據分割就可以了。至於判斷目標PID是否有效,可以在服務器端實現。
  • 如何判斷私信目標PID是否有效?
    現在的想法是,服務器讀取私有管道文件,如果私有管道不存在,則拒絕通信。其實目前已經實現了該效果。如果服務器循環遍歷之後找不到對應文件名的客戶端的私有管道文件,那就什麼都不做。

2.3 關於用戶名的問題

昨天已經實現了羣聊和私聊的功能,但是在私聊時需要輸入對方客戶端的PID識別號,很不方便。目前的想到的解決方法是:在每一個客戶端啓動時,首先要求用戶輸入它的用戶名,私聊時以用戶名作爲客戶端的標識。

  • 問題關鍵
    如果使用用戶名作爲標識的話,需要在客戶端發送的結構體數據中,添加用戶名的成員變量。在私聊時,將數據分割方式由分割PID換成分割用戶名。這樣其實會簡單一些,因爲不用再去判斷PID和消息內容了。
    調了一會,現在功能已經實現了。這部分的功能實現,因爲與之前的思路(用客戶端PID作爲標識)有不同的地方,所以修改起來還是比較麻煩的,修改的代碼比較多。

  • 解決方案

  1. 客戶端啓動之後,就要求用戶輸入用戶名。該用戶名被保存在客戶端的結構體成員變量中,發給服務器。
  2. 在客戶端進行數據的過濾,如果是私發信息,就將私發對象的用戶名和要私發的信息拆分開來,分別存儲到結構體成員變量中。如果不是私發信息,就在相應的結構體變量中添加標識。
  3. 服務器收到客戶端的結構體數據之後,將該客戶端的PID和用戶名,分別保存在兩個數組中。
  4. 服務器通過檢測結構體中的target_name,來判斷是否該數據需要私發。如果需要私發,就執行私發代碼;否則執行循環發送代碼。
  5. 私發的情況下,服務器首先需要在用戶名數組中,循環查找一下是否存在該用戶。如果存在該用戶,那麼可以向對方發送消息,同時獲取該用戶在用戶結構體中的座標(它是第幾個用戶)。根據這個目標用戶的座標,就可以找到與它對應的PID,進而可以打開私有管道,只對其私發信息。
  6. 同樣,通過在用戶PID數組中循環查找發送消息的用戶位置,可以找到與其對應的用戶名。進而可以知道,是那個用戶發出的消息。

PS: 我自認爲在解決該問題中,比較巧妙地用到了 用戶名數組和用戶PID數組的下標一致性 這樣的特點。

2.4 關於客戶端界面的問題

2.4.1 時間顯示問題

本來是想用QT做一個界面,但估計我短時間做不出來。。。
那就先把客戶端的終端界面搞一搞。模仿QQ的聊天室界面,在用戶輸入消息之後,需要在每個消息之前加上信息發送的時間。

  • 問題分析

給客戶端的每條消息加上時間,也有兩種解決方式。

  1. 客戶端自己將時間信息加到消息結構體中,用結構體成員保存時間信息。
  2. 由服務器加上時間前綴。服務器回覆每條消息前,都在信息前面加上當前時間作爲消息前綴。
  • 解決方式
    經過我的測試,發現QQ是由服務器統一顯示的時間,大概是爲了統一信息接收時間。但是我想要減輕一下服務器的負擔,所以就由客戶端自身寫入當前時間。
    時間的顯示比較容易,直接調用庫函數就可以了。

2.4.2 握手消息問題

  • 問題分析
    每個客戶端在啓動之後,首先就會向服務器發送一個握手消息,以便服務器爲其創建私有管道。但是現在每次的握手消息會被髮送至所有客戶端,只顯示握手消息的內容的話不太友好。所以需要改變一下握手信息的顯示方式。

  • 解決方式
    處理方式也很簡單,就是在每次服務器羣發數據之前,都事先判斷一下當前的客戶端是不是新來的客戶端。如果是新的客戶端,就改變消息發送的內容。

2.5 關於客戶端退出的問題

2.5.1 客戶端退出後,服務器如何向其他用戶通知

  • 問題描述

現在的程序邏輯是這樣的:當某個客戶端要退出時,需要向服務器發送一個CLIENT_QUIT信息。服務器收到該信息後,切斷該客戶端的私有管道,刪除私有管道文件。然後客戶端數量的標識Client_Number減一。就完事了。
如果是現在這樣處理的話,當某個客戶端退出後,其他用戶是不知道該客戶端的退出的。這就不太好。。。

  • 解決方案

我的解決方案是:當某個客戶端退出後,服務器同樣做之前的動作(切斷該客戶端的私有管道、刪除私有管道文件、客戶端數量減一),同時,服務器也要向所有客戶端廣播消息,告訴其他用戶,該客戶端已退出。這裏的關鍵問題就是如何判斷客戶端退出,我是設了一個標識位Quit_Flag,用標識位做判斷,控制服務器廣播的內容。具體內容可以看一下Server_Send_Message()函數。

2.5.2 如何保證某個客戶端退出後,其他客戶端信息接收的有效性

  • 問題描述

在解決第一個問題後,一個隱藏的問題就被暴露出來了。由於程序中對客戶端信息的存儲,是順序排放在數組中的,而當某個客戶端退出後,該位置的信息沒有被刪除,客戶端數量卻減了一。這就導致,循環發送信息時,最後加進來的客戶端,一定收不到信息。簡言之,某個客戶端退出後,後面客戶端對廣播消息的接受會有異常。

  • 解決方案

之所以有這樣的問題,根本原因是,服務器沒有把已退出客戶端的信息刪除。解決辦法也很簡單,當某個客戶端退出後,服務器把Client_PID_Box數組以及Client_Name_Box數組中的內容重新排序。實際上是,把該客戶端後面的所有客戶端信息,都往前移動一位。我寫了個簡單的Delete_Client_Data函數,實現了該功能。

三、效果展示

目前已錄製了兩個視頻,分別是Linux_命名管道_聊天室_測試視頻Linux_命名管道_聊天室_測試視頻_V2.0

在這裏插入圖片描述

四、程序代碼

源代碼遵循 GNU General Public License v3.0 開源許可協議

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章