ZLToolKit源码阅读:网络基础接口封装类SockUtil

1060 篇文章 307 订阅
本文详细介绍了网络编程中的一些基础接口,如创建TCP客户端和服务器套接字、UDP套接字的创建和绑定,以及相关网络选项的设置。还涵盖了IP地址和端口的操作,如DNS解析、判断IP是否在同一网段、获取套接字的本地和远程IP及端口等。此外,还涉及到了套接字选项的设置,如TCP_NODELAY、SO_REUSEADDR、SO_BROADCAST等,以及组播相关操作。
摘要由CSDN通过智能技术生成

概述

本部分主要看下sockutil.h和sockutil.cpp两个文件。该模块统一封装不同平台下的网络相关API,作为后续网络编程的基础接口

在这里插入图片描述

分析

网络基础接口

在这里插入图片描述

SockUtil::connect(作为客户端并连接服务器)

声明:

 /**
     * 创建tcp客户端套接字并连接服务器
     * @param host 服务器ip或域名
     * @param port 服务器端口号
     * @param async 是否异步连接
     * @param local_ip 绑定的本地网卡ip
     * @param local_port 绑定的本地端口号
     * @return -1代表失败,其他为socket fd号
     */
    static int connect(const char *host, uint16_t port, bool async = true, const char *local_ip = "::", uint16_t local_port = 0);

框架:

int SockUtil::connect(const char *host, uint16_t port, bool async, const char *local_ip, uint16_t local_port) {
    sockaddr_storage addr;
    ......

    int sockfd = (int) socket(addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd < 0) {
        WarnL << "创建套接字失败:" << host;
        return -1;
    }

    .....

    if (bind_sock(sockfd, local_ip, local_port, addr.ss_family) == -1) {
        close(sockfd);
        return -1;
    }

    ....
    
    if (::connect(sockfd, (sockaddr *) &addr, len) == 0) {
        //同步连接成功
        return sockfd;
    }
   
    .....
    
    WarnL << "连接主机失败:" << host << " " << port << " " << get_uv_errmsg(true);
    close(sockfd);
    return -1;
}

SockUtil::listen(创建一个TCP服务端)

声明:

    /**
     * 创建tcp监听套接字
     * @param port 监听的本地端口
     * @param local_ip 绑定的本地网卡ip
     * @param back_log accept列队长度
     * @return -1代表失败,其他为socket fd号
     */
    static int listen(const uint16_t port, const char *local_ip = "::", int back_log = 1024);

关键:

