[C++]sylar高性能服务器框架学习记录:Socket模块

记录一下最近这学期学习的sylar服务器框架项目,输出整理一下项目的结构,用到的知识和自己的体会

项目仓库地址

https://github.com/sylar-yin/sylar/

整理博客过程中参考的大佬资料链接:

============================================

基础介绍

该模块主要包含Socket类,对socket的一些方法进行了封装

成员变量存储了socketfd,地址协议族,套接字类型,协议,是否连接以及Address::ptr格式的本地和远端地址

class Socket : public std::enable_shared_from_this
<Socket>, Noncopyable {
public:
    typedef std::shared_ptr
<Socket> ptr;
    typedef std::weak_ptr
<Socket> weak_ptr;

    enum Type {             // socket类型
        TCP = SOCK_STREAM,  // TCP
        UDP = SOCK_DGRAM    // UDP
    };

    enum Family {           // 地址协议族
        IPv4 = AF_INET,     // IPv4
        IPv6 = AF_INET6,    // IPv6
        UNIX = AF_UNIX      // UNIX
    };

    static Socket::ptr CreateTCP(sylar::Address::ptr address);  // 根据地址创建TCP套接字(自动选择地址协议族)
    static Socket::ptr CreateUDP(sylar::Address::ptr address);  // 根据地址创建UDP套接字(自动选择地址协议族)

    static Socket::ptr CreateTCPSocket();                       // 创建IPv4TCP套接字
    static Socket::ptr CreateUDPSocket();                       // 创建IPv4UDP套接字

    static Socket::ptr CreateTCPSocket6();                      // 创建IPv6TCP套接字
    static Socket::ptr CreateUDPSocket6();                      // 创建IPv6UDP套接字

    static Socket::ptr CreateUnixTCPSocket();                   // 创建UNIXTCP套接字
    static Socket::ptr CreateUnixUDPSocket();                   // 创建UNIXUDP套接字

    Socket(int family, int type, int protocol = 0);             // 构造函数
    ~Socket();                                                  // 析构函数

    int64_t getSendTimeout();                                   // 获取发送超时时间
    void setSendTimeout(int64_t v);                             // 设置发送超时时间

    int64_t getRecvTimeout();                                   // 获取接收超时时间
    void setRecvTimeout(int64_t v);                             // 设置接收超时时间

    bool getOption(int level, int option, void* result, size_t* len);           // 获取socketfd信息
    template<class T>
    bool getOption(int level, int option, T& result) {                          // 获取socketfd信息(模板函数)
        size_t length = sizeof(T);
        return getOption(level, option, &result, &length);
    }

    bool setOption(int level, int option, const void* result, socklen_t len);   // 设置socketfd信息
    template<class T>
    bool setOption(int level, int option, const T& value) {                     // 设置socketfd信息(模板函数)
        return setOption(level, option, &value, sizeof(T));
    }

    Socket::ptr accept();                                               // 接收connect()连接(服务端)
    bool bind(const Address::ptr addr);                                 // 绑定套接字到一个地址上(服务端)
    bool connect(const Address::ptr addr, uint64_t timeout_ms = -1);    // 连接地址(客户端)
    bool listen(int backlog = SOMAXCONN);                               // 将套接字设置为监听模式(服务端)
    bool close();                                                       // 关闭socket

    int send(const void* buffer, size_t length, int flags = 0);                             // 发送数据(单数据块)
    int send(const iovec* buffer, size_t length, int flags = 0);                            // 发送数据(多数据块)
    int sendTo(const void* buffer, size_t length, const Address::ptr to, int flags = 0);    // 指定地址发送数据(单数据块)
    int sendTo(const iovec* buffer, size_t length, const Address::ptr to, int flags = 0);   // 指定地址发送数据(多数据块)

    int recv(void* buffer, size_t length, int flags = 0);                                   // 接收数据(单数据块)
    int recv(iovec* buffer, size_t length, int flags = 0);                                  // 接收数据(多数据块)
    int recvFrom(void* buffer, size_t length, Address::ptr from, int flags = 0);            // 指定地址接收数据(单数据块)
    int recvFrom(iovec* buffer, size_t length, Address::ptr from, int flags = 0);           // 指定地址接收数据(多数据块)

    Address::ptr getRemoteAddress();                    // 获取远端地址
    Address::ptr getLocalAddress();                     // 获取本地地址

    int getFamily() const { return m_family; }
    int getType() const { return m_type; }
    int getProtocol() const { return m_protocol; }

    bool isConnected() const { return m_isConnected; }
    bool isValid() const;   // 返回socketfd是否有效
    int getError();         // 返回socketfd错误

    std::ostream& dump(std::ostream& os) const;
    int getSocket() const { return m_sock; }

    bool cancelRead();      // 取消读事件
    bool cancelWrite();     // 取消写事件
    bool cancelAccept();    // 取消accept
    bool cancelAll();       // 取消所有事件
private:
    void initSock();            // 初始化socketfd
    void newSock();             // 创建socketfd
    bool init(int sock);        // 初始化accept的用于通信的socket的函数
private:
    int m_sock;                     // socketfd
    int m_family;                   // 地址协议族
    int m_type;                     // 套接字类型
    int m_protocol;                 // 协议,默认传入0
    bool m_isConnected;             // 是否连接

    Address::ptr m_localAddress;    // 本地地址
    Address::ptr m_remoteAddress;   // 远端地址
};

