Linux笔记---封装套接字

1. 模板方法模式

模板方法模式(Template Method Pattern) 是一种行为型设计模式,其核心思想 是:在抽象类 中定义一个算法的骨架 (模板方法),将算法中某些可变的步骤延迟到子类中实现,从而让子类在不改变算法整体结构的前提下,灵活定制算法的特定步骤。

模板方法模式包含以下关键角色

  • 抽象类: 定义算法的骨架(模板方法),并声明一些抽象方法(留给子类实现)和具体方法(算法中固定不变的步骤)。 模板方法通常会调用这些抽象方法和具体方法,完成整个算法流程。
  • 具体子类: 继承抽象类,实现抽象类中声明的抽象方法,从而定制算法中的可变步骤。 子类不会修改模板方法本身(即算法的整体结构保持不变)。

以 "冲饮料" 为例,冲咖啡冲茶的流程相似(烧水→冲泡→倒杯→加调料),但 "冲泡" 和 "加调料" 的具体步骤不同,适合用模板方法模式实现:

cpp 复制代码
#include <iostream>
#include <string>

// 抽象类:定义算法骨架
class Beverage {
public:
    // 模板方法:定义制作饮料的整体流程(用final防止子类重写)
    virtual void prepareRecipe() const final {
        boilWater();      // 固定步骤:烧水
        brew();           // 可变步骤:冲泡(子类实现)
        pourInCup();      // 固定步骤:倒入杯子
        if (customerWantsCondiments()) {  // 钩子方法:可选步骤
            addCondiments();  // 可变步骤:加调料(子类实现)
        }
    }

protected:
    // 纯虚函数:必须由子类实现的步骤
    virtual void brew() const = 0;
    virtual void addCondiments() const = 0;

    // 具体方法:固定不变的步骤
    void boilWater() const {
        std::cout << "烧开水" << std::endl;
    }

    void pourInCup() const {
        std::cout << "倒入杯子中" << std::endl;
    }

    // 钩子方法(Hook):默认实现,子类可选择重写
    virtual bool customerWantsCondiments() const {
        return true;  // 默认加调料
    }
};

// 具体子类:咖啡
class Coffee : public Beverage {
protected:
    void brew() const override {
        std::cout << "用沸水冲泡咖啡粉" << std::endl;
    }

    void addCondiments() const override {
        std::cout << "加牛奶和糖" << std::endl;
    }

    // 重写钩子方法:询问是否加调料
    bool customerWantsCondiments() const override {
        std::string answer;
        std::cout << "请问需要加牛奶和糖吗?(y/n): ";
        std::cin >> answer;
        return answer == "y" || answer == "Y";
    }
};

// 具体子类:茶
class Tea : public Beverage {
protected:
    void brew() const override {
        std::cout << "用沸水浸泡茶叶" << std::endl;
    }

    void addCondiments() const override {
        std::cout << "加柠檬" << std::endl;
    }
};

// 测试
int main() {
    Beverage* coffee = new Coffee();
    std::cout << "=== 制作咖啡 ===" << std::endl;
    coffee->prepareRecipe();

    Beverage* tea = new Tea();
    std::cout << "\n=== 制作茶 ===" << std::endl;
    tea->prepareRecipe();

    delete coffee;
    delete tea;
    return 0;
}

特点

  • 算法骨架固定:模板方法(如prepareRecipe)定义了算法的整体流程,子类无法修改(通常用final修饰)。
  • 可变步骤延迟:抽象方法(如brew、addCondiments)由子类实现,灵活定制具体细节。
  • 代码复用:抽象类中封装了公共步骤(如boilWater),避免子类重复实现。

优点

  • 封装不变部分,扩展可变部分,符合 "开闭原则"
  • 提取公共代码,减少重复,便于维护
  • 父类控制算法流程,子类专注实现细节,职责清晰

缺点

  • 每个具体实现都需要一个子类,可能导致类数量增多
  • 子类执行的结果会影响父类,一定程度上破坏了 **"里氏替换原则"**的纯粹性。

适用场景

  • 多个子类有相同的算法流程,但部分步骤实现不同(如框架中的生命周期方法)。
  • 需要控制子类扩展时(只允许子类修改特定步骤,不允许改变整体流程)。
  • 希望提取多个类的公共行为,集中到抽象类中。

2. 封装Socket类

AI告诉我,socket类的封装是模板方法模式的典型应用场景,但是我觉得不是很合理,原因如下。

