FTP文件管理項目(本地雲)項目日報(五)

由於上次說了改工期,兩天算一個工作日,所以昨天就沒有日報了。

統計一下這兩天團隊成員們都幹了啥,紀錄一下我都幹了啥。

上一波日報

1號 https://blog.csdn.net/qq_43762191/article/details/106870906
2號 https://blog.csdn.net/qq_45792305/article/details/106881268

有點尷尬啊,可能是其他人都等着開完會才寫日報,或者是寫完還沒過審,然後今天大家都太忙。。。

那就直接紀錄我自己吧。

我都進度

預期進度

測試完文件方面代碼
結果:沒碰。
實際進度:epoll模塊、進程間通信模塊、線程池模塊進行中,解壓包模塊依舊沒去測試。

爲什麼?這也不是理由,沒做就是沒做,我不喜歡找理由。
昨天下午參加了一個評審,我是評審嘉賓之一很開心。
今天突然收到噩耗明天要考毛概,期末考,所以我就抽出了今晚預留給解壓包模塊測試的時間去複習了。

進程間通信模塊設計(“分佈式服務器”設計(初體驗))

不好搞,是真的不好搞,還好我學過點皮毛的設計模式,還知道要用“中介者”。

在這裏插入圖片描述

中介者服務器(中控)採用accept,邊緣服務器全部採用connect,在連接成功後向中控中心彙報自己的情況(fd對應的服務器名是啥,方便通信),奈何我天資愚鈍,這個圖我想了一晚上。

中控服務器設計

在這裏插入圖片描述

代碼寫到一半就收到要考試的噩耗,所以任務隊列模塊還沒寫,就先不貼出來了。

線程池模塊(代碼測試完畢)

這個模塊也算是重新理解了一遍,順便整理了一篇關於線程池的博客,代碼註釋率達到百分八十,非常適合新手看
勉強還能看的線程池詳解

寫過博客呢,代碼思路就很清晰,兩個小時就連圖帶代碼全寫好了。

在這裏插入圖片描述

//Mutex.h

#pragma once

#include <stdio.h>
#include <pthread.h>
#include <errno.h>

class Mutex
{
public:
    Mutex();
    virtual ~Mutex();
    bool lock();
    bool unlock();
    bool trylock();
protected:
    pthread_mutex_t mutex;
};
//Mutex.cpp

#include "Mutex.h"

Mutex::Mutex()
{
    //    mutex = PTHREAD_MUREX_INITALIZER;  //ÕâÑù¾²Ì¬³õʼ»¯¶àºÃ
    if (pthread_mutex_init(&mutex, NULL) != 0)
    {
        perror("pthread mutex init error");
    }
}


Mutex::~Mutex()
{
    if (pthread_mutex_destroy(&mutex) != 0)
    {
        perror("pthread mutex destroy error");
    }
}

bool Mutex::lock()
{
    int ret = pthread_mutex_lock(&mutex);
    if (ret != 0)
    {
        perror("pthread mutex lock error");
    }
    return ret == 0 ? true : false;
}

bool Mutex::unlock()
{
    int ret = pthread_mutex_unlock(&mutex);
    if (ret != 0)
    {
        perror("pthread mutex unlock error");
    }
    return ret == 0 ? true : false;
}

bool Mutex::trylock()
{
    int ret = pthread_mutex_trylock(&mutex);
    if (ret != 0)
    {
        perror("pthread mutex trylock error");
    }
    return ret == 0 ? true : false;
}
//Cond.h

#pragma once
#include "Mutex.h"

class Cond : public Mutex
{
public:
    Cond();
    virtual ~Cond();
    bool wait();
    bool timewait(unsigned int sec);
    bool signal();
    bool broadcast();
    bool isTimeout();
private:
    pthread_cond_t cond;
    bool timeout;
};
//Cond.cpp

#include "Cond.h"

#include <sys/time.h>


Cond::Cond() :Mutex()
{
    timeout = false;

    if (pthread_cond_init(&cond, NULL) != 0)
    {
        perror("pthread cond init error");
    }
}

Cond::~Cond()
{
    if (pthread_cond_destroy(&cond) != 0)
    {
        perror("pthread cond destroy error");
    }
}

bool Cond::wait()
{
    int ret = pthread_cond_wait(&cond, &mutex);
    if (ret != 0)
    {
        perror("pthread cond wait error");
    }
    return ret == 0 ? true : false;
}

