18_server消息接收与发送分离

为server创建新的线程负责发送数据

目的:

  1. 并发性:如果服务器需要同时处理多个客户端的请求,使用单独的线程可以提高并发性。这样,一个线程可以专注于接收数据,而另一个线程可以专注于发送数据,从而避免了在单个线程中处理I/O操作时可能出现的阻塞。
  2. 性能:对于需要高吞吐量的服务器,使用单独的线程可以减少等待时间和上下文切换,从而提高性能。当一个线程等待网络操作完成时,另一个线程可以继续处理其他任务。
  3. 简化设计:在某些情况下,使用单独的线程可以简化服务器的设计。例如,如果发送和接收操作之间有复杂的依赖关系,将它们分开处理可能会更容易管理。
  4. 资源利用:在多核处理器上,使用多线程可以更好地利用系统资源,因为不同的线程可以在不同的核心上运行。

和之前的生产消费者模型一样,服务器收到数据后,把任务分给发送线程处理

1.创建了CELLTask.hpp文件

/*
    v1.0
*/
#ifndef _CELL_TASK_H_

#include<thread>
#include<mutex>
#include<list>

//任务类型-基类,把任务本身抽象为一个基类,不同的任务可以继承该类,并重写自己的dotask方法
class CellTask
{
public:
    CellTask()
    {

    }

    //虚析构
    virtual ~CellTask()
    {

    }
    //执行任务
    virtual void doTask()
    {

    }
private:

};

//执行任务的服务类型
class CellTaskServer 
{
private:
    //任务数据,拿到任务后,就遍历每个任务,让任务运行起来dotask
    std::list<CellTask*> _tasks;
    //任务数据缓冲区
    std::list<CellTask*> _tasksBuf;
    //改变数据缓冲区时需要加锁
    std::mutex _mutex;
public:
    //添加任务
    void addTask(CellTask* task)
    {
        std::lock_guard<std::mutex> lock(_mutex);
        _tasksBuf.push_back(task);
    }
    //启动工作线程
    void Start()
    {
        //线程
        std::thread t(std::mem_fn(&CellTaskServer::OnRun),this);
        t.detach();
    }
protected:
    //工作函数
    void OnRun()
    {
        while (true)
        {
            //从缓冲区取出数据
            if (!_tasksBuf.empty())
            {
                std::lock_guard<std::mutex> lock(_mutex);
                for (auto pTask : _tasksBuf)
                {
                    _tasks.push_back(pTask);
                }
                _tasksBuf.clear();
            }
            //如果没有任务
            if (_tasks.empty())
            {
                std::chrono::milliseconds t(1);
                std::this_thread::sleep_for(t);
                continue;
            }
            //处理任务
            for (auto pTask : _tasks)
            {
                pTask->doTask();
                delete pTask;
            }
            //清空任务
            _tasks.clear();
        }

    }
};
#endif // !_CELL_TASK_H_

2.在server端添加任务类

//网络消息发送任务
class CellSendMsg2ClientTask:public CellTask   //添加发送消息的任务类型
{
    ClientSocket* _pClient;
    DataHeader* _pHeader;
public:
    CellSendMsg2ClientTask(ClientSocket* pClient, DataHeader* header)
    {
        _pClient = pClient;
        _pHeader = header;
    }

    //执行任务
    void doTask()
    {
        _pClient->SendData(_pHeader);  //全部都在堆空间上创建,消息用完就释放
        delete _pHeader;
    }
};

3.在cellserver中添加CellTaskServer成员变量和添加发送任务方法

private:
    //...
    CellTaskServer _taskServer;

public:
void addSendTask(ClientSocket* pClient, DataHeader* header)
{
    CellSendMsg2ClientTask* task = new CellSendMsg2ClientTask(pClient, header);
    _taskServer.addTask(task);
}

4.在主服务器中事件类的OnNetMsg处理消息的方法中

class MyServer : public EasyTcpServer
{
public:
    virtual void OnNetMsg(CellServer* pCellServer, ClientSocket* pClient, DataHeader* header)
    {
        EasyTcpServer::OnNetMsg(pCellServer, pClient, header);
        switch (header->cmd)
        {
        case CMD_LOGIN:
        {
            //send recv 
            Login* login = (Login*)header;
            //printf("收到客户端<Socket=%d>请求:CMD_LOGIN,数据长度:%d,userName=%s PassWord=%s\n", cSock, login->dataLength, login->userName, login->PassWord);
            //忽略判断用户密码是否正确的过程
            //LoginResult ret;
            //pClient->SendData(&ret);
            LoginResult* ret = new LoginResult();
            pCellServer->addSendTask(pClient, ret);
        }//接收 消息---处理 发送   生产者 数据缓冲区  消费者 
        break;
        case CMD_LOGOUT:
        {
            Logout* logout = (Logout*)header;
            //printf("收到客户端<Socket=%d>请求:CMD_LOGOUT,数据长度:%d,userName=%s \n", cSock, logout->dataLength, logout->userName);
            //忽略判断用户密码是否正确的过程
            //LogoutResult ret;
            //SendData(cSock, &ret);
        }
        break;
        default:
        {
            printf("<socket=%d>收到未定义消息,数据长度:%d\n", pClient->sockfd(), header->dataLength);
            //DataHeader ret;
            //SendData(cSock, &ret);
        }
        break;
        }
    }

};

这里,

  1. easytcpserver作为生产者,cellserver作为消费者
  2. cellserver作为生产者,CellTaskServer作为消费者

但是1没有使用统一的格式

现在的server收发流程

18.1

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top