int SockUtil::listen(const uint16_t port, const char *local_ip, int back_log) {
    ...
    if ((fd = (int)socket(family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
        WarnL << "创建套接字失败:" << get_uv_errmsg(true);
        return -1;
    }

    ...

    if (bind_sock(fd, local_ip, port, family) == -1) {
        close(fd);
        return -1;
    }

    //开始监听
    if (::listen(fd, back_log) == -1) {
        WarnL << "开始监听失败:" << get_uv_errmsg(true);
        close(fd);
        return -1;
    }

    return fd;
}

SockUtil::bindUdpSock(创建一个UDP套接字)

int SockUtil::bindUdpSock(const uint16_t port, const char *local_ip, bool enable_reuse) {
    ......
    if ((fd = (int)socket(family, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
        WarnL << "创建套接字失败:" << get_uv_errmsg(true);
        return -1;
    }
    ......

    if (bind_sock(fd, local_ip, port, family) == -1) {
        close(fd);
        return -1;
    }
    return fd;
}

然后我们来看看bind_sock是怎么实现的

bind_sock:绑定socket fd到某个网卡和端口

static int bind_sock(int fd, const char *ifr_ip, uint16_t port, int family) {
    switch (family) {
        case AF_INET: return bind_sock4(fd, ifr_ip, port);
        case AF_INET6: return bind_sock6(fd, ifr_ip, port);
        default: assert(0); return -1;
    }
}

有两种情况:使用的地址是IPv4型的,使用的地址是IPv6型的。但是它们都是调用bind接口来绑定

static int bind_sock4(XXXX, const char *ifr_ip, uint16_t port) {
	struct sockaddr_in addr;
    .....
    if (::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
        WarnL << "绑定套接字失败:" << get_uv_errmsg(true);
        return -1;
    }
    return 0;
}


static int bind_sock6(XXXX, const char *ifr_ip, uint16_t port) {
	 struct sockaddr_in6 addr;
    ......
    if (::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
        WarnL << "绑定套接字失败:" << get_uv_errmsg(true);
        return -1;
    }
    return 0;
}

那它们之间有什么不同吗?没啥不同,参数都是一样的,只是一个将IP和PORT转成sockaddr_in ,一个转成sockaddr_in6

static int bind_sock4(int fd, const char *ifr_ip, uint16_t port) {
    struct sockaddr_in addr;
    bzero(&addr, sizeof(addr));

    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);   
    if (1 != inet_pton(AF_INET, ifr_ip, &(addr.sin_addr))) {
        if (strcmp(ifr_ip, "::")) {
            WarnL << "inet_pton to ipv4 address failed:" << ifr_ip;
        }
        addr.sin_addr.s_addr = INADDR_ANY;
    }	
    .....
}


static int bind_sock6(int fd, const char *ifr_ip, uint16_t port) {
    set_ipv6_only(fd, false);  // -----
    struct sockaddr_in6 addr;
    bzero(&addr, sizeof(addr));
    addr.sin6_family = AF_INET6;
    addr.sin6_port = htons(port);
    if (1 != inet_pton(AF_INET6, ifr_ip, &(addr.sin6_addr))) {
        if (strcmp(ifr_ip, "0.0.0.0")) {
            WarnL << "inet_pton to ipv6 address failed:" << ifr_ip;
        }
        addr.sin6_addr = IN6ADDR_ANY_INIT;
    }
    ...
}

有一个不同的是,当使用IPv6时,需要设置套接字选项IPPROTO_IPV6:

static int set_ipv6_only(int fd, bool flag) {
    int opt = flag;
    int ret = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, sizeof opt);
    if (ret == -1) {
        TraceL << "设置 IPV6_V6ONLY 失败!";
    }
    return ret;
}

当设置了IPPROTO_IPV6之后,就可以设置如下选项了

在这里插入图片描述

dissolveUdpSock(不理解???)

不理解,命名是绑定,为什么说解绑?

   /**
     * @brief 解除与 sock 相关的绑定关系
     * @param sock, socket fd 号
     * @return 0 成功, -1 失败
     */
    static int dissolveUdpSock(int sock);
int SockUtil::dissolveUdpSock(int fd) {
    struct sockaddr_storage addr;
    socklen_t addr_len = sizeof(addr);
    if (-1 == getsockname(fd, (struct sockaddr *)&addr, &addr_len)) {
        return -1;
    }
    addr.ss_family = AF_UNSPEC;
    if (-1 == ::connect(fd, (struct sockaddr *)&addr, addr_len) && get_uv_error() != UV_EAFNOSUPPORT) {
        // mac/ios时返回EAFNOSUPPORT错误
        WarnL << "解除 UDP 套接字连接关系失败: " << get_uv_errmsg(true);
        return -1;
    }
   return 0;
}

IP端口信息

在这里插入图片描述

in_same_lan: 判断两个ip是否为同一网段

#define ip_addr_netcmp(addr1, addr2, mask) (((addr1) & (mask)) == ((addr2) & (mask)))

bool SockUtil::in_same_lan(const char *myIp, const char *dstIp) {
    string mask = get_ifr_mask(get_ifr_name(myIp).data());
    return ip_addr_netcmp(inet_addr(myIp), inet_addr(dstIp), inet_addr(mask.data()));
}

getDomainIP:从域名解析得到IP

声明:

    /**
     * dns解析
     * @param host 域名或ip
     * @param port 端口号
     * @param addr sockaddr结构体
     * @return 是否成功
     */
    static bool getDomainIP(const char *host, uint16_t port, struct sockaddr_storage &addr, int ai_family = AF_INET,
                            int ai_socktype = SOCK_STREAM, int ai_protocol = IPPROTO_TCP, int expire_sec = 60);

关键:

1. 解析IP存储到参数addr中:
bool SockUtil::getDomainIP(const char *host,  ..... struct sockaddr_storage &addr,
                           int ai_family, int ai_socktype, int ai_protocol, int expire_sec) {
    bool flag = DnsCache::Instance().getDomainIP(host, addr, ai_family, ai_socktype, ai_protocol, expire_sec);
	.....
}

2. 设置PORT:
bool SockUtil::getDomainIP(......., uint16_t port, ......) {
                           
    if (解析成功) {
        switch (addr.ss_family ) {
            case AF_INET : ((sockaddr_in *) &addr)->sin_port = htons(port); break;
            case AF_INET6 : ((sockaddr_in6 *) &addr)->sin6_port = htons(port); break;
            default: assert(0); break;
        }
    }
    return flag;
}

实现:

bool SockUtil::getDomainIP(const char *host, uint16_t port, struct sockaddr_storage &addr,
                           int ai_family, int ai_socktype, int ai_protocol, int expire_sec) {
    bool flag = DnsCache::Instance().getDomainIP(host, addr, ai_family, ai_socktype, ai_protocol, expire_sec);
    if (flag) {
        switch (addr.ss_family ) {
            case AF_INET : ((sockaddr_in *) &addr)->sin_port = htons(port); break;
            case AF_INET6 : ((sockaddr_in6 *) &addr)->sin6_port = htons(port); break;
            default: assert(0); break;
        }
    }
    return flag;
}

也就是关键是由DnsCache::Instance()。。。实现的。关于DnsCache::Instance()干了写啥,我们稍后再说

怎么调用:

    sockaddr_storage addr;
    //优先使用ipv4地址
    if (!getDomainIP(host, port, addr, AF_INET, SOCK_STREAM, IPPROTO_TCP)) {
        //dns解析失败
        return -1;
    }

is_ipv4 && is_ipv6:判断是否为ipv4/ipv6地址

bool SockUtil::is_ipv4(const char *host) {
    struct in_addr addr;
    return 1 == inet_pton(AF_INET, host, &addr);
}

bool SockUtil::is_ipv6(const char *host) {
    struct in6_addr addr;
    return 1 == inet_pton(AF_INET6, host, &addr);
}

get_xxx_port,get_xxx_ip:获取fd绑定的本地/远端IP和端口

using getsockname_type = decltype(getsockname);

static string get_socket_ip(int fd, getsockname_type func) {
    struct sockaddr_storage addr;
    socklen_t addr_len = sizeof(addr);
    if (-1 == func(fd, (struct sockaddr *)&addr, &addr_len)) {
        return "";
    }
    return SockUtil::inet_ntoa((struct sockaddr *)&addr);
}

static uint16_t get_socket_port(int fd, getsockname_type func) {
    struct sockaddr_storage addr;
    socklen_t addr_len = sizeof(addr);
    if (-1 == func(fd, (struct sockaddr *)&addr, &addr_len)) {
        return 0;
    }
    return SockUtil::inet_port((struct sockaddr *)&addr);
}

string SockUtil::get_local_ip(int fd) {
    return get_socket_ip(fd, getsockname);
}

string SockUtil::get_peer_ip(int fd) {
    return get_socket_ip(fd, getpeername);
}

uint16_t SockUtil::get_local_port(int fd) {
    return get_socket_port(fd, getsockname);
}

uint16_t SockUtil::get_peer_port(int fd) {
    return get_socket_port(fd, getpeername);
}

inet_ntoa:线程安全的in_addr转ip字符串

    /**
     * 线程安全的in_addr转ip字符串
     */
    static std::string inet_ntoa(const struct in_addr &addr);
    static std::string inet_ntoa(const struct in6_addr &addr);
    static std::string inet_ntoa(const struct sockaddr *addr);
    static uint16_t inet_port(const struct sockaddr *addr);
    static struct sockaddr_storage make_sockaddr(const char *ip, uint16_t port);

实现:

static inline string my_inet_ntop(int af, const void *addr) {
    string ret;
    ret.resize(128);
    if (!inet_ntop(af, addr, (char *) ret.data(), ret.size())) {
        ret.clear();
    } else {
        ret.resize(strlen(ret.data()));
    }
    return ret;
}

string SockUtil::inet_ntoa(const struct in_addr &addr) {
    return my_inet_ntop(AF_INET, &addr);
}

std::string SockUtil::inet_ntoa(const struct in6_addr &addr) {
    return my_inet_ntop(AF_INET6, &addr);
}


std::string SockUtil::inet_ntoa(const struct sockaddr *addr) {
    switch (addr->sa_family) {
        case AF_INET: return SockUtil::inet_ntoa(((struct sockaddr_in *)addr)->sin_addr);
        case AF_INET6: {
            if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)addr)->sin6_addr)) {
                struct in_addr addr4;
                memcpy(&addr4, 12 + (char *)&(((struct sockaddr_in6 *)addr)->sin6_addr), 4);
                return SockUtil::inet_ntoa(addr4);
            }
            return SockUtil::inet_ntoa(((struct sockaddr_in6 *)addr)->sin6_addr);
        }
        default: assert(false); return "";
    }
}



