使用模板方法设计模式封装 socket 套接字并实现Tcp服务器和客户端 简单工厂模式设计

文章目录

  • 使用模板方法设计模式封装套接字
  • 使用封装后的套接字实现Tcp服务器和客户端
    • 实现Tcp服务器
    • 实现Tcp客户端
  • 工厂模式

在这里插入图片描述

使用模板方法设计模式封装套接字

可以使用模块方法设计模式来设计套接字 socket 的封装

模板方法(Template Method)设计模式是一种行为设计模式,它在一个方法中定义了一个算法的骨架,并允许子类为一个或多个步骤提供实现。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。

  1. 抽象类(Abstract Class):定义了一个或多个抽象操作,以及一个模板方法。这个模板方法调用了一个或多个抽象操作。
  2. 具体子类(Concrete Subclass):实现抽象类中的抽象操作,从而完成算法中特定步骤的具体实现。

抽象类定义了一个模板方法,这个方法通常包含对具体方法的调用,抽象类还定义了一些抽象方法,这些方法会在模板方法中被调用,但具体的实现由子类来提供(抽象类,也就是父类中,将这些方法都设置为纯虚函数,子类要重写纯虚函数),子类通过继承抽象类并提供抽象方法的实现,从而可以自定义模板方法中的某些步骤,当模板方法被调用时,它会按照定义的顺序依次调用抽象类中的抽象方法和具体方法。

#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#define Convert(addrptr) ((struct sockaddr *)addrptr)namespace Net_Work
{const static int defaultsockfd = -1;const int backlog = 5;enum{SocketError = 1,BindError,ListenError};// 封装一个基类, 套接字对应的接口类// 一旦一个类中有纯虚函数, 只有被继承并且实现此方法后, 才能创建对象// ::close 使用系统的接口函数// 设计模式:模板方法class Socket{public:virtual ~Socket() {}virtual void CreateSocketOrDie() = 0;            // 创建套接字virtual void BindSocketOrDie(uint16_t port) = 0; // 绑定virtual void ListenSocketOrDie(int backlog) = 0; // 监听// 获取连接, 拿到tcp所需的新的文件描述符 newsockfd, 并返回并 create 一个 tcp 套接字, 并将客户端信息通过输出型参数返回virtual Socket* AcceptConnection(std::string* peerip, uint16_t* peerport) = 0;virtual bool ConnetServer(std::string& serverip, uint16_t serverport) = 0;virtual int GetSockFd() = 0;virtual void SetSockFd(int sockfd) = 0;virtual void CloseSockfd() = 0;public:// 下面这些方法会被子类继承下去void BulidListenSocketMethod(uint16_t port, int backlog) // 创建监听套接字, 给 Server 用的{CreateSocketOrDie();BindSocketOrDie(port);ListenSocketOrDie(backlog);}bool BulidConnectSocketMethod(std::string& serverip, uint16_t serverport) // 创建连接套接字, 给Client 用{CreateSocketOrDie();return ConnetServer(serverip, serverport); // connet 有可能会失败}void BulidNormalSocketMethod(int sockfd){SetSockFd(sockfd);}};// 成员函数用 override 修饰后, 派生类必须重载基类的同名虚函数, 否则编译不能通过class TcpSocket : public Socket{public:TcpSocket(int sockfd = defaultsockfd) : _sockfd(sockfd){}~TcpSocket(){}void CreateSocketOrDie() override{_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0)exit(SocketError);}void BindSocketOrDie(uint16_t port) override{struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;local.sin_port = htons(port);int n = ::bind(_sockfd, Convert(&local), sizeof(local));if (n < 0)exit(BindError);}void ListenSocketOrDie(int backlog) override{int n = ::listen(_sockfd, backlog);if (n < 0)exit(ListenError);}Socket* AcceptConnection(std::string* peerip, uint16_t* peerport) override{struct sockaddr_in peer;socklen_t len = sizeof(peer);int newsockfd = ::accept(_sockfd, Convert(&peer), &len);if (newsockfd < 0)return nullptr;*peerport = ntohs(peer.sin_port);*peerip = inet_ntoa(peer.sin_addr);Socket* s = new TcpSocket(newsockfd);return s;}bool ConnetServer(std::string& serverip, uint16_t serverport) override{struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(serverip.c_str());server.sin_port = htons(serverport);int n = ::connect(_sockfd, Convert(&server), sizeof(server));if (n == 0)return true;elsereturn false;}int GetSockFd() override{return _sockfd;}void SetSockFd(int sockfd) override{_sockfd = sockfd;}void CloseSockfd() override{if (_sockfd > defaultsockfd)::close(_sockfd);}private:int _sockfd;};
}

