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

封装Socket编程接口

一、Socket编程接口与TCP/UDP的关系

Socket是网路通信接口,介于传输层和应用层之间,其封装了传输层的TCP/UDP协议以及网络层的IP协议,允许开发者通过调用编程接口选择使用TCP或UDP协议来实现不同的通信需求。

TCP协议特点:

  • 面向连接:通过三次握手建立连接(第一次握手:客户端调用connect函数向服务端申请建立连接;第二次握手:服务端处于监听状态,接收客户端的连接;第三次握手:客户端连接服务端成功,connect函数返回成功),这种机制保证了通信双方状态同步,但是增加了延迟和资源开销
  • 可靠传输:保证数据传输的可靠性和顺序性,如果数据丢失,发送方会重新发送,如果数据顺序打乱,会按数据序列号重新排序
  • 面向字节流:将数据当作连续的字节流,没有明确的数据边界,多个数据包可能被合并或者拆分,需要自定义数据边界,解决粘包问题
  • 流量与拥塞控制:通过滑动窗口机制动态调整发送速率,避免接收方缓冲区溢出;采用拥塞控制算法​(如慢启动、快速重传)适应网络状况。例如,网络拥塞时自动降低发送速率
  • 适用场景:网页浏览、文件传输

UDP协议特点:

  • 无连接:直接发送数据包,无需建立连接(类似于发短信,无需确认对方是否在线)
  • 不可靠传输:不保证数据传输的可靠性,数据可能丢失、重复或乱序,且不进行重传。例如视频通话时偶尔丢帧不影响体验,如果数据重传会导致卡顿
  • 面向数据报:每个数据包(数据报)都有固定报头,独立传输,不会合并和拆分
  • 无流量与拥塞控制:发送方以恒定速率发送数据,可能加剧网络拥塞或导致丢包。例如,直播中网络波动时,UDP不会降低码率,可能造成画面模糊
  • 适用场景:实时视频,在线游戏

二、封装Socket编程接口

利用模板方法模式封装Socket编程TCP协议的接口(UDP协议接口尚未实现)

Gitee链接:encapsulated_socket · 周不才/cpp_linux study - 码云 - 开源中国

源代码:

Socket.hpp(Log.hpp和InetAddr.hpp见码云)

// 封装socket编程接口

#pragma once
#include <iostream>
#include <string>
#include <memory>//提供智能指针
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp" //日志文件
#include "InetAddr.hpp"//IP地址:点分十进制和in_addr格式转换

namespace socket_ns
{
    using namespace log_ns;

    static const int gbacklog=8;//监听队列最大数量

    class Socket;

    // 自定义错误码
    enum
    {
        SOCKET_ERROR = 1,
        BIND_ERROR,
        LISTENERROR
    };

    // 抽象类,父类
    class Socket
    {
    public:
        // 创建套接字
        virtual void CreateSocket()=0;
        //套接字绑定地址
        virtual void BindSocket(uint16_t port)=0;
        //服务器设置为监听模式
        virtual void BeginListen(int backlog=gbacklog)=0;
        //接收客户端连接
        virtual std::shared_ptr<Socket> Accept(InetAddr* clientaddr)=0;
        //客户端请求连接
        virtual bool Connect(const std::string& ip, uint16_t port)=0;
        //关闭套接字
        virtual void CloseSocket()=0;
        //获取套接字文件描述符
        virtual int GetSockfd()=0;
        //发送数据
        virtual ssize_t Send(const std::string& in)=0;
        //接收数据
        virtual ssize_t Receive(std::string* out)=0;

    public:
        //服务端开始监听
        void StartListening(uint16_t port,int backlog=gbacklog)
        {
            //参数说明
            //port是端口号

            CreateSocket();//创建套接字
            BindSocket(port);//套接字绑定地址
            BeginListen(backlog);//服务端设置为监听状态

        }
        //客户端开始连接
        bool StartConnecting(const std::string& ip, uint16_t port)
        {
            //参数说明
            //ip和port是请求连接服务端的ip地址和端口号

            CreateSocket();//创建套接字
            return Connect(ip,port);//客户端请求连接服务端
        }
    };

    

