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也是给出了中肯的评价:

相关推荐
AlexMercer10125 小时前
[前端]1.html基础
前端·笔记·学习·html
楚肽生物小敏5 小时前
Cy3-Tyramide,Cyanine 3 Tyramide; 174961-75-2
笔记
健康平安的活着7 小时前
langchain4j笔记篇(阳哥)
笔记
ホロHoro8 小时前
学习笔记:MYSQL(4)
笔记·学习·mysql
守.护13 小时前
云计算学习笔记——HTTP服务、NFS服务篇
笔记·学习·云计算
wdfk_prog14 小时前
[Linux]学习笔记系列 -- lib/dump_stack.c 栈回溯打印(Stack Trace Dumping) 内核调试与错误诊断的基石
linux·运维·服务器·c语言·笔记·学习
i.ajls14 小时前
无监督学习,推荐系统以及强化学习笔记
笔记·学习·机器学习
聆风吟º14 小时前
【Spring Boot 报错已解决】Web server failed to start. Port 8080 was already in use.
spring boot·笔记·技术干货
Suckerbin14 小时前
LAMPSecurity: CTF6靶场渗透
笔记·安全·web安全·网络安全