根据我们之前编程的经验,我们可以按照套接字创建的流程和用法将套接字分为5类:

  1. TCP监听套接字:显式绑定地址,用于TCP服务端监听来自客户端的连接请求。
  2. TCP连接套接字:TCP监听套接字accept成功之后返回的用于为客户端提供服务的套接字。
  3. TCP客户端套接字:隐式绑定地址,通过connect与服务端建立连接。
  4. UDP服务端套接字:显式绑定地址,用于和客户端进行报文交流。
  5. UDP客户端套接字:隐式绑定地址,用于和服务端进行报文交流。

但是,显然这些套接字无论是创建的过程还是用法都大相径庭,很难说有什么固定的流程。

但是但是,AI都这么说了,我还是试着来封装一下。我希望封装之后的成果就是:各种套接字被被创建出来就直接完成了初始化,直接开始发挥自己主要的作用

例如,TCP监听套接字被创建出来之后就可以开始调用自己的Accept方法等待连接,TCP服务端套接字、UDP服务端/客户端套接字被创建出来就可以开始不断地收发消息。

为了贴合主题,我们先将套接字初始化的流程固定一下:

cpp 复制代码
// 模板方法:固定初始化流程(禁止子类重写)
virtual void Initialize() final
{
    CreateSocket(); // 创建套接字
    Bind(); // 绑定地址
    Listen(); // 设置监听
    Connect(); // 建立连接
}

这时候就有人问了:bind是每个套接字都要做的吗?listen是每个套接字都要做的吗?connect是每个套接字都要做的吗?

你说的很对,所以我们在实现子类的时候,不需要这些步骤的子类就提供对应方法的空实现即可。

做起来最麻烦的还是接口的设计,例如基类Read\Write接口的设计,如何设计参数与返回值能使得TCP套接字和UDP套接字都能通用。

总之也是非常麻烦,博主也懒得介绍具体的代码了,反正套接字编程的流程大家都很熟悉了,不知道如何设计接口的小伙伴可以参考一下博主的代码:

cpp 复制代码
#pragma once
#include "Common.hpp"
#include "Mutex.hpp"

namespace SocketModule
{
    const int default_pending_num = 10;
    class TCPConnectSocket;
    class Socket
    {
    protected:
        int _sockfd;
        InetAddr _addr;
        MutexModule::Mutex _mutex;  // 线程安全锁

        // 基础套接字创建(供子类调用)
        void CreateSocket(int type)
        {
            _sockfd = ::socket(AF_INET, type, 0);
            if(_sockfd == -1)
            {
                LOG(LogLevel::FATAL) << "socket: 申请套接字失败! " << strerror(errno);
                throw std::runtime_error("socket create failed");  
            }
        }

        // 模板方法的核心步骤(纯虚函数,由子类实现)
        virtual void CreateSocket() = 0;
        virtual void Bind() = 0;
        virtual void Listen() = 0;
        virtual void Connect() = 0;

        // 模板方法:固定初始化流程(禁止子类重写)
        virtual void Initialize() final
        {
            CreateSocket();
            Bind();
            Listen();
            Connect();
        }

    public:
        // 用于服务端/客户端套接字(未连接状态)
        Socket(const std::string& ip, in_port_t port) 
            : _sockfd(-1)
            , _addr(ip, port)
        {}

        // 用于已连接套接字(如accept返回的TCP连接)
        Socket(const int sockfd, const InetAddr& addr)
            : _sockfd(sockfd)
            , _addr(addr)
        {}

        void Close()
        {
            if(_sockfd != -1)
                ::close(_sockfd);
            _sockfd = -1;
        }

        // 虚析构函数确保子类资源正确释放
        virtual ~Socket()
        {
            Close();
        }

