KBEngine网络代码分析

目录

Q:CBEngine使用什么网络模型?

一.epoll_create

二.epoll_ctl

三.epoll_wait

四.accept

五.recv

六.send

总结


先看看官网关于网络的问答

Q:CBEngine使用什么网络模型?

A:由于正式运营环境在Linux下,所以网络模型选择了epoll, 生产环境在Windows比较方便所以CBEngine也支持了Windows系统,但是网络模型只是简单的select。

看看epoll用到的API, epoll_create epoll_ctl  epoll_wait

一.epoll_create

直接搜索关键字 epoll_create,创建epoll实例


EpollPoller::EpollPoller(int expectedSize) :
	epfd_(epoll_create(expectedSize))
{
	if (epfd_ == -1)
	{
		ERROR_MSG(fmt::format("EpollPoller::EpollPoller: epoll_create failed: {}\n",
				kbe_strerror()));
	}
};

二.epoll_ctl

epoll_ctl, 设置epoll的事件

bool EpollPoller::doRegister(int fd, bool isRead, bool isRegister)
{
	struct epoll_event ev;
	memset(&ev, 0, sizeof(ev)); // stop valgrind warning
	int op;

	ev.data.fd = fd;

	// Handle the case where the file is already registered for the opposite
	// action.
	if (this->isRegistered(fd, !isRead))
	{
		op = EPOLL_CTL_MOD;

		ev.events = isRegister ? EPOLLIN|EPOLLOUT :
					isRead ? EPOLLOUT : EPOLLIN;
	}
	else
	{
		// TODO: Could be good to use EPOLLET (leave like select for now).
		ev.events = isRead ? EPOLLIN : EPOLLOUT;
		op = isRegister ? EPOLL_CTL_ADD : EPOLL_CTL_DEL;
	}

	if (epoll_ctl(epfd_, op, fd, &ev) < 0)
	{
		const char* MESSAGE = "EpollPoller::doRegister: Failed to {} {} file "
				"descriptor {} ({})\n";
		if (errno == EBADF)
		{
			WARNING_MSG(fmt::format(MESSAGE,
					(isRegister ? "add" : "remove"),
					(isRead ? "read" : "write"),
					fd,
					kbe_strerror()));
		}
		else
		{
			ERROR_MSG(fmt::format(MESSAGE,
					(isRegister ? "add" : "remove"),
					(isRead ? "read" : "write"),
					fd,
					kbe_strerror()));
		}

		return false;
	}

	return true;
}

三.epoll_wait

epoll_wait  等待事件

int EpollPoller::processPendingEvents(double maxWait)
{
	const int MAX_EVENTS = 10;
	struct epoll_event events[ MAX_EVENTS ];
	int maxWaitInMilliseconds = int(ceil(maxWait * 1000));

#if ENABLE_WATCHERS
	g_idleProfile.start();
#else
	uint64 startTime = timestamp();
#endif

	KBEConcurrency::onStartMainThreadIdling();
	int nfds = epoll_wait(epfd_, events, MAX_EVENTS, maxWaitInMilliseconds);
	KBEConcurrency::onEndMainThreadIdling();


#if ENABLE_WATCHERS
	g_idleProfile.stop();
	spareTime_ += g_idleProfile.lastTime_;
#else
	spareTime_ += timestamp() - startTime;
#endif

	for (int i = 0; i < nfds; ++i)
	{
		if (events[i].events & (EPOLLERR|EPOLLHUP))
		{
			this->triggerError(events[i].data.fd);
		}
		else
		{
			if (events[i].events & EPOLLIN)
			{
				this->triggerRead(events[i].data.fd);
			}

			if (events[i].events & EPOLLOUT)
			{
				this->triggerWrite(events[i].data.fd);
			}
		}
	}

	return nfds;
}

四.accept

accept,在这里等待连接

INLINE EndPoint * EndPoint::accept(u_int16_t * networkPort, u_int32_t * networkAddr, bool autosetflags)
{
	sockaddr_in		sin;
	socklen_t		sinLen = sizeof(sin);
	int ret = (int)::accept(socket_, (sockaddr*)&sin, &sinLen);

#if KBE_PLATFORM == PLATFORM_UNIX
	if (ret < 0) return NULL;
#else
	if (ret == INVALID_SOCKET) return NULL;
#endif

	EndPoint * pNew = EndPoint::createPoolObject(OBJECTPOOL_POINT);

	pNew->setFileDescriptor(ret);
	pNew->addr(sin.sin_port, sin.sin_addr.s_addr);
	
	if(autosetflags)
	{
		pNew->setnonblocking(true);
		pNew->setnodelay(true);
	}
	
	if (networkPort != NULL) *networkPort = sin.sin_port;
	if (networkAddr != NULL) *networkAddr = sin.sin_addr.s_addr;

	return pNew;
}

五.recv

接收消息的 recv 函数

INLINE int EndPoint::recv(void * gramData, int gramSize)
{
	if (isSSL())
		return SSL_read(sslHandle_, (char*)gramData, gramSize);

	return ::recv(socket_, (char*)gramData, gramSize, 0);
}

接收消息,从线程工作函数 到系统API调用堆栈

从这里可以看到消息的接收的运行在主线程上的

读取的数据存放到类 MemoryStream上的成员变量 std::vector<uint8> data_;上

int len = ep.recv(data() + wpos(), (int)(size() - wpos()));
 uint8 *data() { return &data_[0]; }

接收到的消息后面是如何处理的,去登录的比较上层的地方下个断点看看

红框内就是消息从recv 到 loginapp类

 经过一路分发,最终送到登录的逻辑处理函数

void Loginapp::login(Network::Channel* pChannel, MemoryStream& s)
{
	AUTO_SCOPED_PROFILE("login");

	COMPONENT_CLIENT_TYPE ctype;
	CLIENT_CTYPE tctype = UNKNOWN_CLIENT_COMPONENT_TYPE;
	std::string loginName;
	std::string password;
	std::string datas;
	bool forceInternalLogin = false;

	// 前端类别
	s >> tctype;
	ctype = static_cast<COMPONENT_CLIENT_TYPE>(tctype);
	
	// 附带数据
	s.readBlob(datas);

	// 帐号登录名
	s >> loginName;

	// 密码
	s >> password;

	loginName = KBEngine::strutil::kbe_trim(loginName);

在login内经过校验,最后通过网络发送给dbmgr进行数据库查询

	// 向dbmgr查询用户合法性
	Network::Bundle* pBundle = Network::Bundle::createPoolObject(OBJECTPOOL_POINT);
	(*pBundle).newMessage(DbmgrInterface::onAccountLogin);
	(*pBundle) << loginName << password;
	(*pBundle).appendBlob(datas);
	dbmgrinfos->pChannel->send(pBundle);

六.send

去API,send下个断点看看是如何一路走到这里的, 红框内就是login一路到send的调用。

调用API send的实现

INLINE int EndPoint::send(const void * gramData, int gramSize)
{
	if (isSSL())
		return SSL_write(sslHandle_, (char*)gramData, gramSize);

	return ::send(socket_, (char*)gramData, gramSize, 0);
}

总结

自此可以看到epoll的初始化,一条消息从recv进来,再到send出去的过程。整个过程都是在主线程内运行的。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值