uint16_t SockUtil::inet_port(const struct sockaddr *addr) {
    switch (addr->sa_family) {
        case AF_INET: return ntohs(((struct sockaddr_in *)addr)->sin_port);
        case AF_INET6: return ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
        default: assert(false); return 0;
    }
}


struct sockaddr_storage SockUtil::make_sockaddr(const char *host, uint16_t port) {
    struct sockaddr_storage storage;
    bzero(&storage, sizeof(storage));

    struct in_addr addr;
    struct in6_addr addr6;
    if (1 == inet_pton(AF_INET, host, &addr)) {
        // host是ipv4
        reinterpret_cast<struct sockaddr_in &>(storage).sin_addr = addr;
        reinterpret_cast<struct sockaddr_in &>(storage).sin_family = AF_INET;
        reinterpret_cast<struct sockaddr_in &>(storage).sin_port = htons(port);
        return storage;
    }
    if (1 == inet_pton(AF_INET6, host, &addr6)) {
        // host是ipv6
        reinterpret_cast<struct sockaddr_in6 &>(storage).sin6_addr = addr6;
        reinterpret_cast<struct sockaddr_in6 &>(storage).sin6_family = AF_INET6;
        reinterpret_cast<struct sockaddr_in6 &>(storage).sin6_port = htons(port);
        return storage;
    }
    throw std::invalid_argument(string("not ip address:") + host);
}


