使用模板方法设计模式封装 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;。损失没有办法再下…

使用Flink CDC实现 Oracle数据库数据同步(非SQL)

文章目录 前言一、开启归档日志二、创建flinkcdc专属用户2.1 对于Oracle 非CDB数据库&#xff0c;执行如下sql2.2 对于Oracle CDB数据库&#xff0c;执行如下sql 三、指定oracle表、库级启用四、使用flink-connector-oracle-cdc实现数据库同步4.1 引入pom依赖4.1 Java主代码4.1…

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&…

OpenSSL/3.3.0: error:0A00018A:SSL routines::dh key too small

php curl解决办法: curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, ‘DEFAULTSECLEVEL1’); python 解决办法: from twisted.internet.ssl import AcceptableCiphers from scrapy.core.downloader import contextfactory contextfactory.DEFAULT_CIPHERS AcceptableCiphers.from…

CSS 核心知识点 - grid

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

Spring Boot 集成 MyBatis-Plus 总结

Spring Boot 集成 MyBatis-Plus 总结 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在Java开发中&#xff0c;Spring Boot以其简洁和高效的特点&#xff0c;…

Oh My Zsh Git 插件

以下是一些常见的别名和它们对应的 Git 命令&#xff1a; g: gitga: git addgaa: git add --allgapa: git add --patchgau: git add --updategb: git branchgba: git branch -agbd: git branch -dgbda: git branch --no-color --merged | command grep -vE “^(||*|\s*(main|m…

第十九站:Java钛蓝——区块链技术的新探索

在区块链技术的新探索中&#xff0c;Java作为一门成熟的编程语言&#xff0c;正在通过Hyperledger Fabric和Web3j等技术实现其在区块链领域的应用。以下是对这些技术的简要介绍和如何使用Java源代码与它们进行交互的讲解。 Hyperledger Fabric Hyperledger Fabric是一个由Lin…

React.js 全面解析:从基础到实战案例

引言&#xff1a; React.js&#xff0c;由Facebook推出并维护的开源JavaScript库&#xff0c;以其组件化思想、虚拟DOM技术和声明式编程风格&#xff0c;成为构建用户界面的首选工具之一。本文将系统性地介绍React的基础概念、核心特性&#xff0c;并通过实际案例展示基础属性…

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

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

vue3中省市区联动在同一个el-form-item中咋么设置rules验证都不为空的效果

在开发中出现如下情况&#xff0c;在同一个el-form-item设置了省市区三级联动的效果 <el-form-item label"地区" prop"extraProperties.Province"><el-row :gutter"20"><el-col :span"12"><el-select v-model&qu…

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

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

Unity3D Excel表格数据处理模块详解

一、引言 在Unity3D开发中&#xff0c;我们经常需要处理大量的数据&#xff0c;这些数据可能是游戏配置、角色属性、道具信息等。Excel表格作为一种常见的数据存储方式&#xff0c;具有结构清晰、易于编辑的特点&#xff0c;因此被广泛应用于游戏开发中。本文将详细介绍如何在…

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

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

面经-常用框架

1.Spring 1.1什么是Spring框架&#xff1f; Spring 是⼀种轻量级开发框架&#xff0c;旨在提⾼开发⼈员的开发效率以及系统的可维护性。 Spring 的 6 个特征:核⼼技术&#xff0c;测试&#xff0c;数据访问&#xff0c;Web⽀持&#xff0c;集成&#xff0c;语⾔ 1.2列举⼀些重…

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…

走马灯封装

走马灯功能需求&#xff1a; 支持定时切换&#xff1b;支持左右按钮切换&#xff08;根据鼠标是否在切换组件内展示和隐藏左右切换按钮&#xff09;&#xff1b;支持底部标识切换&#xff1b; 走马灯 完整代码如下&#xff1a; /*** class 走马灯*/import react, { Compone…

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;如果在…