使用封装后的套接字实现Tcp服务器和客户端

实现Tcp服务器

#pragma once
#include "Socket.hpp"
#include <iostream>
#include <pthread.h>
#include <functional>// void 表示返回值为空, ()里面是参数
using func_t = std::function<void(Net_Work::Socket* sockp)>;class TcpServer;class ThreadData
{
public:ThreadData(TcpServer* tcp_this, Net_Work::Socket* sockp) :_this(tcp_this), _sockp(sockp){}
public:TcpServer* _this;Net_Work::Socket* _sockp;
};class TcpServer
{
public:TcpServer(uint16_t port, func_t handler_request) :_port(port), _listensocket(new Net_Work::TcpSocket()), _handler_request(handler_request){_listensocket->BulidListenSocketMethod(_port, Net_Work::backlog);}static void* ThreadRun(void* args){pthread_detach(pthread_self());ThreadData* td = static_cast<ThreadData*>(args);td->_this->_handler_request(td->_sockp); // 先回调,td->_sockp->CloseSockfd(); // 再关闭文件描述符delete td->_sockp;delete td;}void Loop(){while (true){std::string peerip;uint16_t peerport;Net_Work::Socket* newsock = _listensocket->AcceptConnection(&peerip, &peerport);if (newsock == nullptr) continue;std::cout << "获取一个新连接 sockfd: " << newsock->GetSockFd() << " client info: " << peerip << ":" << peerport << std::endl;pthread_t tid;ThreadData* td = new ThreadData(this, newsock);pthread_create(&tid, nullptr, ThreadRun, td);}}~TcpServer(){delete _listensocket;}
private:int _port;Net_Work::Socket* _listensocket;func_t _handler_request;
};

实现Tcp客户端

#include "Protocol.hpp"
#include "Socket.hpp"
#include <unistd.h>
#include <iostream>
#include <string>
#include <memory>int main(int argc, char* argv[])
{if (argc != 3){std::cout << "Usage: " << argv[0] << " serverip serverport" << std::endl;return 0;}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);Net_Work::Socket* s = new Net_Work::TcpSocket();if (!s->BulidConnectSocketMethod(serverip, serverport)){std::cerr << "connect " << serverip << ":" << serverport << "failed" << std::endl;}std::cout << "connect " << serverip << ":" << serverport << " success" << std::endl;std::unique_ptr<Factory> factory = std::make_unique<Factory>();std::shared_ptr<Request> req = factory->BuildRequest(10, 20, '+');while (true){req->Inc();send(s->GetSockFd(), &(*req), sizeof(*req), 0);sleep(1);}s->CloseSockfd();return 0;
}

工厂模式

工厂设计模式是一种创建型设计模式,它提供了一种灵活的方式来实例化和组织对象的创建。工厂设计模式是一种创建对象的软件设计模式,它通过一个公共接口或基类来创建对象,而无需暴露对象的具体实现。这种设计模式的主要作用是将对象的创建与使用分离,从而降低耦合度,使代码更易于理解和维护。

  • 抽象产品(Abstract Product):定义了一个产品的接口或抽象类,它描述了产品的共同属性和方法。
  • 具体产品(Concrete Product):实现了抽象产品接口或继承自抽象产品类的具体类,代表了具体的对象。
  • 工厂类(Factory Class):负责创建具体产品的实例,它通常包含一个工厂方法,用于创建产品对象。
    优点:
  1. 解耦:将对象的创建与使用分离,降低了客户端与具体产品之间的耦合度。
  2. 灵活性和可扩展性:通过引入抽象层,可以轻松地创建新的具体产品类,而无需修改客户端代码。
  3. 代码复用:通过封装对象的创建过程,可以在多个客户端之间复用相同的创建逻辑。

简易的工厂模式:一个请求类,一个回应类,一个工厂类。