设置socket

在这里插入图片描述

setNoDelay:开启TCP_NODELAY

    /**
     * 开启TCP_NODELAY,降低TCP交互延时
     * @param fd socket fd号
     * @param on 是否开启
     * @return 0代表成功,-1为失败
     */
    static int setNoDelay(int fd, bool on = true);
int SockUtil::setNoDelay(int fd, bool on) {
    int opt = on ? 1 : 0;
    int ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &opt, static_cast<socklen_t>(sizeof(opt)));
    if (ret == -1) {
        TraceL << "设置 NoDelay 失败!";
    }
    return ret;
}

setNoBlocked

    /**
     * 设置读写socket是否阻塞
     * @param fd socket fd号
     * @param noblock 是否阻塞
     * @return 0代表成功,-1为失败
     */
    static int setNoBlocked(int fd, bool noblock = true);
int SockUtil::setNoBlocked(int fd, bool noblock) {
    int ul = noblock;
    int ret = ioctl(fd, FIONBIO, &ul); //设置为非阻塞模式
    if (ret == -1) {
        TraceL << "设置非阻塞失败!";
    }

    return ret;
}

setReuseable

   /**
     * 设置后续可绑定复用端口(处于TIME_WAITE状态)
     * @param fd socket fd号
     * @param on 是否开启该特性
     * @return 0代表成功,-1为失败
     */
    static int setReuseable(int fd, bool on = true, bool reuse_port = true);
int SockUtil::setReuseable(int fd, bool on, bool reuse_port) {
    int opt = on ? 1 : 0;
    int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, static_cast<socklen_t>(sizeof(opt)));
    if (ret == -1) {
        TraceL << "设置 SO_REUSEADDR 失败!";
        return ret;
    }
    if (reuse_port) {
        ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *) &opt, static_cast<socklen_t>(sizeof(opt)));
        if (ret == -1) {
            TraceL << "设置 SO_REUSEPORT 失败!";
        }
    }
    return ret;
}