bool Cond::timewait(unsigned int sec)
{
    struct timespec abstime;
    clock_gettime(CLOCK_REALTIME, &abstime);
    abstime.tv_sec += sec;
    timeout = false;
    int ret = pthread_cond_timedwait(&cond, &mutex, &abstime);
    if (ret == ETIMEDOUT)
    {
        timeout = true;
        return false;
    }
    else if (ret != 0)
    {
        perror("pthread cond timedwait error");
    }
    return ret == 0 ? true : false;
}

bool Cond::signal()
{
    int ret = pthread_cond_signal(&cond);
    if (ret != 0)
    {
        perror("pthread cond signal error");
    }
    return ret == 0 ? true : false;
}


//¹ã²¥ÉÙÓÃ
bool Cond::broadcast()
{
    int ret = pthread_cond_broadcast(&cond);
    if (ret != 0)
    {
        perror("pthread cond broadcast error");
    }
    return ret == 0 ? true : false;
}

bool Cond::isTimeout()
{
    return timeout;
}
//Pthread_Pool.h

#pragma once

#include <pthread.h>
#include <unistd.h>
#include <list>	//據說list不安全,不安全就不安全吧,更不安全的都忍了
#include "Cond.h"	//封裝過的條件變量類,繼承自封裝的mutex鎖類,所以具有鎖和條件變量的雙重屬性

using namespace std;

class Task	//任務接口,每個任務必須實現的接口,以供工作線程調度任務的執行
{
public:
    Task() {}
    virtual ~Task() {}
    virtual int run() = 0; //留給子類實現
};

typedef list<Task*> list_task; //任務隊列,用於暫存等待處理的任務,等待線程喚醒時處理,提供一種緩衝機制。

class Pthread_Pool	//線程池類
{
public:
    Pthread_Pool(unsigned int max = 100, unsigned int min = 10, unsigned int wait = 60);
    ~Pthread_Pool();
    void addTask(Task* task);	// 往任務隊列中添加新線程

private:
    static void* taskThread(void* arg);// 工作線程
    void createThread();		// 新建一個線程
    void destroyThread();		// 銷燬一個線程池

    unsigned int maxcount;		// 最大線程數
    unsigned int mincount; 		// 最小線程數
    unsigned int count;	 		// 當前線程池中線程數
    unsigned int waitcount; 	// 等待線程數
    unsigned int waitsec;		// 等待時間
    list_task	 taskList;      //任務隊列
    Cond taskCond;    //任務鎖,線程接任務時使用
    Cond cond;        //線程鎖,創建線程時使用
    bool Stop;                  //線程池是否被允許運作,初始化線程池對象時置0,線程池銷燬時置爲1
};
//Pthread_Pool.cpp

#include "Pthread_Pool.h"

//開放接口1
Pthread_Pool::Pthread_Pool(unsigned int max, unsigned int min, unsigned int wait)
{
    //配置基本參數
    count = 0;		//當前線程池爲空
    waitcount = 0;  //沒有等待線程
    mincount = min;	//核心線程數(出廠配置)
    maxcount = max;	//最大線程數(能承受的最高配置)
    waitsec = wait;	//線程保活時長(過了時長還沒接到任務,那就裁掉)
    Stop = false;	//允許運作

    //上鎖,創建一定數量的線程作爲初始線程池
    cond.lock();
    for (unsigned i = 0; i < mincount; i++)
    {
        createThread();	//跳轉到這個函數的實現->->->->->
    }
    cond.unlock();
}

Pthread_Pool::~Pthread_Pool()
{
    destroyThread();	//銷燬線程池
}

void Pthread_Pool::createThread()
{
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, taskThread, (void*)this);
    //以執行taskThread()爲目的創建線程,跳轉到taskThread()函數的實現 ->->->->->

    if (ret < 0)
        perror("pthread create error");
    else
        count++;
}

// 工作線程
void* Pthread_Pool::taskThread(void* arg)
{
    pthread_detach(pthread_self()); //設置線程自分離屬性
    Pthread_Pool* pool = (Pthread_Pool*)arg;
    while (1)
    {
        pool->cond.lock();

        //如果沒有工作線程在等待
        if (pool->taskList.empty())
        {
            if (pool->Stop)	//當收到線程池停止運行的消息時
            {
                pool->count--;	//線程數減一
                pool->cond.unlock();
                pthread_exit(NULL); //本線程強制退出
            }

            pool->waitcount++;	//等待任務的線程數加一
            bool bSignal = pool->cond.timewait(pool->waitsec); //新任務等待被喚醒
            pool->waitcount--;	//沒等到,沒事幹,喝西北風了

            // 刪除無用線程
            if (!bSignal && pool->count > pool->mincount)	//如果沒事幹 && 有多餘線程
            {
                pool->count--;	//先裁員一個,不要一次做絕了,反正是在while循環裏面,沒事幹裁員機會多得是
                pool->cond.unlock();
                pthread_exit(NULL);
            }
        }
        pool->cond.unlock();	//記得要釋放鎖

//如果有工作線程在等待
        if (!pool->taskList.empty())
        {
            pool->taskCond.lock();	//上任務鎖
            Task* t = pool->taskList.front(); 	//獲取任務隊列中最前端的任務並執行
            pool->taskList.pop_front(); //移除被領取的任務
            pool->taskCond.unlock();//記得解鎖

            t->run(); //任務開始
            delete t; //弄完就刪了
        }
    }
    pthread_exit(NULL);
}