std::ostream& operator<<(std::ostream& os, const Socket& sock);

构造/析构函数

// 构造函数
Socket::Socket(int family, int type, int protocol)
    : m_sock(-1)
    , m_family(family)
    , m_type(type)
    , m_protocol(protocol)
    ,m_isConnected(false) {
}

// 析构函数
Socket::~Socket() {
    close();
}

设置超时时间/socketfd信息

int64_t Socket::getSendTimeout() {
    FdCtx::ptr ctx = FdMgr::GetInstance()->get(m_sock);
    if(ctx) {
        return ctx->getTimeout(SO_SNDTIMEO);
    }
    return -1;
}

void Socket::setSendTimeout(int64_t v) {
    struct timeval tv{int(v / 1000), int(v % 1000 * 1000)};
    setOption(SOL_SOCKET, SO_SNDTIMEO, tv);
}

int64_t Socket::getRecvTimeout() {
    FdCtx::ptr ctx = FdMgr::GetInstance()->get(m_sock);
    if(ctx) {
        return ctx->getTimeout(SO_RCVTIMEO);
    }
    return -1;
}

void Socket::setRecvTimeout(int64_t v) {
    struct timeval tv{int(v / 1000), int(v % 1000 * 1000)};
    setOption(SOL_SOCKET, SO_RCVTIMEO, tv);
}

bool Socket::getOption(int level, int option, void* result, size_t* len) {
    int rt = getsockopt(m_sock, level, option, result, (socklen_t*)len);
    if(rt) {
        SYLAR_LOG_ERROR(g_logger) << "setOption sock=" << m_sock
            << " level=" << level << " option=" << option
            << " errno=" << errno << " errnostr=" << strerror(errno);
        return false;
    }
    return true;
}

bool Socket::setOption(int level, int option, const void* result, socklen_t len) {
    if(setsockopt(m_sock, level, option, result, (socklen_t)len)) {
        SYLAR_LOG_ERROR(g_logger) << "setOption sock=" << m_sock
            << " level=" << level << " option=" << option
            << " errno=" << errno << " errnostr=" << strerror(errno);
        return false;
    }
    return true;
}

accept函数

运行在服务端,接收connect连接

Socket::ptr Socket::accept() {
    // 创建用于通信的socket对象
    Socket::ptr sock(new Socket(m_family, m_type, m_protocol));
    // 调用accept()接口(已被hook),获取用于通信的socket
    int newsock = ::accept(m_sock, nullptr, nullptr);
    if(newsock == -1) {
        SYLAR_LOG_ERROR(g_logger) << "accept(" << m_sock << ") errno="
            << errno << " errstr=" << strerror(errno);
        return nullptr;
    }
    // init处理新创建的用于通信的socket
    if(sock->init(newsock)) {
        return sock;
    }
    return nullptr;
}

