在windows项目中如何设计TCP网络单例模式

目录

一、前言

二、设计单例模式

1.添加类CServerSocket

2.添加构造函数和析构函数

3.添加实例和获取实例的接口

4.实现CHelper类

三、在单例中添加网络功能函数

1.初始化网络函数环境InitSocketEnv()

2.初始化网络InitSocket()

3.接收客户端连接AcceptClient()

4.处理客户端消息DealCommand()

5.发送消息Send()

6.断开连接CloseClient()

7.声明套接字变量以及完善构造和析构函数

  四、结尾


一、前言

        在项目设计中,单例设计模式是一种非常常用的软件设计模式,有很多模块都会用到它,例如TCP网络模块以及数据库模块等等,它的好处在于它确保了一个类只有一个实例,并提供了一个全局访问点(通常是静态方法)来获取这个实例。这使得在整个应用程序中都可以轻松地访问该类的唯一实例,而无需在多个组件之间传递引用。

        它一共有以下几个特点:

        1.单例模式只有一个实例。是指整个项目全程只有而且只能创造一个实例对象

        2.单例类必须自己创建自己的唯一实例。是指在类的内部通过自己来创造实例。

        3.单例类必须给所有其他对象提供这一实例。是指提供一个全局的接口,让其他类来访问。

        以上就是单例设计模式的全部特点,下面我将会以windows中TCP网络服务器为例,来详细讲解单例模式的设计过程。

二、设计单例模式

1.添加类CServerSocket

        首先我们先在项目中添加一个CServerSocket类,以后我们添加网络类的功能都会在这个类中。

class CServerSocket
{
public: private:}
2.添加构造函数和析构函数

        这个过程的重点在于我们必须将构造函数和析构函数设为private私有,这是因为单例模式的特性:单例模式只有一个实例。如果我们将构造函数和析构函数变为public,这样其他类能够自己也能声明一个实例,就违反了原则所在。

class CServerSocket
{
public: private:CServerSocket(const CServerSocket& ss) {};CServerSocket() {}; ~CServerSocket() {};
};
3.添加实例和获取实例的接口

        这一步很重要,我们要在类的内部声明一个实例 m_instance,同时实现访问实例的接口函数getInstance()和释放实例的接口函数releaseInstance()

class CServerSocket
{
public: static CServerSocket* getInstance() {if (m_instance == NULL){m_instance = new CServerSocket();}return m_instance;};static void releaseInstance(){if (m_instance != NULL){CServerSocket* tmp = m_instance;m_instance = NULL;delete tmp;}}
private:// 静态成员变量static CServerSocket* m_instance;CServerSocket(const CServerSocket& ss) {};CServerSocket() {}; ~CServerSocket() {};
};

        同时需要在cpp文件中,去实现初始化。这是因为类的静态成员函数不同于类内部的函数,我们需要在类的内部声明,同时在类的外部初始化。

#include "ServerSocket.h"// 静态成员变量的初始化
CServerSocket* CServerSocket::m_instance = NULL;
CServerSocket* pserver = CServerSocket::getInstance();
4.实现CHelper类

        CHelp类的作用是帮助m_instance实例进行释放作用,主要作用是调用releaseInstance()接口。

class CServerSocket
{
public: static CServerSocket* getInstance() {if (m_instance == NULL){m_instance = new CServerSocket();}return m_instance;};static void releaseInstance(){if (m_instance != NULL){CServerSocket* tmp = m_instance;m_instance = NULL;delete tmp;}}
private:// 静态成员变量static CServerSocket* m_instance;CServerSocket(const CServerSocket& ss) {};CServerSocket() {}; ~CServerSocket() {};
class CHelper{	public:CHelper() {CServerSocket::getInstance();}~CHelper(){CServerSocket::releaseInstance();}};static CHelper m_helper;
};
#include "ServerSocket.h"// 静态成员变量的初始化
CServerSocket* CServerSocket::m_instance = NULL;
CServerSocket* pserver = CServerSocket::getInstance();CServerSocket::CHelper CServerSocket::m_helper;

三、在单例中添加网络功能函数

        以上四步我们的单例模式就基本上成型了,下面我们就是只需要在类中在添加一些网络功能函数就可以在项目中去使用了。而这一部分我之前有讲过,基本上就是一些固定写法,请看多线程网络实战之仿qq群聊的服务器和客户端 这一章节。

1.初始化网络函数环境InitSocketEnv()
	// 1.套接字初始化BOOL InitSocketEnv(){WSADATA wsaData;int stu = WSAStartup(MAKEWORD(2, 2), &wsaData);if (stu != 0){std::cout << "WSAStartup 错误:" << stu << std::endl;return 0;}return TRUE;}
