代码拉取完成,页面将自动刷新
#pragma once
#include <iostream>
#include <string>
#include <sys/select.h>
#include "Socket.hpp"
#include "Log.hpp"
using namespace Net_Work;
const static int gdefaultport = 8888;
const static int gbacklog = 8;
const static int num = sizeof(fd_set) * 8;
class SelectServer
{
void HandlerEvent(fd_set &rfds)
{
for (int i = 0; i < num; i++)
{
if (_rfds_array[i] == nullptr)
continue;
int fd = _rfds_array[i]->GetSockFd();
if (FD_ISSET(fd, &rfds)) // 判断是否是读准备就绪
{
if (fd == _listensock->GetSockFd()) // 监听fd建立新链接(链接就绪)
{
lg.LogMessage(Info, "get a new link\n");
std::string clientip;
uint16_t clientport;
// 这里已经不会再阻塞了,select已经检测到了listensock已经就绪了!!!
Socket *sock = _listensock->AcceptConnection(&clientip, &clientport);
if (!sock)
{
lg.LogMessage(Error, "accept error\n");
return;
}
lg.LogMessage(Info, "get a client, client info is# %s: %d, fd: %d\n", clientip.c_str(), clientport, sock->GetSockFd());
// 这里已经获取连接成功
// 连接已经建立好,不能确定对方是否发数据
// read?write?绝对不能!!!read 底层数据是否就绪时不确定的!谁清楚fd上面是否有读事件呢?select!
// 新链接fd到来的时候,要把新的fd, 想办法交给select托管 -- 只需要添加到数组_rfds_array中即可
int pos = 0;
for (; pos < num; pos++)
{
if (_rfds_array[pos] == nullptr)
{
_rfds_array[pos] = sock;
lg.LogMessage(Info, "get a new link, fd is : %d\n", sock->GetSockFd());
break;
}
}
// 数组满了
if (pos == num)
{
sock->CloseSockFd();
delete sock;
lg.LogMessage(Warning, "server is full...!\n");
}
}
else // 普通的读事件就绪
{
// 这里读取已经不会卡住
std::string buffer;
bool res = _rfds_array[i]->Recv(&buffer, 1024);
if (res)
{
lg.LogMessage(Info, "client say# %s\n", buffer.c_str());
buffer += ": 你好呀,少年";
_rfds_array[i]->Send(buffer);
buffer.clear();
}
else
{
lg.LogMessage(Warning, "client quit, maybe close or error, close fd : %d\n", _rfds_array[i]->GetSockFd());
_rfds_array[i]->CloseSockFd();
delete _rfds_array[i];
_rfds_array[i] = nullptr;
}
}
}
}
}
public:
SelectServer(int port = gdefaultport) : _port(port), _listensock(new TcpSocket())
{
}
void InitServer()
{
// 创建套接字+bind
_listensock->BuildListenSocketMethod(_port, gbacklog);
for (int i = 0; i < num; i++)
{
_rfds_array[i] = nullptr;
}
// 默认为listensock
_rfds_array[0] = _listensock.get();
}
void Loop()
{
_running = true;
while (_running)
{
// 所有的fd,都要交给select. listensock上面新连接,相当于读事件,有新连接,就等价于有新数据到来
// 首先不能直接accept,而是将listensock交给select。因为只有select有资格知道有没有IO事件就绪
fd_set rfds; // 可表示的位图是有上限的
FD_ZERO(&rfds);
int max_fd = _listensock->GetSockFd();
for (int i = 0; i < num; i++)
{
if (_rfds_array[i] == nullptr)
{
continue;
}
else
{
// 设置等待的文件fd
// 默认只有listensock
int fd = _rfds_array[i]->GetSockFd();
FD_SET(fd, &rfds); // 添加所有合法fd到rfds集合中
if (max_fd < fd) // 更新最大fd
{
max_fd = fd;
}
}
}
// 设定时间
struct timeval timeout = {0, 0};
PrintDebug();
// 最大的fd要能够动态更新
// rfds本质是一个输入输出型参数,rfds是在select调用返回的时候,不断被修改,所以,每次都要重置
int n = select(max_fd + 1, &rfds, nullptr, nullptr, /*&timeout*/ nullptr); // 返回准备就绪的fd,并设置到rfds中(也可能是之前设设置的fd准备就绪并返回)
switch (n)
{
case 0:
lg.LogMessage(Info, "select timeout..., last time: %u.%u\n", timeout.tv_sec, timeout.tv_usec);
break;
case -1:
lg.LogMessage(Error, "select error!!!\n");
break;
default:
// 正常的就绪的fd
// 第一次等待client建立连接
lg.LogMessage(Info, "select success, begin event handler, last time: %u.%u\n", timeout.tv_sec, timeout.tv_usec);
HandlerEvent(rfds); // 准备就绪的fd集合(读集合) _rfds_array: 3,4,5,6,7,8,9,10 -> rfds: 4,5,6
break;
}
}
_running = false;
}
void Stop()
{
_running = false;
}
void PrintDebug()
{
std::cout << "current select rfds list is : ";
for (int i = 0; i < num; i++)
{
if (_rfds_array[i] == nullptr)
continue;
else
std::cout << _rfds_array[i]->GetSockFd() << " ";
}
std::cout << std::endl;
}
~SelectServer()
{
}
private:
std::unique_ptr<Socket> _listensock;
int _port;
int _running;
// select 服务器要被正确设计,需要程序员定义数据结构,把所有的fd管理起来,往往是数组!
Socket *_rfds_array[num];
};
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。