// 初始化accept的用于通信的socket的函数
bool Socket::init(int sock) {
    // 注册到FdManager
    FdCtx::ptr ctx = FdMgr::GetInstance()->get(sock);
    // 初始化对象成员
    if(ctx && ctx->isSocket() && !ctx->isClose()) {
        m_sock = sock;
        m_isConnected = true;
        initSock();
        getLocalAddress();
        getRemoteAddress();
        return true;
    }
    return false;
}

bind函数

服务端使用,用于绑定socket到一个地址上

bool Socket::bind(const Address::ptr addr) {
    if(!isValid()) {    // 如果没有socketfd
        newSock();      // 创建socketfd
        if(SYLAR_UNLIKELY(!isValid())) {
            return false;
        }
    }

    // 检验地址族是否匹配
    if(SYLAR_UNLIKELY(addr->getFamily() != m_family)) {
        SYLAR_LOG_ERROR(g_logger) << "bind sock.family("
            << m_family << ") addr.family(" << addr->getFamily()
            << ") not equal, addr=" << addr->toString();
        return false;
    }

    // 调用bind()接口(已被hook)绑定地址
    if(::bind(m_sock, addr->getAddr(), addr->getAddrLen())) {
        SYLAR_LOG_ERROR(g_logger) << "bind error errno=" << errno
            << " errstr=" << strerror(errno);
        return false;
    }

    // 配置本地地址信息
    getLocalAddress();
    return true;
}

connect函数

客户端使用,用于连接地址

bool Socket::connect(const Address::ptr addr, uint64_t timeout_ms) {
    if(!isValid()) {
        newSock();
        if(SYLAR_UNLIKELY(!isValid())) {
            return false;
        }
    }

    if(SYLAR_UNLIKELY(addr->getFamily() != m_family)) {
        SYLAR_LOG_ERROR(g_logger) << "connect sock.family("
            << m_family << ") addr.family(" << addr->getFamily()
            << ") not equal, addr=" << addr->toString();
        return false;
    }

    if(timeout_ms == (uint64_t)-1) {    // 有超时时间
        // 调用connect()接口(已被hook)
        if(::connect(m_sock, addr->getAddr(), addr->getAddrLen())) {
            SYLAR_LOG_ERROR(g_logger) << "sock=" << m_sock << "connect(" << addr->toString()
                << ") error errno=" << errno << " errstr=" << strerror(errno);
            close();
            return false;
        }
    } else {                            // 没有超时时间
        // 调用connect_with_timeout()
        if(::connect_with_timeout(m_sock, addr->getAddr(), addr->getAddrLen(), timeout_ms)) {
            SYLAR_LOG_ERROR(g_logger) << "sock=" << m_sock << "connect(" << addr->toString()
                << ") timeout=" << timeout_ms << "error errno=" 
                << errno << " errstr=" << strerror(errno);
            close();
            return false;
        }
    }

    // 更新socket对象状态
    m_isConnected = true;
    getRemoteAddress();
    getLocalAddress();
    return true;
}

listen函数

服务端使用,用于将套接字设置为监听模式

// 将套接字设置为监听模式,等待连接请求
bool Socket::listen(int backlog) {
    if(!isValid()) {
        SYLAR_LOG_ERROR(g_logger) << "listen error sock=-1";
        return false;
    }
    // 调用listen()接口,设置socketfd为监听模式
    if(::listen(m_sock, backlog)) {
        SYLAR_LOG_ERROR(g_logger) << "listen error errno=" << errno
            << " errstr=" << strerror(errno);
        return false;
    }
    return true;
}

close函数

用于关闭socket

bool Socket::close() {
    if(!isConnected() && m_sock == -1) {
        return true;
    }
    m_isConnected = false;
    if(m_sock != -1) {
        // 调用close()接口(已被hook)
        ::close(m_sock);
        m_sock = -1;
    }
    return false;
}