setRecvBuf&&setSendBuf

    /**
     * 设置socket接收缓存,默认貌似8K左右,一般有设置上限
     * 可以通过配置内核配置文件调整
     * @param fd socket fd号
     * @param size 接收缓存大小
     * @return 0代表成功,-1为失败
     */
    static int setRecvBuf(int fd, int size = SOCKET_DEFAULT_BUF_SIZE);


    /**
     * 设置socket接收缓存,默认貌似8K左右,一般有设置上限
     * 可以通过配置内核配置文件调整
     * @param fd socket fd号
     * @param size 接收缓存大小
     * @return 0代表成功,-1为失败
     */
    static int setSendBuf(int fd, int size = SOCKET_DEFAULT_BUF_SIZE);

int SockUtil::setRecvBuf(int fd, int size) {
    int ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size));
    if (ret == -1) {
        TraceL << "设置接收缓冲区失败!";
    }
    return ret;
}


int SockUtil::setSendBuf(int fd, int size) {
    int ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &size, sizeof(size));
    if (ret == -1) {
        TraceL << "设置发送缓冲区失败!";
    }
    return ret;
}

setBroadcast

   /**
     * 运行发送或接收udp广播信息
     * @param fd socket fd号
     * @param on 是否开启该特性
     * @return 0代表成功,-1为失败
     */
    static int setBroadcast(int fd, bool on = true);
int SockUtil::setBroadcast(int fd, bool on) {
    int opt = on ? 1 : 0;
    int ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &opt, static_cast<socklen_t>(sizeof(opt)));
    if (ret == -1) {
        TraceL << "设置 SO_BROADCAST 失败!";
    }
    return ret;
}

socket默认不支持发送广播报文,通过SO_BROADCAST选项的设置,开启广播发送功能。简单总结一下广播报文收发的规律:

  • 客户端socket开启SO_BROADCAST选项后才能发送广播报文,否则调用sendto会报错
  • 服务端无需开启SO_BROADCAST
    • 服务端bind单播地址时,不接受客户端的广播数据,仅接受目的地址为单播地址的报文
    • 服务端bind广播地址时,接收客户端的广播数据
    • 服务端bind通用地址INADDR_ANY时,既能够接收客户端的单播报文,也能接收广播报文

SO_BROADCAST博文

setKeepAlive

   /**
     * 是否开启TCP KeepAlive特性
     * @param fd socket fd号
     * @param on 是否开启该特性
     * @return 0代表成功,-1为失败
     */
    static int setKeepAlive(int fd, bool on = true);
int SockUtil::setKeepAlive(int fd, bool on) {
    int opt = on ? 1 : 0;
    int ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, static_cast<socklen_t>(sizeof(opt)));
    if (ret == -1) {
        TraceL << "设置 SO_KEEPALIVE 失败!";
    }
    return ret;
}

setCloExec

  /**
     * 是否开启FD_CLOEXEC特性(多进程相关)
     * @param fd fd号,不一定是socket
     * @param on 是否开启该特性
     * @return 0代表成功,-1为失败
     */
    static int setCloExec(int fd, bool on = true);

int SockUtil::setCloExec(int fd, bool on) {
    int flags = fcntl(fd, F_GETFD);
    if (flags == -1) {
        TraceL << "设置 FD_CLOEXEC 失败!";
        return -1;
    }
    if (on) {
        flags |= FD_CLOEXEC;
    } else {
        int cloexec = FD_CLOEXEC;
        flags &= ~cloexec;
    }
    int ret = fcntl(fd, F_SETFD, flags);
    if (ret == -1) {
        TraceL << "设置 FD_CLOEXEC 失败!";
        return -1;
    }
    return ret;
}

setCloseWait

    /**
     * 开启SO_LINGER特性
     * @param sock socket fd号
     * @param second 内核等待关闭socket超时时间,单位秒
     * @return 0代表成功,-1为失败
     */
    static int setCloseWait(int sock, int second = 0);

int SockUtil::setCloseWait(int fd, int second) {
    linger m_sLinger;
    //在调用closesocket()时还有数据未发送完,允许等待
    // 若m_sLinger.l_onoff=0;则调用closesocket()后强制关闭
    m_sLinger.l_onoff = (second > 0);
    m_sLinger.l_linger = second; //设置等待时间为x秒
    int ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &m_sLinger, sizeof(linger));
    if (ret == -1) {
        TraceL << "设置 SO_LINGER 失败!";
    }
    return ret;
}

