网络资源的初始化与释放(C++ RAII惯用法)

本文介绍了C++中利用RAII(Resource Acquisition Is Initialization)原则进行网络资源管理,并结合单例模式确保资源的唯一性和正确释放。通过CServerSocket类的实例,展示了如何在构造时初始化套接字,使用CHelper类确保在程序结束时释放资源。这种方式避免了资源的重复初始化和泄露,保证了程序的健壮性。
摘要由CSDN通过智能技术生成

1. 网络资源的初始化与释放(C++ RAII惯用法)

C++ RAII 惯用法

RAII (Resource Acquisition Is Initialization)资源获取即初始化

我们拿到资源的时候就已经初始化,一旦不需要该资源,该资源就会被释放

资源:

在 C++ 的语境下,资源代表一些可以必须先被获取才能使用的对象。例如堆内存是资源,文件句柄是资源,互斥锁也是资源。

千言万语,比不上一段代码:

ServerSocket.h

class CServerSocket
{
public:
	static CServerSocket* getInstance()
	{
		if(m_instance == nullptr)
		{
			m_instance = new CServerSocket();
		}
		return  m_instance;
	}
	bool InitSocket()
	{
		
		if (m_sock == -1) return false;

		sockaddr_in serv_adr;

		memset(&serv_adr, 0, sizeof(serv_adr));

		serv_adr.sin_family = AF_INET;
		serv_adr.sin_addr.S_un.S_addr = INADDR_ANY;
		serv_adr.sin_port = htons(18888);

		//绑定
		if (bind(m_sock, (sockaddr*)&serv_adr, sizeof(sockaddr_in) ) == -1) return false;

	
		//TODO: 返回值

		if( listen(m_sock, 5) == -1) return false;
		return true;
		
	}

	bool AcceptClient()
	{
		sockaddr_in client_adr;
		int cli_sz = sizeof(client_adr);
		m_client = accept(m_sock, (sockaddr*)&client_adr, &cli_sz);
		if (m_client == -1) return  false;
		return true;
		
	}

	int DealCommand()
	{
		if (m_client == -1)
		{
			return -1;
		}
		char* buffer = new char[4096];
		memset(buffer, 0, 4096);
		size_t index = 0;
		while(true)
		{
			size_t len = recv(m_client, buffer +index, sizeof(buffer) - index, 0);
			if(len <= 0 )
			{
				return -1;
			}
			//TODO: 处理命令
			index += len;
			len = index;
			m_packet =  CPacket ((BYTE*)buffer, len);

			if(len > 0)
			{
				memmove(buffer, buffer + len, 4096 - len);
				index -= len;
				return m_packet.sCmd;
			}
			
		}
		return -1;
	}

	bool Send(const char* pData,size_t nSize)
	{
		if (m_client == -1) return false;
		return  send(m_client, pData, nSize, 0) > 0;

	}
	bool Send( CPacket& pack)
	{
		if (m_client == -1) return false;
		return  send(m_client, pack.Data(), pack.size(), 0) > 0;

	}

	CServerSocket& operator=(const CServerSocket& ss) = delete;
	CServerSocket(const CServerSocket&) = delete;
private:
	SOCKET m_sock;
	SOCKET m_client;
	CPacket m_packet;
	
	CServerSocket():m_client(INVALID_SOCKET)
	{	
		
		if(InitSockEnv() == FALSE)
		{
			MessageBox(NULL, _T("无法初始化套接字环境,请检查网络设置"), _T("初始化错误"), MB_OK | MB_ICONERROR);
			exit(0);
		}
		m_sock = socket(PF_INET, SOCK_STREAM, 0);
	};
	~CServerSocket()
	{
		closesocket(m_sock);
		WSACleanup();
	};

	BOOL InitSockEnv()
	{

		WSADATA data;
		if(WSAStartup(MAKEWORD(1, 1), &data) != 0)
		{
			return  FALSE;
		}
		return TRUE;
		//TODO: 返回值处理
	}

	static  CServerSocket* m_instance;
	static  void releaseInstance()
	{
		if(m_instance != NULL)
		{
			CServerSocket* tmp = m_instance;
			m_instance  = NULL;
			delete tmp;
		}
	}
	
	class CHelper
	{
	public:
		CHelper()
		{
			CServerSocket::getInstance();
		}
		~CHelper()
		{
			CServerSocket::releaseInstance();
		}
	};
	static CHelper m_helper;
};

ServerSocket.cpp

CServerSocket* CServerSocket::m_instance = NULL;
CServerSocket::CHelper CServerSocket::m_helper;
CServerSocket* pserver = CServerSocket::getInstance();

main.cpp

CServerSocket* pserver =  CServerSocket::getInstance();
            int count = 0;

            if (pserver->InitSocket() == false)
            {
                MessageBox(NULL, _T(""), _T("网络初始化失败"), MB_OK | MB_ICONERROR);
                exit(0);
            }
        	
            while (pserver != NULL)
            {
                if (pserver)
                {
                    
                    if (pserver->AcceptClient() == false)
                    {
                        if (count >= 3) {
                            MessageBox(NULL, _T("多次无法正常接入,程序退出"), _T("接入用户失败"), MB_OK | MB_ICONERROR);
                            exit(0);
                        }
                        MessageBox(NULL, _T("无法正常接入用户,自动重试"), _T("接入用户失败"), MB_OK | MB_ICONERROR);
                        count++;
                    	
                    }
                }
                int ret = pserver->DealCommand();

以上就是RAII的一种实现,但是又采用单例模式(下文讲解)

我们在构造函数CServerSocket()调用InitSockEnv()获取并初始化网络资源,在进入main函数后,获取实例

main()函数结束后,应该调用析构函数,但事实上调用不了,因为此时全局数据区中,存储的并不是一个实例对象,而是一个实例对象的指针,即一个地址变量而已!实例对象呢?在堆区,因为是通过new得来的!虽然这样能够减小全局数据区的占用,把实例对象这一大坨都放到堆区。

所以我你们需要一个CHelper类,来帮助我们关闭套接字,释放网络资源我们在Server.cpp文件中创建了一个CHelper的静态对象,当main()函数结束时,由于m_helper对象在全局数据区,所以会调用CHelper的析构函数,从而调用CServerSocket::releaseInstance()帮助我们释放资源

main()函数开始时获取并初始化网络资源,在main()函数结束时,释放回收网络资源,这就是RAII

因为网络资源在整个程序运行时.只需要初始化一次,释放一次,所以``CServerSocket`采用单例模式,这样就避免了重复初始化

C++11采用智能指针也可以实现单例模式,以后可能会介绍

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值