#include <iostream>
#include <memory>// 请求
class Request
{
public:Request(){}Request(int x, int y, char op) :_data_x(x), _data_y(y), _oper(op){}
private:// 约定好的 x _oper yint _data_x;int _data_y;char _oper; // + - * / %
};// 相应
class Response
{
public:Response(){}Response(int result, int code) :_result(result), _code(code){}
private:int _result; // 计算结果int _code; // 运算状态
};// 工厂模式
class Factory
{
public:std::shared_ptr<Request> BuildRequest(){std::shared_ptr<Request> req = std::make_shared<Request>();return req;}std::shared_ptr<Request> BuildRequest(int x, int y, char op){std::shared_ptr<Request> req = std::make_shared<Request>(x, y, op);return req;}std::shared_ptr<Response> BuildResponse(){std::shared_ptr<Response> resp = std::make_shared<Response>();return resp;}std::shared_ptr<Response> BuildResponse(int result, int code){std::shared_ptr<Response> resp = std::make_shared<Response>(result, code);return resp;}
};

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

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

相关文章

【深度学习】深度学习基础

李宏毅深度学习笔记 局部极小值与鞍点 鞍点其实就是梯度是零且区别于局部极小值和局部极大值的点。 鞍点的叫法是因为其形状像马鞍。鞍点的梯度为零&#xff0c;但它不是局部极小值。我们把梯度为零的点统称为临界点&#xff08;critical point&#xff09;。损失没有办法再下…

Docker Desktop 简易操作指南 (Windows, macOS, Linux)

1. 下载最新版本 Docker Desktop https://www.docker.com/products/docker-desktop/ 2.启动 Docker Desktop 3.常用命令&#xff08;在 cmd 或 Terminal 中执行&#xff09; #列出所有镜像&#xff08;Images&#xff09; docker images #列出所有容器&#xff08;Containers&…

CSS 核心知识点 - grid

思维导图 参考网址: https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_grid_layout 一、什么是 grid&#xff1f; CSS Grid布局是在CSS3规范中引入的一种新的布局方式&#xff0c;旨在解决传统布局方法&#xff08;如浮动、定位、表格布局&#xff09;存在的许多问题。C…

DataWhale-吃瓜教程学习笔记(四)

学习视频&#xff1a;第3章-二分类线性判别分析_哔哩哔哩_bilibili 西瓜书对应章节&#xff1a; 3.4 文章目录 - 算法原理- 损失函数推导-- 异类样本中心尽可能远-- 同类样本方差尽可能小-- 综合 知识点补充 - 二范数二范数&#xff08;2-norm&#xff09;详解定义几何意义性质…

OpenHarmony开发实战:HDF驱动开发流程

概述 HDF&#xff08;Hardware Driver Foundation&#xff09;驱动框架&#xff0c;为驱动开发者提供驱动框架能力&#xff0c;包括驱动加载、驱动服务管理、驱动消息机制和配置管理。并以组件化驱动模型作为核心设计思路&#xff0c;让驱动开发和部署更加规范&#xff0c;旨在…

四川赤橙宏海商务信息咨询有限公司抖音开店靠谱吗?

在数字化浪潮席卷全球的今天&#xff0c;电商行业正以前所未有的速度发展。而在这个大潮中&#xff0c;四川赤橙宏海商务信息咨询有限公司凭借其专业的团队和前瞻性的战略眼光&#xff0c;专注于抖音电商服务&#xff0c;为广大商家提供了一站式解决方案&#xff0c;成为了行业…

Ubuntu20.04安装LibTorch并完成高斯溅射环境搭建

0. 简介 最近受到优刻得的使用邀请&#xff0c;正好解决了我在大模型和自动驾驶行业对GPU的使用需求。UCloud云计算旗下的Compshare的GPU算力云平台。他们提供高性价比的4090 GPU&#xff0c;按时收费每卡2.6元&#xff0c;月卡只需要1.7元每小时&#xff0c;并附带200G的免费…

接口自动化测试-项目实战

什么是接口自动化测试&#xff1a;使用工具或代码代替人对接口进行测试 测试项目结构&#xff08;python包&#xff09; 1、接口api包 2、script:业务脚本 3、data:数据 4、config.py :配置文件 5、reporter:报告 错误问题&#xff1a; 1、未打印任何东西。添加pip ins…

C语言 指针——缓冲区溢出与缓冲区溢出攻击

目录 缓冲区溢出攻击 缓冲区溢出攻击实例 字符串的安全输入方法​编辑 防止缓冲区溢出的两个要点 缓冲区溢出攻击 网络黑客常针对系统和程序自身存在的漏洞&#xff0c;编写相应的攻击程序  对缓冲区溢出漏洞的攻击 —— 最常见  几乎占到了网络攻击次数的一半以上…