    // TCP子类
    class TcpSocket : public Socket
    {
    private:
        int _sockfd; // 套接字文件描述符
    public:
        //无参构造函数
        TcpSocket()
        {}
        //有参构造函数
        TcpSocket(int sockfd)
            :_sockfd(sockfd)
        {}
    public:
        //创建套接字
        virtual void CreateSocket() override
        {
            _sockfd = socket(AF_INET, SOCK_STREAM, 0); // IPv4协议,TCP协议
            if (_sockfd < 0)
            {
                LOG(FATAL,"socket create error\n");
                exit(SOCKET_ERROR);
            }
            LOG(INFO,"socket create success\n");
        }
        //绑定套接字
        virtual void BindSocket(uint16_t port) override
        {
            //参数说明
            //port是端口号,绑定套接字需要端口号

            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;

            if(bind(_sockfd,(struct sockaddr*)&local,sizeof(local))<0)
            {
                LOG(FATAL,"socket bind error\n");
                exit(BIND_ERROR);
            }
            LOG(INFO,"socket bind success\n");
        }
        //服务器设置为监听模式
        virtual void BeginListen(int backlog=gbacklog) override
        {
            //参数说明
            //backlog是监听队列最大数量,默认为gbacklog

            if(listen(_sockfd,backlog)<0)
            {
                LOG(FATAL,"listen error\n");
                exit(LISTENERROR);
            }
            LOG(INFO,"listen success\n");
        }
        //接收客户端连接
        virtual std::shared_ptr<Socket> Accept(InetAddr* clientaddr) override
        {
            //参数说明
            //clientaddr是输出型参数,用于带出连接的客户端的地址信息

            struct sockaddr_in client;
            memset(&client,0,sizeof(client));
            socklen_t len=sizeof(client);
            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, ip: %s, sockfd: %d\n",clientaddr->AddrStr().c_str(),sockfd);
            return std::make_shared<TcpSocket>(sockfd);//用连接成功后获取到的监听套接字创建子类TcpSokcet的对象并使用智能指针管理
        }
        //客户端请求连接
        virtual bool Connect(const std::string& ip, uint16_t port) override
        {
            //参数说明
            //ip和port是请求连接的服务端的ip地址和端口号

            struct sockaddr_in server;
            memset(&server,0,sizeof(server));
            server.sin_family=AF_INET;
            server.sin_port=htons(port);
            inet_pton(AF_INET,ip.c_str(),&server.sin_addr.s_addr);
            if(connect(_sockfd,(struct sockaddr*)&server,sizeof(server))<0)
                return false;
            else
                return true;
        }
        //关闭套接字
        virtual void CloseSocket() override
        {
            if(_sockfd>0)
                close(_sockfd);
        }
        //获取套接字文件描述符
        virtual int GetSockfd() override
        {
            return _sockfd;
        }
        //发送数据
        virtual ssize_t Send(const std::string& in) override
        {
            //参数说明
            //in是要发送的数据

            return write(_sockfd,in.c_str(),in.size());
        }
        //接收数据
        virtual ssize_t Receive(std::string* out) override
        {
            //参数说明
            //out是输出型参数,用于存储读取到的数据

            char buffer[1024];//数据缓冲区,存储读取到的数据
            ssize_t n=read(_sockfd,buffer,sizeof(buffer));
            if(n>0)
            {
                buffer[n]=0;//数据缓冲区末尾设置为0,防止读取到残留数据
                *out+=buffer;
            }
            return n;
        }
    };
}

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

相关文章:

  • 蓝桥杯--冲刺题单--随时更新
  • 物联网平台与边缘计算网关的深度结合应用
  • Spring Boot 集成 Kafka 消息发送方案
  • ‌C# I/O 核心用法
  • 【工程实践/大批量文件处理】文件排序
  • 笛卡尔轨迹规划之齐次变换矩阵与欧拉角、四元数的转化
  • 数据类设计_图片类设计之7_矩阵图形类设计更新_实战之页面简单设计(前端架构)
  • VLLM专题(十九)—兼容 OpenAI 的服务器
  • Matplotlib 柱形图
  • 波场trx质押能量租赁平台开发
  • 通信网络安全防护风险评估报告怎么写?范文模版分享
  • 剑指 Offer II 113. 课程顺序
  • NPN三极管基极接稳压管的作用
  • 识别并脱敏上传到deepseek/chatgpt的文本文件中的护照信息
  • companion object和object 从kotlin转java分析
  • Python+selenium,轻松搭建Web自动化测试框架
  • 稳定运行的以PostgreSQL数据库为数据源和目标的ETL性能变差时提高性能方法和步骤
  • 【MyDB】7-客户端服务端通信之1-服务端客户端通信实现前言
  • deque
  • sql批量修改字段某一指定部分+修改重复编号