send系列函数

发送数据

int Socket::send(const void* buffer, size_t length, int flags) {
    if(isConnected()) {
        return ::send(m_sock, buffer, length, flags);
    }
    return -1;
}

int Socket::send(const iovec* buffer, size_t length, int flags) {
    if(isConnected()) {
        msghdr msg;
        memset(&msg, 0, sizeof(msg));
        msg.msg_iov = (iovec*)buffer;
        msg.msg_iovlen = length;
        return ::sendmsg(m_sock, &msg, flags);
    }
    return -1;
}

int Socket::sendTo(const void* buffer, size_t length, const Address::ptr to, int flags) {
    if(isConnected()) {
        return ::sendto(m_sock, buffer, length, flags, to->getAddr(), to->getAddrLen());
    }
    return -1;
}

int Socket::sendTo(const iovec* buffer, size_t length, const Address::ptr to, int flags) {
    if(isConnected()) {
        msghdr msg;
        memset(&msg, 0, sizeof(msg));
        msg.msg_iov = (iovec*)buffer;
        msg.msg_iovlen = length;
        msg.msg_name = to->getAddr();
        msg.msg_namelen = to->getAddrLen();
        return ::sendmsg(m_sock, &msg, flags);
    }
    return -1;
}

recv系列函数

接收数据

int Socket::recv(void* buffer, size_t length, int flags) {
    if(isConnected()) {
        return ::recv(m_sock, buffer, length, flags);
    }
    return -1;
}

int Socket::recv(iovec* buffer, size_t length, int flags) {
    if(isConnected()) {
        msghdr msg;
        memset(&msg, 0, sizeof(msg));
        msg.msg_iov = (iovec*)buffer;
        msg.msg_iovlen = length;
        return ::recvmsg(m_sock, &msg, flags);
    }
    return -1;
}

int Socket::recvFrom(void* buffer, size_t length, Address::ptr from, int flags) {
    if(isConnected()) {
        socklen_t len = from->getAddrLen();
        return ::recvfrom(m_sock, buffer, length, flags, from->getAddr(), &len);
    }
    return -1;
}

int Socket::recvFrom(iovec* buffer, size_t length, Address::ptr from, int flags) {
    if(isConnected()) {
        msghdr msg;
        memset(&msg, 0, sizeof(msg));
        msg.msg_iov = (iovec*)buffer;
        msg.msg_iovlen = length;
        msg.msg_name = from->getAddr();
        msg.msg_namelen = from->getAddrLen();
        return ::recvmsg(m_sock, &msg, flags);
    }
    return -1;
}

获取本地和远端地址

Address::ptr Socket::getRemoteAddress() {
    if(m_remoteAddress) {
        return m_remoteAddress;
    }

    Address::ptr result;
    switch(m_family) {
        case AF_INET:
            result.reset(new IPv4Address());
            break;
        case AF_INET6:
            result.reset(new IPv6Address());
            break;
        case AF_UNIX:
            result.reset(new UnixAddress());
            break;
        default:
            result.reset(new UnknownAddress(m_family));
            break;
    }
    socklen_t addrlen = result->getAddrLen();
    // 调用getpeername()获取socket连接的对等方的地址
    if(getpeername(m_sock, result->getAddr(), &addrlen)) {
        SYLAR_LOG_ERROR(g_logger) << "getpeername error sock=" << m_sock
            << " errno=" << errno << " errstr=" << strerror(errno);
        return Address::ptr(new UnknownAddress(m_family));
    }
    if(m_family == AF_UNIX) {
        UnixAddress::ptr addr = std::dynamic_pointer_cast
<UnixAddress>(result);
        addr->setAddrLen(addrlen);
    }
    m_remoteAddress = result;
    return m_remoteAddress;
}