Android (已解决)Gradle 编译失败 Unsupported class file major version 61

文章目录 一、报错原因二、解决方法 一、报错原因 新版本的 Android Studio 默认使用的是 Java 17 LTS&#xff0c;而这个历史项目的 Gradle 版本很低&#xff0c;不支持高版本的 Java。 具体原因&#xff1a;Java 17 (major version 61) 编译的 class 文件&#xff0c;如果在…

DevEco Studio有时会多出来.js和.map文件,导致项目不能运行

1、问题 在使用DevEco的时候有时候会出现啥都没干&#xff0c;但是在项目的目录下会自动生成和文件同名的.js和.js.map文件&#xff0c;至于为什么会生成目前我也不知道&#xff0c;如果想要更深了解可以到论坛讨论&#xff1a;华为开发者论坛。生成.js和.js.map文件优…

Terraform基础概念一

Terraform基础概念一 1.Infrastructure-as-Code(IaC)概念1.1 IaC优势1.2 IaC工具1.3 IaC的两种方式 2.Terraform基础概念2.1 Terraform工作原理2.2 Terraform 工作流 3.总结 1.Infrastructure-as-Code(IaC)概念 基础设施即代码&#xff08;Infrastructure-as-Code&#xff0c;…

SVN 的忽略(Ignore)和递归(Recursively)以及忽略部分

SVN中忽略大家经常用到&#xff0c;但总是似懂非懂&#xff0c;下面就详细展开说明一下忽略如何设置。 两个忽略 通常设置忽略都是文件夹和里面的文件都忽略&#xff0c;通常只需要鼠标右键点击忽略就可以了&#xff0c;如图&#xff1a; 第一个忽略用的最多&#xff0c;就是…

排序算法(C语言版)

前言 排序作为生产环境中常见的需求之一&#xff0c;对整个产品有举足轻重的影响&#xff0c;可以说使用一个合适的排序算法是业务逻辑中比较重要的一部分。今天我们就来介绍常见的排序算法以及实现 排序 所谓排序无非就是按照特定的规则对一组数据就行顺序化。 常见的排序有…

Spring项目报错解读与全部报错详解

你好,我是Qiuner. 为帮助别人少走弯路和记录自己编程学习过程而写博客 这是我的 github https://github.com/Qiuner ⭐️ ​ gitee https://gitee.com/Qiuner &#x1f339; 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我…

图像大模型中的注意力和因果掩码

AIM — 图像领域中 LLM 的对应物。尽管 iGPT 已经存在 2 年多了&#xff0c;但自回归尚未得到充分探索。在本文中&#xff0c;作者表明&#xff0c;当使用 AIM 对网络进行预训练时&#xff0c;一组图像数据集上的下游任务的平均准确率会随着数据和参数的增加而线性增加。 要运…

uniApp获取实时定位

通过你获取的key放到项目manifest.json里面&#xff0c;对应填写你所需要的key值&#xff0c;还有高德用户名 用户名&#xff1a; key值的位置&#xff1a; 代码&#xff1a; html: <view class"intList pdNone"><view class"label">详细地…

爬虫:爬取知乎热榜一级评论及回答2024不包含翻页

一、先上结果&#xff08;注:本文仅为兴趣爱好探究&#xff0c;请勿进行商业利用或非法研究&#xff0c;负责后果自负&#xff0c;与作者无关&#xff09; 1、爬标题及其具体内容 2、抓标题下的对应回答 3、爬取对应一级评论 二、上流程 1、获取cookies&#xff08;相信哥哥姐姐…

静心冥想训练入门|流静

在喧嚣的都市中&#xff0c;我们时常被琐事所困&#xff0c;心灵难以得到片刻的宁静。然而&#xff0c;静心冥想训练如同一扇通往内心宁静的门户&#xff0c;引领我们踏上一段静谧的旅程。 静心冥想&#xff0c;并非遥不可及的高深技艺&#xff0c;而是每个人都能掌握的心灵修炼…

优思学院|「按计划推动型」与「需求拉动型」的生产模式

针对生产架构做对比分类的用语&#xff0c;主要有按计划推进型与需求拉动型。 「按计划推动型」与「需求拉动型」两者乃是生产架构上常使用、成对比的两个用语。不过&#xff0c;有时不只用来指单纯的生产现场架构&#xff0c;也有人把它应用在更广泛的生产架构设计上。 按计划…