当前位置: 首页 > article >正文

Socket封装---模板方法类

目录

一、模板方法类

二、Socket的框架

三、TCPSocket对父类的虚函数重写


       在平时写网络代码的时候,经常会涉及到socket套接字的部分,这一部分用的十分频繁,但是流程都是固定的,我实在是饱受其苦,但是由于C++不像java一样有已经封装好的socket和serverSock类。所以我在想能不能自己把套接字封装一下,这样以后在使用的时候就会十分便捷。

一、模板方法类

        模板方法模式(Template Method Pattern)是一种行为设计模式,它在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变算法的结构即可重定义算法的某些特定步骤。

        模板方法类通常适用于固定的步骤,但是不同人使用这个相同的步骤可能会有细小的差别。

二、Socket的框架

        唯一要注意的一点就是这里使用了智能指针,将Socket包裹成了shared_ptr,但是使用SockPtr的时候,仍然可以传他的子类TCPSocket进去,以后还是能用SockPtr来调用成员函数的。从这里可以体现出继承和多态的特性。

class Socket;
    //因为这里要用到Socket这个类,但是此时他还没有被定义出来,所以我们在他前面声明一下,告诉编译器他的定义在后面,让我们在这一行可以使用他
    using SockPtr = std::shared_ptr<Socket>;
    
    // 基本套接字:只负责提供接口,他的子类通过让接口做组合,完成固定的逻辑
    class Socket
    {
    public:
        
    public:
        virtual void CreateSocketOrDie() = 0;
        virtual void CreateBindOrDie(uint16_t port) = 0;
        virtual void CreateListenOrDie(int backlog = gbacklog) = 0;
        virtual SockPtr Accepter(InetAddr *clientAddr) = 0;
        virtual bool Connector(const std::string& peerip,uint16_t peerport) = 0;
        virtual int sockfd()=0;
        virtual void Close()=0;

        virtual int Recv(std::string* out)=0;
        virtual int Send(const std::string& in)=0;

    public:
        //父类中只是对虚函数进行组合,以固定的逻辑顺序执行函数组合。
        //但是实际上调用的时候用父类的指针或者引用调用BuildListenSocket这种函数,会走子类重写的函数,这是多态的特性
        void BuildListenSocket(uint16_t port)
        {
            // 1.创建套接字
            CreateSocketOrDie();
            // 2.绑定
            CreateBindOrDie(port);
            // 3.设置监听状态
            CreateListenOrDie();
        }

        bool BuildClientSocket(const std::string& peerip,uint16_t peerport)
        {
            //1.创建套接字
            CreateSocketOrDie();
            //2.连接
            return Connector(peerip,peerport);
        }
        
    };

三、TCPSocket对父类的虚函数重写

class TcpSocket : public Socket
    {
    public:
        TcpSocket() {}
        TcpSocket(int sockfd) : _sockfd(sockfd)
        {}
        ~TcpSocket()
        {}

        void CreateSocketOrDie() override
        {
            // 1. 创建socket
            _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                LOG(FATAL, "socket create error\n");
                exit(SOCKET_ERROR);
            }
            LOG(INFO, "socket create success, sockfd: %d\n", _sockfd); // 3
        }
        void CreateBindOrDie(uint16_t port) override
        {
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(port);
            local.sin_addr.s_addr = INADDR_ANY;

            // 2. bind sockfd 和 Socket addr
            if (::bind(_sockfd, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                LOG(FATAL, "bind error\n");
                exit(BIND_ERROR);
            }

            LOG(INFO, "bind success, sockfd: %d\n", _sockfd);
        }
        void CreateListenOrDie(int backlog) override
        {
            // 3. 因为tcp是面向连接的,tcp需要未来不断地能够做到获取连接
            if (::listen(_sockfd, backlog) < 0)
            {
                LOG(FATAL, "listen error\n");
                exit(LISTEN_ERR);
            }
            LOG(INFO, "listen success\n");
        }
        SockPtr Accepter(InetAddr *clientAddr) override
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            // 4. 获取新连接
            int sockfd = ::accept(_sockfd, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                LOG(WARNING, "accept error\n");
                return nullptr;
            }
            *clientAddr = InetAddr(client);
            LOG(INFO, "get a new link, client info : %s, sockfd is : %d\n", clientAddr->AddrStr().c_str(), sockfd);
            return std::make_shared<TcpSocket>(sockfd);
        }
        bool Connector(const std::string& peerip,uint16_t peerport) override
        {
            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_port = htons(peerport);
            ::inet_pton(AF_INET, peerip.c_str(), &server.sin_addr);

            int n = ::connect(_sockfd, (struct sockaddr *)&server, sizeof(server));
            if (n < 0)
            {
                return false;
            }
            return true;
        }

        int sockfd() override
        {
            return _sockfd;
        }

        void Close() override
        {
            if(_sockfd>0)
            {
                ::close(_sockfd);
            }
        }

        int Recv(std::string* out)override
        {
            char inbuffer[1024]; // 当做字符串
            int n = ::recv(_sockfd, inbuffer, sizeof(inbuffer) - 1,0);
            if (n > 0)
            {
                inbuffer[n] = 0;
                //覆盖式不可取,要使用+=
                *out+=inbuffer;
            }
            return n;
        }
        int Send(const std::string& in)override
        {
            return ::send(_sockfd,in.c_str(),in.size(),0);
        }
        
    private:
        int _sockfd; // 可以是listen也可是普通套接字
    };

    // //使用的时候:
    // Socket* sock=new TcpSocket();
    // sock->BuildListenSocket();

}

这上面除了封装代码,还使用到了LOG日志,这个在我之前的笔记中有写到。当然除了TCP可以对他进行重写,你还可以定义一个UDPSocket类,与上述类似。


http://www.kler.cn/a/594651.html

相关文章:

  • SvelteKit 最新中文文档教程(6)—— 状态管理
  • created在vue3 script setup中的写法
  • 新增菜品-02.代码开发2
  • 基于CVX优化器的储能电池调峰调频算法matlab仿真
  • 算法训练篇06--力扣611.有效三角形的个数
  • 【蓝桥杯速成】| 8.回溯算法
  • 什么是 HTML 实体,常见的 HTML 实体有哪些用途?
  • DeepSeek引爆AIoT革命:华奥系科技领跑“万物智联”时代
  • 实现Token无感刷新
  • 合批Batching
  • 【DR_CAN-最优控制笔记】02.动态规划_Dynamic Programming_基本概念
  • 力扣977. 有序数组的平方(双指针技巧)
  • 三、小白学JAVA-比较运算符与循环
  • Java设计模式之责任链模式
  • UML(统一建模语言)中总共有哪些图
  • ubuntu20如何升级nginx到最新版本(其它版本大概率也可以)
  • git工作区、暂存区、本地仓库、远程仓库的区别和联系
  • 高主频CPU+RTX4090:AI生图性能优化超150%
  • [C++面试] 迭代器面试点(难点)
  • 【docker】--- 详解 WSL2 中的 Ubuntu 和 Docker Desktop 的区别和关系!