组播设置

在这里插入图片描述

static void clearMulticastAllSocketOption(int socket) {
    int multicastAll = 0;
    (void)setsockopt(socket, IPPROTO_IP, IP_MULTICAST_ALL, (void*)&multicastAll, sizeof multicastAll);
}


template<typename A, typename B>
static inline void write4Byte(A &&a, B &&b) {
    memcpy(&a, &b, sizeof(a));
}

setMultiTTL:设置组播ttl

    /**
     * 设置组播ttl
     * @param sock socket fd号
     * @param ttl ttl值
     * @return 0代表成功,-1为失败
     */
int SockUtil::setMultiTTL(int fd, uint8_t ttl = 64) {
    int ret = -1;
#if defined(IP_MULTICAST_TTL)
    ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &ttl, sizeof(ttl));
    if (ret == -1) {
        TraceL << "设置 IP_MULTICAST_TTL 失败!";
    }
#endif
    clearMulticastAllSocketOption(fd);
    return ret;
}

setMultiIF:设置组播发送网卡


    /**
     * 设置组播发送网卡
     * @param sock socket fd号
     * @param local_ip 本机网卡ip
     * @return 0代表成功,-1为失败
     */
int SockUtil::setMultiIF(int fd, const char *local_ip) {
    int ret = -1;
#if defined(IP_MULTICAST_IF)
    struct in_addr addr;
    addr.s_addr = inet_addr(local_ip);
    ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (char *) &addr, sizeof(addr));
    if (ret == -1) {
        TraceL << "设置 IP_MULTICAST_IF 失败!";
    }
#endif
    clearMulticastAllSocketOption(fd);
    return ret;
}

setMultiLOOP:设置是否接收本机发出的组播包

   /**
     * 设置是否接收本机发出的组播包
     * @param fd socket fd号
     * @param acc 是否接收
     * @return 0代表成功,-1为失败
     */
    static int setMultiLOOP(int fd, bool acc = false);

int SockUtil::setMultiLOOP(int fd, bool accept) {
    int ret = -1;
#if defined(IP_MULTICAST_LOOP)
    uint8_t loop = accept;
    ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &loop, sizeof(loop));
    if (ret == -1) {
        TraceL << "设置 IP_MULTICAST_LOOP 失败!";
    }
#endif
    clearMulticastAllSocketOption(fd);
    return ret;
}

joinMultiAddr/leaveMultiAddr:加入/退出组播

   /**
     * 加入组播
     * @param fd socket fd号
     * @param addr 组播地址
     * @param local_ip 本机网卡ip
     * @return 0代表成功,-1为失败
     */
    static int joinMultiAddr(int fd, const char *addr, const char *local_ip = "0.0.0.0");
    /**
     * 退出组播
     * @param fd socket fd号
     * @param addr 组播地址
     * @param local_ip 本机网卡ip
     * @return 0代表成功,-1为失败
     */
    static int leaveMultiAddr(int fd, const char *addr, const char *local_ip = "0.0.0.0");
int SockUtil::joinMultiAddr(int fd, const char *addr, const char *local_ip) {
    int ret = -1;
#if defined(IP_ADD_MEMBERSHIP)
    struct ip_mreq imr;
    imr.imr_multiaddr.s_addr = inet_addr(addr);
    imr.imr_interface.s_addr = inet_addr(local_ip);
    ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq));
    if (ret == -1) {
        TraceL << "设置 IP_ADD_MEMBERSHIP 失败:" << get_uv_errmsg(true);
    }
#endif
    clearMulticastAllSocketOption(fd);
    return ret;
}

int SockUtil::leaveMultiAddr(int fd, const char *addr, const char *local_ip) {
    int ret = -1;
#if defined(IP_DROP_MEMBERSHIP)
    struct ip_mreq imr;
    imr.imr_multiaddr.s_addr = inet_addr(addr);
    imr.imr_interface.s_addr = inet_addr(local_ip);
    ret = setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq));
    if (ret == -1) {
        TraceL << "设置 IP_DROP_MEMBERSHIP 失败:" << get_uv_errmsg(true);
    }
#endif
    clearMulticastAllSocketOption(fd);
    return ret;
}

