之前在处理客户端多个socket连接服务端的程序运行时,例如1000个客户端;这1000个客户端socket连接到服务端需要花费相当长的时间。因此,本节对服务端的程序进行改进,使接受连接的select业务和接受发送数据的select业务分开。
1、生产者和消费者模型
生产者线程处理客户端连接的请求,并将新接入的客户端socket安排到消费者线程的处理队列中。
消费者线程,每个线程增加一个消息队列,用于缓冲是否有新客户端socket连接,对缓冲队列内的socket进行处理。
消息队列(缓冲队列)是一个临界资源。
生产者和消费者模型的特点:
(1)解耦合,生产者和消费者只依赖于缓冲区。
(2)支持并发和异步,生产者和消费者可以相互转换。
2、设计消费者线程类
首先,在之前的EasyTcpServer类中,我们删除对客户端网络数据收发处理的代码,保留接受客户端连接的代码;在EasyTcpServer类中,主要的成员函数是初始化socket:initSocket;绑定端口:Bind;监听端口:Listen;接受连接:Accept;Select模型判断是否有socket连接:OnRun;关闭Socket:_closeSocket;
其次,设计一个CellServer类,用来封装线程和网络数据收发处理及响应的功能;
在CellServer类中,我们封装一个缓冲区队列,用来接受主线程连入的socket,这也是消费者线程与生产者线程通信的数据结构;
void addClientToCellServer(ClientSocket *pClient) //生产者线程向全局变量,也就是缓冲区队列中加入连接来的客户端socket
{
_clients.push_back(pClient);
auto pMinServer = _cellServers[0];
for (auto pCellServer : _cellServers)
{
if (pCellServer->getClientCount() < pMinServer->getClientCount())
{
pMinServer = pCellServer;
}
}
pMinServer->addClient(pClient);
}
接着,封装Select网络模型,将已经连入这个消费者线程的socket查询是否有网络数据收发;然后封装接受数据函数RecvData和网络消息响应函数 OnnetMsg;这样也就实现了将服务端的功能(连入客户端、客户端网络收发)分离;
需要注