//開放接口2,向任務隊列中添加任務
void Pthread_Pool::addTask(Task* task)
{
    if (Stop)	//線程池是否停止工作
        return;

    //向任務隊列中添加新任務
    taskCond.lock();	//上任務鎖
    taskList.push_back(task);	//添加任務
    taskCond.unlock();	//記得解鎖

    cond.lock();	//上線程鎖
    if (waitcount)	//如果有空閒線程
    {
        cond.signal();	//喚醒一個線程
    }
    else if (count < maxcount)	//如果沒有空閒線程,一般來說,走到這裏面來,那這個線程池的設計是有點失敗了	
    {
        createThread();	//那就創建一個
        cond.signal();	//然後喚醒
    }
    cond.unlock();
}


void Pthread_Pool::destroyThread()
{
    printf("destroy?\n");

#if 0   //強行清理
    list_task::iterator it = taskList.begin();
    for (; it!= taskList.end(); it++)
    {
        Task* t = *it;
        delete t;

        t = NULL;
    }
    taskList.clear();
#endif

    // 等待所有線程執行完畢
    Stop = true;
    while (count > 0)
    {
        cond.lock();
        cond.broadcast();	//廣播
        cond.unlock();

        sleep(1);
    }
}

epoll連接模塊

這個模塊呢,因爲進程間通信未搭建成功,所以還沒完全竣工,epoll模塊是有現成了,就是配件未到位。
在這裏插入圖片描述

要看epoll的文章,這篇不錯,適合新手:epoll,求知者離我近點

對解壓包模塊對修訂

解壓包不採用同一個包對象,這是之前沒想到的,因爲解包時初始化的大小不一定適用於封包時的大小哦。
此外,協議中頭包的預留空間撥出一半給服務器名,用於邊緣服務器與中控通信。

//對頭包的修改

typedef struct packet_header_st
{
    int fd;//用於前後端通信即目標客戶端fd(服務器用到)
    int funcId; // 功能號
        //登錄包0x01,註冊包0x02,找回密碼0x03,修改密碼0x04

        //客戶端獲取文件列表0x11,上傳文件0x12,下載文件0x13,共享文件0x14,除獲取列表外各種文件業務應答0x15
        //心跳0x21
        //中介服務器信息填充0x30

    int optid; // 操作碼:請求0x00 和 應答0x01

    int usrlenth;// 包體的長度
    int packet_seq; //包序號
    int packet_sum; //包總數

    char to_fd[6];      //目標服務器名稱(填信息時爲本服務器名稱)
    char dstAddr[6]; //預留

    int syn; // 判斷包頭是否正確 0x04
}packet_header_t;
//解壓包處的修改
bool PacketBase::pack()
{
    Body = new char[Body_Size];
    memcpy(m_Data, &this->Head, sizeof(packet_header_t));
    memcpy(m_Data + sizeof(packet_header_t), this->Body, sizeof(Body)); //Õâ¸öBody³¤¶ÈÔÚ·â°üµÄʱºò¶¨ 
    memcpy(m_Data + sizeof(packet_header_t) + sizeof(Body), &this->Tail, sizeof(packet_tali_t));
   
    return true;
}

bool PacketBase::unpack()
{
    if (Body_Size <= 0) {   //Èç¹ûÊý¾Ý²»×ã
        std::cout << "Êý¾Ý°üÆÆËð" << std::endl;
        return false;
    }

    Body = new char[Body_Size];    //·ÀÖ¹Ô½½ç¿ÉÒÔÔÚÕâÀïÏÂÊÖ   
    memcpy(&this->Head, m_Data, sizeof(packet_header_t));   //ÏȽ«°üÍ·¶Á³ö
    memcpy(Body, m_Data + sizeof(packet_header_t), sizeof(Body));
    memcpy(&this->Tail, m_Data + sizeof(packet_header_t) + sizeof(Body), sizeof(packet_tali_t));
 
    return true;   
}

今天的分享就到這裏。

在這裏插入圖片描述

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