joinMultiAddrFilter/leaveMultiAddrFilter:加入/退出组播


    /**
     * 加入组播并只接受该源端的组播数据
     * @param sock socket fd号
     * @param addr 组播地址
     * @param src_ip 数据源端地址
     * @param local_ip  本机网卡ip
     * @return 0代表成功,-1为失败
     */
    static int joinMultiAddrFilter(int sock, const char *addr, const char *src_ip, const char *local_ip = "0.0.0.0");
  /**
     * 退出组播
     * @param fd socket fd号
     * @param addr 组播地址
     * @param src_ip 数据源端地址
     * @param local_ip  本机网卡ip
     * @return 0代表成功,-1为失败
     */

    static int leaveMultiAddrFilter(int fd, const char *addr, const char *src_ip, const char *local_ip = "0.0.0.0");
int SockUtil::joinMultiAddrFilter(int fd, const char *addr, const char *src_ip, const char *local_ip) {
    int ret = -1;
#if defined(IP_ADD_SOURCE_MEMBERSHIP)
    struct ip_mreq_source imr;

    write4Byte(imr.imr_multiaddr, inet_addr(addr));
    write4Byte(imr.imr_sourceaddr, inet_addr(src_ip));
    write4Byte(imr.imr_interface, inet_addr(local_ip));

    ret = setsockopt(fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq_source));
    if (ret == -1) {
        TraceL << "设置 IP_ADD_SOURCE_MEMBERSHIP 失败:" << get_uv_errmsg(true);
    }
#endif
    clearMulticastAllSocketOption(fd);
    return ret;
}



int SockUtil::leaveMultiAddrFilter(int fd, const char *addr, const char *src_ip, const char *local_ip) {
    int ret = -1;
#if defined(IP_DROP_SOURCE_MEMBERSHIP)
    struct ip_mreq_source imr;

    write4Byte(imr.imr_multiaddr, inet_addr(addr));
    write4Byte(imr.imr_sourceaddr, inet_addr(src_ip));
    write4Byte(imr.imr_interface, inet_addr(local_ip));

    ret = setsockopt(fd, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq_source));
    if (ret == -1) {
        TraceL << "设置 IP_DROP_SOURCE_MEMBERSHIP 失败:" << get_uv_errmsg(true);
    }
#endif
    clearMulticastAllSocketOption(fd);
    return ret;
}

网卡相关

在这里插入图片描述

template<typename FUN>
void for_each_netAdapter_posix(FUN &&fun){ //type: struct ifreq *
    struct ifconf ifconf;
    char buf[1024 * 10];
    //初始化ifconf
    ifconf.ifc_len = sizeof(buf);
    ifconf.ifc_buf = buf;
    int sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        WarnL << "创建套接字失败:" << get_uv_errmsg(true);
        return;
    }
    if (-1 == ioctl(sockfd, SIOCGIFCONF, &ifconf)) {    //获取所有接口信息
        WarnL << "ioctl 失败:" << get_uv_errmsg(true);
        close(sockfd);
        return;
    }
    close(sockfd);
    //接下来一个一个的获取IP地址
    struct ifreq * adapter = (struct ifreq*) buf;
    for (int i = (ifconf.ifc_len / sizeof(struct ifreq)); i > 0; --i,++adapter) {
        if(fun(adapter)){
            break;
        }
    }
}

getInterfaceList:获取本机网卡列表

vector<map<string, string> > SockUtil::getInterfaceList() {
    vector<map<string, string> > ret;
    for_each_netAdapter_posix([&](struct ifreq *adapter){
        map<string,string> obj;
        obj["ip"] = SockUtil::inet_ntoa(&(adapter->ifr_addr));
        obj["name"] = adapter->ifr_name;
        ret.emplace_back(std::move(obj));
        return false;
    });
    return ret;
}

get_local_ip:获取本机默认网卡ip