2.初始化网络InitSocket()
	bool InitSocket(short port){// 2. 创造套接字if (m_socket == INVALID_SOCKET){std::cout << "socket failed!" << GetLastError() << std::endl;WSACleanup(); //释放Winsock库资源return false;}// 3. bind 绑定套接字SOCKADDR_IN addrSrv;addrSrv.sin_family = AF_INET;  // ipv4 协议addrSrv.sin_addr.S_un.S_addr = htonl(ADDR_ANY);addrSrv.sin_port = htons(port);int ret = bind(m_socket, (sockaddr*)&addrSrv, sizeof(SOCKADDR));   // TODOif (ret != 0) return false;// 4. 监听ret = listen(m_socket, 1);      // TODOif (ret != 0) return false;return true;};
3.接收客户端连接AcceptClient()
	bool AcceptClient(){SOCKADDR_IN addrCli;int cli_size = sizeof(addrCli);m_client = accept(m_socket, (sockaddr*)&addrCli, &cli_size);if (m_client == INVALID_SOCKET){std::cout << "socket failed!" << GetLastError() << std::endl;WSACleanup(); //释放Winsock库资源return false;}TRACE("%s\r\n", "client connect success !!!");return true;};
4.处理客户端消息DealCommand()
#define BUFF_SIZE 409600int DealCommand(){if (m_client == -1) return false;char* buffer = new char[BUFF_SIZE];if (buffer == NULL){delete[]buffer;TRACE("内存不足!!");return -1;}memset(buffer, BUFF_SIZE, 0);static size_t index = 0;while (true){size_t len = recv(m_client, buffer + index, BUFF_SIZE - index,0);if ((len <= 0) && (index == 0)){ delete[]buffer;return -1;}index += len;len = index;m_packet = CPacket((BYTE*)buffer, len);if (len > 0){memmove(buffer, buffer + len, BUFF_SIZE - len);index -= len;delete[]buffer;return m_packet.sCmd;}} delete[]buffer;return -1;}
5.发送消息Send()
bool Send(const char* pData, int nSize){if (m_client == -1) return false;return send(m_client, pData, nSize, 0) > 0;}
6.断开连接CloseClient()
	void CloseClient(){if (m_client != INVALID_SOCKET){closesocket(m_client);m_client = INVALID_SOCKET;}}
7.声明套接字变量以及完善构造和析构函数
class CServerSocket
{
public: static CServerSocket* getInstance() {if (m_instance == NULL){m_instance = new CServerSocket();}return m_instance;};static void releaseInstance(){if (m_instance != NULL){CServerSocket* tmp = m_instance;m_instance = NULL;delete tmp;}}
protected:bool InitSocket(short port){// 2. 创造套接字if (m_socket == INVALID_SOCKET){std::cout << "socket failed!" << GetLastError() << std::endl;WSACleanup(); //释放Winsock库资源return false;}// 3. bind 绑定套接字SOCKADDR_IN addrSrv;addrSrv.sin_family = AF_INET;  // ipv4 协议addrSrv.sin_addr.S_un.S_addr = htonl(ADDR_ANY);addrSrv.sin_port = htons(port);int ret = bind(m_socket, (sockaddr*)&addrSrv, sizeof(SOCKADDR));   // TODOif (ret != 0) return false;// 4. 监听ret = listen(m_socket, 1);      // TODOif (ret != 0) return false;return true;};bool AcceptClient(){SOCKADDR_IN addrCli;int cli_size = sizeof(addrCli);m_client = accept(m_socket, (sockaddr*)&addrCli, &cli_size);if (m_client == INVALID_SOCKET){std::cout << "socket failed!" << GetLastError() << std::endl;WSACleanup(); //释放Winsock库资源return false;}TRACE("%s\r\n", "client connect success !!!");return true;};
#define BUFF_SIZE 409600int DealCommand(){if (m_client == -1) return false;char* buffer = new char[BUFF_SIZE];if (buffer == NULL){delete[]buffer;TRACE("内存不足!!");return -1;}memset(buffer, BUFF_SIZE, 0);static size_t index = 0;while (true){size_t len = recv(m_client, buffer + index, BUFF_SIZE - index,0);if ((len <= 0) && (index == 0)){ delete[]buffer;return -1;}index += len;len = index;if (len > 0){memmove(buffer, buffer + len, BUFF_SIZE - len);index -= len;delete[]buffer;}} delete[]buffer;return -1;}bool Send(const char* pData, int nSize){if (m_client == -1) return false;return send(m_client, pData, nSize, 0) > 0;}void CloseClient(){if (m_client != INVALID_SOCKET){closesocket(m_client);m_client = INVALID_SOCKET;}}private:SOCKET m_client;SOCKET m_socket;	CServerSocket& operator=(const CServerSocket& ss) {};CServerSocket(const CServerSocket& ss) {m_client = ss.m_client;m_socket = ss.m_socket;};CServerSocket() {m_client = INVALID_SOCKET;m_socket = INVALID_SOCKET;if (InitSocketEnv() == FALSE){MessageBox(NULL, _T("无法初始化套接字,检查网络设置"), _T("初始化错误!"),MB_OK | MB_ICONERROR);exit(0);}// 2. 创建套接字m_socket = socket(AF_INET, SOCK_STREAM, 0);}; ~CServerSocket() {closesocket(m_socket);WSACleanup();};// 1.套接字初始化BOOL InitSocketEnv(){WSADATA wsaData;int stu = WSAStartup(MAKEWORD(2, 2), &wsaData);if (stu != 0){std::cout << "WSAStartup 错误:" << stu << std::endl;return 0;}return TRUE;}
private:// 静态成员变量static CServerSocket* m_instance;CServerSocket(const CServerSocket& ss) {};CServerSocket() {}; ~CServerSocket() {};
class CHelper{	public:CHelper() {CServerSocket::getInstance();}~CHelper(){CServerSocket::releaseInstance();}};static CHelper m_helper;
};

  四、结尾

        以上就是TCP网络实现单例的整个过程,重要的更多是实现单例模式的思想,当然对于网络套接字而言,最重要的就是数据。而如何设计和封装发送和接收的网络包,这才是一大难点

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/52120.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用博达网站群管理平台的树状导航

1 介绍 由于网站建设需要&#xff0c;需在首页的左边竖栏部分使用树状导航。我又过了一遍《网站群管理平台用户手册》&#xff0c;没发现如何在网站的首页设置树状导航组件。昨天&#xff0c;我之所以在创建树状导航上不知所措&#xff0c;是因为平台本身有一些误导&#xff0…

如何用Java SpringBoot+Vue搭建校内跑腿业务系统?实战教程解析

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

ASM实例的SPILE 存储在ASM的磁盘组上时,集群要如何去获取SPFILE并启动ASM实例?(1)

从11g R2 开始&#xff0c;ASM spfile 会自动存储在安装集群软件时创建的第一个磁盘组中&#xff0c;一般为OCR磁盘组。由于投票盘/OCR 存储在 ASM 上&#xff0c;因此需要在节点上启动 ASM。要启动 ASM&#xff0c;需要其 SPFILE 。但 SPFILE 仅位于 ASM 磁盘组上。集群是如何…

【html+css 绚丽Loading】 - 000009 五行逆流珠

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽Loading&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495…

[000-01-018].第3节:Linux环境下ElasticSearch环境搭建

我的后端学习笔记大纲 我的ElasticSearch学习大纲 1.Linux系统搭建ES环境&#xff1a; 1.1.单机版&#xff1a; a.安装ES-7.8版本 1.下载ES: 2.上传与解压&#xff1a;将下载的tar包上传到服务器software目录下&#xff0c;然后解压缩&#xff1a;tar -zxvf elasticsearch-7…

Excel求和方法之

一 SUM&#xff08;&#xff09;&#xff0c;选择要相加的数,回车即可 二 上面的方法还不够快。用下面这个 就成功了 三 还有一种一样快的 选中之后&#xff0c;按下Alt键和键&#xff08;即Alt&#xff09;

深度学习------------------卷积神经网络(LeNet)

目录 LeNet网络手写的数字识别MNIST总结卷积神经网络&#xff08;LeNet&#xff09; 问题 LeNet网络 手写的数字识别 MNIST ①输入的是&#xff1a;3232的image ②放到一个55的卷积层里面&#xff08;为什么是5&#xff1f;因为32-x128&#xff0c;∴x5&#xff09;&#xff0c…

计算机毕业设计PySpark+Flask bilibili弹幕情感分析 B站视频数据可视化 B站爬虫 机器学习 深度学习 NLP自然语言处理 大数据毕业设计

### 开题报告&#xff1a;基于PySpark和Flask的B站弹幕情感分析系统 #### 一、研究背景 在网络视频平台的用户互动中&#xff0c;弹幕&#xff08;Danmaku&#xff09;作为一种实时评论的形式&#xff0c;已经成为观众表达观点和情感的重要方式。尤其是在B站&#xff08;哔哩…

MySQL 数据库管理

在 MySQL 中&#xff0c;数据库管理是非常基础但又至关重要的技能。无论是创建新的数据库、选择当前使用的数据库&#xff0c;还是查看数据库的相关信息&#xff0c;这些操作都是日常数据库管理中不可或缺的一部分。本文将详细介绍 MySQL 数据库管理的基本操作&#xff0c;包括…

Go Roadmap-Basics中文笔记

Go Roadmap-Basics 地址&#xff1a;https://roadmap.sh/golang 简介&#xff1a;Github star No.6 学习路线 Go 中译版 Learn the Basics Go特点&#xff1a;静态类型&#xff0c;运行速度快&#xff0c;编译语言&#xff0c;编译速度快&#xff0c;自动垃圾回收&#xff…

【GH】【EXCEL】P4: Chart

文章目录 data and chartdonut chart (radial chart)Radial Chart bar chartBar Chart line chartLine Chart Scatter ChartScatter Chart Surface ChartSurface Chart Chart DecoratorsChart Decorators Chart GraphicsChart Graphics data and chart donut chart (radial cha…

C语言04--数组超详解

1.基本概念 逻辑&#xff1a;一次性定义多个相同类型的变量&#xff0c;并存储到一片连续的内存中语法&#xff1a; 数据类型 数组名字 [ 数据的量 ] ; 示例&#xff1a; int a[5]; int Num ; 语法释义&#xff1a; a 是数组名&#xff0c;即这片连续内存的名称[5] …

一文入门:使用 Python的requests 库

简介 requests 是 Python 中一个简单易用的 HTTP 库&#xff0c;用于发送各种 HTTP 请求。它建立在 Python 标准库 urllib 之上&#xff0c;提供了更人性化的接口。 安装 requests 库 在开始之前&#xff0c;确保你已经安装了 requests 库。如果还没有安装&#xff0c;可以通…

基于WEB的旅游推荐系统设计与实现

TOC springboot280基于WEB的旅游推荐系统设计与实现 第1章 绪论 1.1选题动因 当前的网络技术&#xff0c;软件技术等都具备成熟的理论基础&#xff0c;市场上也出现各种技术开发的软件&#xff0c;这些软件都被用于各个领域&#xff0c;包括生活和工作的领域。随着电脑和笔…

(七)Flink Watermark

Flink 的 Watermark 是用来标识数据流中的一个时间点。Watermark 的设计是为了解决乱序数据处理的问题,尤其是涉及到多个分区的 Kafka 消费者时。在 Watermark 的作用下,即使某些数据出现了延迟到达的情况,也不会导致整个处理流程的中断。此外,Watermark 还能防止过期的数据…

五、2 移位操作符赋值操作符

1、移位操作符 2、赋值操作符 “ ”赋值&#xff0c;“ ”判断是否相等 1&#xff09;连续赋值 2&#xff09;复合赋值符

驾驭时间之舟:SQL中时序数据处理的深度探索

标题&#xff1a;驾驭时间之舟&#xff1a;SQL中时序数据处理的深度探索 在数据的长河中&#xff0c;时间序列数据以其独特的流动性和连续性&#xff0c;记录着世界的每一次脉动。SQL作为数据查询的通用语言&#xff0c;在处理时序数据时&#xff0c;也展现出了其独特的魅力和…

VS Code开发C#(.NET)之快速入门

本篇快速介绍在VS Code中开发C#的完整说明和示例&#xff1a; 环境准备 安装VS Code&#xff1a; 前往Visual Studio Code官网 下载并安装VS Code。 安装.NET SDK&#xff1a; C#是基于.NET框架的&#xff0c;因此需要安装 .NET SDK。 前往 .NET官网 下载并安装适用于操…

每天一个数据分析题(四百九十一)- 主成分分析与因子分析

因子载荷矩阵是主成分载荷矩阵&#xff08;&#xff09;的结果 A. 最小方差斜交旋转 B. 最大方差斜交旋转 C. 最小方差正交旋转 D. 最大方差正交旋转 数据分析认证考试介绍&#xff1a;点击进入 题目来源于CDA模拟题库 点击此处获取答案 数据分析专项练习题库 内容涵盖…

成为Python砖家(4): 装饰器的简单理解

第一次理解 Python 中的装饰器&#xff08;decorator&#xff09;&#xff0c;是Python中一个非常强大的工具&#xff0c;它是一个返回函数的函数。 上面这个定义很简洁&#xff0c;但是没说清楚。 第二次理解 装饰器&#xff0c;是一个接收函数 func、返回封装后的函数 wr…