Address::ptr Socket::getLocalAddress() {
    if(m_localAddress) {
        return m_localAddress;
    }

    Address::ptr result;
    switch(m_family) {
        case AF_INET:
            result.reset(new IPv4Address());
            break;
        case AF_INET6:
            result.reset(new IPv6Address());
            break;
        case AF_UNIX:
            result.reset(new UnixAddress());
            break;
        default:
            result.reset(new UnknownAddress(m_family));
            break;
    }
    socklen_t addrlen = result->getAddrLen();
    // 调用getsockname()获取socket连接的本端地址
    if(getsockname(m_sock, result->getAddr(), &addrlen)) {
        SYLAR_LOG_ERROR(g_logger) << "getsockname error sock=" << m_sock
            << " errno=" << errno << " errstr=" << strerror(errno);
        return Address::ptr(new UnknownAddress(m_family));
    }
    if(m_family == AF_UNIX) {
        UnixAddress::ptr addr = std::dynamic_pointer_cast
<UnixAddress>(result);
        addr->setAddrLen(addrlen);
    }
    m_localAddress = result;
    return m_localAddress;
}

字符串方法

用于日志输出调试

std::ostream& Socket::dump(std::ostream& os) const {
    os << "[Socket sock=" << m_sock
       << " is_connected=" << m_isConnected
       << " family=" << m_family
       << " type=" << m_type
       << " protocol=" << m_protocol;
    if(m_localAddress) {
        os << " local_address" << m_localAddress->toString();
    }
    if(m_remoteAddress) {
        os << " remote_address" << m_remoteAddress->toString();
    }
    os << "]";
    return os;
}

取消socketfd上的事件

bool Socket::cancelRead() {
    return IOManager::GetThis()->cancelEvent(m_sock, sylar::IOManager::READ);
}

bool Socket::cancelWrite() {
    return IOManager::GetThis()->cancelEvent(m_sock, sylar::IOManager::WRITE);
}

bool Socket::cancelAccept() {
    return IOManager::GetThis()->cancelEvent(m_sock, sylar::IOManager::READ);
}

bool Socket::cancelAll() {
    return IOManager::GetThis()->cancelAll(m_sock);
}

测试

尝试连接www.ayanami.blue并发送httpGET请求,接收报文并打印

#include "sylar/socket.h"
#include "sylar/log.h"
#include "sylar/iomanager.h"

static sylar::Logger::ptr g_logger = SYLAR_LOG_ROOT();

void test_socket() {
    sylar::IPAddress::ptr addr = sylar::Address::LookupAnyIPAddress("www.ayanami.blue");
    if(addr) {
        SYLAR_LOG_INFO(g_logger) << "get address: " << addr->toString();
    } else {
        SYLAR_LOG_ERROR(g_logger) << "get address fail";
        return;
    }

    sylar::Socket::ptr sock = sylar::Socket::CreateTCP(addr);
    addr->setPort(80);
    if(!sock->connect(addr)) {
        SYLAR_LOG_ERROR(g_logger) << "connect " << addr->toString() << " fail";
        return;
    } else {
        SYLAR_LOG_INFO(g_logger) << "connect " << addr->toString() << " connected";
    }

    const char buff[] = "GET / HTTP/1.0\r\n\r\n";
    int rt = sock->send(buff, sizeof(buff));
    if(rt <= 0) {
        SYLAR_LOG_ERROR(g_logger) << "send fail rt=" << rt;
        return;
    }

    std::string buffs;
    buffs.resize(4096);
    rt = sock->recv(&buffs[0], buffs.size());

    if(rt <= 0) {
        SYLAR_LOG_ERROR(g_logger) << "recv fail rt=" << rt;
        return;
    }

    buffs.resize(rt);
    SYLAR_LOG_INFO(g_logger) << buffs;
}

int main(int argc, char** argv) {
    sylar::IOManager iom;
    iom.schedule(&test_socket);
    return 0;
}

可以看到成功收到响应报文

image-20260310215022450

总结

对socket相关操作进行了封装,并且结合了之前的Address模块,为后续网络编程提供便利

评论

  1. sankkooos
    Android Chrome 142.0.7444.173
    2 周前
    2026-3-11 18:30:00

    socket~(ノ°ο°)ノ

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