bool check_ip(string &address, const string &ip) {
    if (ip != "127.0.0.1" && ip != "0.0.0.0") {
        /*获取一个有效IP*/
        address = ip;
        uint32_t addressInNetworkOrder = htonl(inet_addr(ip.data()));
        if (/*(addressInNetworkOrder >= 0x0A000000 && addressInNetworkOrder < 0x0E000000) ||*/
            (addressInNetworkOrder >= 0xAC100000 && addressInNetworkOrder < 0xAC200000) ||
            (addressInNetworkOrder >= 0xC0A80000 && addressInNetworkOrder < 0xC0A90000)) {
            //A类私有IP地址:
            //10.0.0.0~10.255.255.255
            //B类私有IP地址:
            //172.16.0.0~172.31.255.255
            //C类私有IP地址:
            //192.168.0.0~192.168.255.255
            //如果是私有地址 说明在nat内部

            /* 优先采用局域网地址,该地址很可能是wifi地址
             * 一般来说,无线路由器分配的地址段是BC类私有ip地址
             * 而A类地址多用于蜂窝移动网络
             */
            return true;
        }
    }
    return false;
}


string SockUtil::get_local_ip() {
    string address = "127.0.0.1";
    for_each_netAdapter_posix([&](struct ifreq *adapter){
        string ip = SockUtil::inet_ntoa(&(adapter->ifr_addr));
        if (strstr(adapter->ifr_name, "docker")) {
            return false;
        }
        return check_ip(address,ip);
    });
    return address;
}

get_ifr_ip:获取网卡IP

    /**
     * 获取网卡ip
     * @param if_name 网卡名
     */
	string SockUtil::get_ifr_ip(const char *if_name) {
	   string ret;
	    for_each_netAdapter_posix([&](struct ifreq *adapter){
	        if(strcmp(adapter->ifr_name,if_name) == 0) {
	            ret = SockUtil::inet_ntoa(&(adapter->ifr_addr));
	            return true;
	        }
	        return false;
	    });
	    return ret;
	}

get_ifr_ip:获获取网卡名

    /**
     * 获取网卡名
     * @param local_op 网卡ip
     */

	string SockUtil::get_ifr_name(const char *local_ip) {
	    string ret = "en0";
	    for_each_netAdapter_posix([&](struct ifreq *adapter){
	        string ip = SockUtil::inet_ntoa(&(adapter->ifr_addr));
	        if(ip == local_ip) {
	            ret = adapter->ifr_name;
	            return true;
	        }
	        return false;
	    });
	    return ret;
	}

get_ifr_mask:根据网卡名获取子网掩码


    /**
     * 根据网卡名获取子网掩码
     * @param if_name 网卡名
     */

	string SockUtil::get_ifr_mask(const char *if_name) {
	    int fd;
	    struct ifreq ifr_mask;
	    fd = socket(AF_INET, SOCK_STREAM, 0);
	    if (fd == -1) {
	        WarnL << "创建套接字失败:" << get_uv_errmsg(true);
	        return "";
	    }
	    memset(&ifr_mask, 0, sizeof(ifr_mask));
	    strncpy(ifr_mask.ifr_name, if_name, sizeof(ifr_mask.ifr_name) - 1);
	    if ((ioctl(fd, SIOCGIFNETMASK, &ifr_mask)) < 0) {
	        WarnL << "ioctl 失败:" << if_name << " " << get_uv_errmsg(true);
	        close(fd);
	        return "";
	    }
	    close(fd);
	    return SockUtil::inet_ntoa(&(ifr_mask.ifr_netmask));
	}

get_ifr_brdaddr:根据网卡名获取广播地址

    /**
     * 根据网卡名获取广播地址
     * @param if_name 网卡名
     */


	string SockUtil::get_ifr_brdaddr(const char *if_name) {
	    int fd;
	    struct ifreq ifr_mask;
	    fd = socket( AF_INET, SOCK_STREAM, 0);
	    if (fd == -1) {
	        WarnL << "创建套接字失败:" << get_uv_errmsg(true);
	        return "";
	    }
	    memset(&ifr_mask, 0, sizeof(ifr_mask));
	    strncpy(ifr_mask.ifr_name, if_name, sizeof(ifr_mask.ifr_name) - 1);
	    if ((ioctl(fd, SIOCGIFBRDADDR, &ifr_mask)) < 0) {
	        WarnL << "ioctl 失败:" << get_uv_errmsg(true);
	        close(fd);
	        return "";
	    }
	    close(fd);
	    return SockUtil::inet_ntoa(&(ifr_mask.ifr_broadaddr));
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值