C++服务开发入门指南
  • 序言
  • 前言
  • 一、一个简单的服务
    • 1 什么是服务
    • 2 服务可以用来做什么
    • 3 简单服务框架
  • 二、网络通信服务框架
    • 1 网络服务的基本概念
    • 2 增加监听端口
    • 3 处理客户端会话
    • 小结
  • 三、添加基础模块
    • 1 日志模块
    • 2 定时器
    • 3 事件机制
    • 4 线程池
    • 5 线程安全
    • 小结
  • 四、一个聊天服务
    • 1 需求描述及分析
    • 2 概要设计
    • 3 创建服务项目
    • 4 ClientUser实现
    • 5 RoomMgr实现
    • 6 ChatRoom实现
    • 7 RoomIDMgr实现
    • 小结
  • 五、测试、迭代及重构
    • 1 测试
    • 2 迭代
    • 3 重构
    • 4 版本号
  • 六、架构设计
    • 1 单点服务
    • 2 分布式服务
  • 七、部署及发布
    • 1 部署环境
    • 2 编译环境
    • 3 部署服务
    • 4 发布服务
  • 八、线上问题处理
    • 1 线上问题
    • 2 问题处理
  • 九、程序员的职业规划
    • 职业规划
Powered by GitBook
On this page
  1. 二、网络通信服务框架

2 增加监听端口

Previous1 网络服务的基本概念Next3 处理客户端会话

Last updated 2 years ago

首先,基于CServiceBase类,派生一个CNetService类

CNetService新增了一个成员变量,用于保存监听端口;增加了一个CreateUser方法,用于业务层创建对应的连接对象,其参数为nOrigin,即来源编号,由业务层约定。

NetService在重写基类函数OnStart时,启动一个服务线程,处理服务的监听事件。

#pragma once
#include "ServiceBase.h"

class CLinkUser;

class CNetService :
    public CServiceBase
{
public:
    CNetService();
    virtual ~CNetService();

public:
	virtual void ServerListenThread();
    virtual void OnStart();

public:
    virtual CLinkUser* CreateUser(int nOrigin);

protected:
    int m_nPort;
};
#include "NetService.h"
#include <thread>
#include <utility>
#include "AsioServer.h"

CNetService::CNetService()
	: m_nPort(20001)
{
}

CNetService::~CNetService()
{
}

void CNetService::ServerListenThread()
{
	asio::io_context io_context;
	CAsioServer svr(io_context, m_nPort);
	io_context.run();
}

void CNetService::OnStart()
{
	std::thread th(&CNetService::ServerListenThread, this);
	th.detach();
}

CLinkUser* CNetService::CreateUser(int nOrigin)
{
	return nullptr;
}

在ServerListenThread函数中,我们使用了Asio的封装类AsioServer,io_context作为参数,传入到CAsioServer的构造函数,后面执行io_context.run(),启动了服务监听。

下面我们看,AsioServer的实现

#pragma once
#define ASIO_STANDALONE
//https://think-async.com/Asio/
#include "asio.hpp"
#include "TCPSession.h"
#include <iostream>
class CAsioServer
{
public:
	CAsioServer(asio::io_context& io_context, int nPort)
		: m_acceptor(io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), nPort))
	{
		OnAccept();
	}

private:
	void OnAccept()
	{
		m_acceptor.async_accept([&](std::error_code ec, asio::ip::tcp::socket socket) {
			if (!m_acceptor.is_open())
			{
				return;
			}

			if (!ec)
			{
				// todo something OnAccept by TCPSession
			}
			OnAccept();
		});
	}
private:
	asio::ip::tcp::acceptor m_acceptor;
};

AsioServer对Asio模块做了简单封装,成员变量m_acceptor在构造函数中接收了传入参数io_context和端口号,服务启动后会监听这个端口号。

svr在构造函数里注册了OnAccept函数,当客户端与服务的TCP连接建立完成后,会触发这个函数,执行acceptor的异步接入,同时启动新的OnAccept函数,等待下一个客户端接入。

此时在m_acceptor.async_accept注册的回调函数中,我们可以建立一个TCPSession,用于管理这个用户和服务端的交互信息。

完善此处的处理逻辑

        	if (!ec)
			{
				CTCPSession* pSession = new CTCPSession(socket);
				pSession->SetCreateUserFunc([this](int nOrigine)->CLinkUser*{
					if(m_CreateUserFunc){
						return m_CreateUserFunc(nOrigine);
					}
					return nullptr;
				});
				pSession->Start();
			}

TCPSession需要在接收客户端消息后,才知道消息发送者的身份,所以,需要将创建LinkUser的函数传递到TCPSession内部,在内部接收到消息后,创建对应的LinkUser。

同样AsioServer本身也不复杂创建,需要由NetService将函数传递进来,因此AsioServer需要增加相应变量和逻辑,完整的AsioServer.h为

#pragma once
#define ASIO_STANDALONE
//https://think-async.com/Asio/
#include "asio.hpp"
#include "TCPSession.h"
#include <iostream>
class CAsioServer
{
public:
	CAsioServer(asio::io_context& io_context, int nPort)
		: m_acceptor(io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), nPort))
	{
		OnAccept();
	}

	void SetCreateUserFunc(CreateFunc func){m_CreateUserFunc = func;}

private:
	void OnAccept()
	{
		m_acceptor.async_accept([&](std::error_code ec, asio::ip::tcp::socket socket) {
			if (!m_acceptor.is_open())
			{
				return;
			}

			if (!ec)
			{
				CTCPSession* pSession = new CTCPSession(socket);
				pSession->SetCreateUserFunc([this](int nOrigine)->CLinkUser*{
					if(m_CreateUserFunc){
						return m_CreateUserFunc(nOrigine);
					}
					return nullptr;
				});
				pSession->Start();
			}

			OnAccept();
		});
	}
private:
	asio::ip::tcp::acceptor m_acceptor;
	CreateFunc m_CreateUserFunc;
};

NetService在创建Svr时传入

void CNetService::ServerListenThread()
{
	asio::io_context io_context;
	CAsioServer svr(io_context, m_nPort);
	svr.SetCreateUserFunc([this](int nOrigine)->CLinkUser*{
					return CreateUser(nOrigine);
				});
	io_context.run();
}

至此,服务框架具有了监听端口的能力,接收客户端的TCP连接请求,并创建相应的会话,在下一节中,我们仔细观察TCPSession所做的处理。