        const InetAddr& addr() const { return _addr; }
        // 纯虚函数:定义子类必须实现的接口
        virtual std::shared_ptr<TCPConnectSocket> Accept() = 0;
        // TCP协议不考虑第二个参数
        virtual int Receive(std::string& recv_msg, InetAddr* peer = nullptr) = 0;
        virtual int Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) = 0;
    };

    // TCP连接套接字(由accept返回)
    class TCPConnectSocket : public Socket
    {
    public:
        TCPConnectSocket(int sockfd, const InetAddr& addr)
            : Socket(sockfd, addr)  // 直接使用已创建的套接字
        {
            Initialize();
        }

        // 重写基类方法(已连接套接字无需重新创建)
        void CreateSocket() override {}

        int Receive(std::string& recv_msg, InetAddr* peer = nullptr) override
        {
            MutexModule::LockGuard lock(_mutex);
            char buffer[BUFFER_SIZE];
            ssize_t size = ::recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
            if (size <= 0)
            {
                recv_msg.clear();
                if (size < 0)
                    LOG(LogLevel::ERROR) << "recv error: " << strerror(errno);
                return size;
            }
            buffer[size] = '\0';
            recv_msg = buffer;
            return size;
        }

        int Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
        {
            MutexModule::LockGuard lock(_mutex);
            ssize_t size = ::send(_sockfd, send_msg.c_str(), send_msg.size(), 0);
            if (size == -1)
                LOG(LogLevel::ERROR) << "send error: " << strerror(errno);
            return size;
        }

    protected:  // 修正访问权限
        void Bind() override {}  // 已连接套接字无需绑定
        void Listen() override {}  // 已连接套接字无需监听
        void Connect() override {}  // 已连接套接字无需再次连接

        std::shared_ptr<TCPConnectSocket> Accept() override
        {
            return nullptr;  // 连接套接字不处理accept
        }
    };

    // TCP监听套接字
    class TCPListenSocket : public Socket
    {
    public:
        TCPListenSocket(in_port_t port)
            : Socket("0.0.0.0", port)  // 监听所有网卡
        {
            Initialize();
        }

        // 重写基类方法(显式标记override)
        void CreateSocket() override
        {
            Socket::CreateSocket(SOCK_STREAM);  // TCP类型
        }

        std::shared_ptr<TCPConnectSocket> Accept() override
        {
            MutexModule::LockGuard lock(_mutex);  // 线程安全保护
            InetAddr client;
            int sockfd = ::accept(_sockfd, client.NetAddrPtr(), &client.AddrLen());
            client = InetAddr(client.NetAddr());
            if(sockfd == -1)
            {
                LOG(LogLevel::WARNING) << "accept: 建立连接失败! " << strerror(errno);
                return nullptr;
            }
            LOG(LogLevel::INFO) << "accept: 建立连接成功! ";
            return std::make_shared<TCPConnectSocket>(sockfd, client);
        }

    protected: 
        void Bind() override
        {
            int n = ::bind(_sockfd, _addr.NetAddrPtr(), _addr.AddrLen());
            if(n == -1)
            {
                LOG(LogLevel::FATAL) << "bind: 绑定地址信息失败! " << strerror(errno);
                throw std::runtime_error("bind failed");
            }
        }

        void Listen() override
        {
            int n = ::listen(_sockfd, default_pending_num);
            if(n == -1)
            {
                LOG(LogLevel::FATAL) << "listen: 设置监听套接字失败! " << strerror(errno);
                throw std::runtime_error("listen failed");
            }
        }

        void Connect() override  // TCP监听套接字无需主动连接
        {}
        int Receive(std::string& recv_msg, InetAddr* peer = nullptr) override
        {
            return -1;  // 监听套接字不处理接收
        }
        int Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
        {
            return -1;  // 监听套接字不处理发送
        }
    };

    // TCP客户端套接字
    class TCPClientSocket : public Socket
    {
    public:
        TCPClientSocket(const std::string& ip, in_port_t port)
            : Socket(ip, port)
        {
            Initialize();
        }

        void CreateSocket() override
        {
            Socket::CreateSocket(SOCK_STREAM);  // TCP类型
        }

        void Connect() override
        {
            int n = ::connect(_sockfd, _addr.NetAddrPtr(), _addr.AddrLen());
            if(n == -1)
            {
                LOG(LogLevel::FATAL) << "connect: 连接失败! " << strerror(errno);
                throw std::runtime_error("connect failed");
            }
            LOG(LogLevel::INFO) << "客户端套接字已连接到[" << _addr.Info() << "]";
        }

        int Receive(std::string& recv_msg, InetAddr* peer = nullptr) override
        {
            MutexModule::LockGuard lock(_mutex);
            char buffer[BUFFER_SIZE];
            ssize_t size = ::recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
            if (size <= 0)
            {
                recv_msg.clear();
                if (size < 0)
                    LOG(LogLevel::ERROR) << "recv error: " << strerror(errno);
                return size;
            }
            buffer[size] = '\0';
            recv_msg = buffer;
            return size;
        }

        int Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
        {
            MutexModule::LockGuard lock(_mutex);
            ssize_t size = ::send(_sockfd, send_msg.c_str(), send_msg.size(), 0);
            if (size == -1)
                LOG(LogLevel::ERROR) << "send error: " << strerror(errno);
            return size;
        }

    protected:  // 修正访问权限
        void Bind() override {}  // 客户端通常不主动绑定
        void Listen() override {}  // 客户端无需监听

        std::shared_ptr<TCPConnectSocket> Accept() override
        {
            return nullptr;  // 客户端不处理accept
        }
    };

    // UDP服务端套接字
    class UDPServerSocket : public Socket
    {
    public:
        UDPServerSocket(const std::string& ip, in_port_t port)
            : Socket(ip, port)
        {
            Initialize();
        }

        void CreateSocket() override
        {
            Socket::CreateSocket(SOCK_DGRAM);  // UDP类型
        }

        int Receive(std::string& recv_msg, InetAddr* peer = nullptr) override
        {
            MutexModule::LockGuard lock(_mutex);
            char buffer[BUFFER_SIZE];
            ssize_t size = ::recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, 
                                     peer->NetAddrPtr(), &peer->AddrLen());
            *peer = InetAddr(peer->NetAddr());
            if (size <= 0)
            {
                recv_msg.clear();
                if (size < 0)
                    LOG(LogLevel::ERROR) << "recvfrom error: " << strerror(errno);
                return size;
            }
            buffer[size] = '\0';
            recv_msg = buffer;
            return size;
        }

        int Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
        {
            MutexModule::LockGuard lock(_mutex);
            if (peer.Ip().empty())  // 确保目标地址有效
            {
                LOG(LogLevel::ERROR) << "sendto error: 目标地址为空";
                return -1;
            }
            ssize_t size = ::sendto(_sockfd, send_msg.c_str(), send_msg.size(), 0,
                                   peer.NetAddrPtr(), peer.AddrLen());
            if (size == -1)
                LOG(LogLevel::ERROR) << "sendto error: " << strerror(errno);
            return size;
        }

    protected:  // 修正访问权限并添加override
        void Bind() override
        {
            int n = ::bind(_sockfd, _addr.NetAddrPtr(), _addr.AddrLen());
            if(n == -1)
            {
                LOG(LogLevel::FATAL) << "bind: 绑定地址信息失败! " << strerror(errno);
                throw std::runtime_error("bind failed");
            }
        }

        void Listen() override {}  // UDP无需监听
        void Connect() override {}  // UDP服务端无需主动连接

        std::shared_ptr<TCPConnectSocket> Accept() override
        {
            return nullptr;  // UDP不支持accept
        }
    };

    // UDP客户端套接字
    class UDPClientSocket : public Socket
    {
    public:
        UDPClientSocket(const std::string& ip, in_port_t port)
            : Socket(ip, port)
        {
            Initialize();
        }

        void CreateSocket() override
        {
            Socket::CreateSocket(SOCK_DGRAM);  // UDP类型
        }

        int Receive(std::string& recv_msg, InetAddr* peer = nullptr) override
        {
            MutexModule::LockGuard lock(_mutex);
            char buffer[BUFFER_SIZE];
            ssize_t size = ::recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, 
                                     peer->NetAddrPtr(), &peer->AddrLen());
            *peer = InetAddr(peer->NetAddr());
            if (size <= 0)
            {
                recv_msg.clear();
                if (size < 0)
                    LOG(LogLevel::ERROR) << "recvfrom error: " << strerror(errno);
                return size;
            }
            buffer[size] = '\0';
            recv_msg = buffer;
            return size;
        }

        int Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
        {
            MutexModule::LockGuard lock(_mutex);
            InetAddr target = peer.Ip().empty() ? _addr : peer;  // 支持默认目标地址
            ssize_t size = ::sendto(_sockfd, send_msg.c_str(), send_msg.size(), 0,
                                   target.NetAddrPtr(), target.AddrLen());
            if (size == -1)
                LOG(LogLevel::ERROR) << "sendto error: " << strerror(errno);
            return size;
        }

    protected:  // 修正访问权限并添加override
        void Bind() override {}  // UDP客户端通常不绑定
        void Listen() override {}  // UDP无需监听
        void Connect() override {}  // UDP无需连接

        std::shared_ptr<TCPConnectSocket> Accept() override
        {
            return nullptr;  // UDP不支持accept
        }
    };
}

deepseek也是给出了中肯的评价:

相关推荐
你要飞16 小时前
Hexo + Butterfly 博客添加 Live2D 看板娘指南
笔记
ajsbxi20 小时前
【Java 基础】核心知识点梳理
java·开发语言·笔记
呱呱巨基20 小时前
vim编辑器
linux·笔记·学习·编辑器·vim
新子y20 小时前
【小白笔记】普通二叉树(General Binary Tree)和二叉搜索树的最近公共祖先(LCA)
开发语言·笔记·python
聪明的笨猪猪20 小时前
Java JVM “调优” 面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
爱学习的uu20 小时前
CURSOR最新使用指南及使用思路
人工智能·笔记·python·软件工程
YuCaiH20 小时前
Linux文件处理
linux·笔记·嵌入式
Cathy Bryant21 小时前
大模型损失函数(二):KL散度(Kullback-Leibler divergence)
笔记·神经网络·机器学习·数学建模·transformer
qq_3985865421 小时前
Threejs入门学习笔记
javascript·笔记·学习
hour_go1 天前
TCP/IP协议相关知识点
网络·笔记·网络协议·tcp/ip