c++ socket (4) 选项设置(c++中socket编程)

c++ socket (4) 选项设置

socket选项设置及获取,一般就会集合成方便类

#ifndef __SOCKET_H__
#define __SOCKET_H__

#include <memory>
#include <string>
#include <memory.h>

#ifdef _WIN32
#include <WinSock2.h>
#include <ws2tcpip.h>

#define socket_t SOCKET
#define invalid_socket INVALID_SOCKET
#define socklen_t int
#define errno GetLastError()

#pragma comment(lib, "ws2_32.lib")  
#endif // _WIN32

#ifdef __linux__
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <netdb.h>
#include <fcntl.h> 

#define socket_t int
#define invalid_socket -1
#define closesocket close
#endif

class Socket {

    static bool init_socket()
    {
#ifdef _WIN32
        WSADATA ws;
        static bool _init = (0 == WSAStartup(MAKEWORD(2, 2), &ws));
        return _init;
#endif
        return true;
    }

public:
    
    //创建服务器(优先使用IPv6)
    static socket_t CreateServer(int port, int type = SOCK_STREAM)
    {
        init_socket();
        struct addrinfo hints;
        memset(&hints, 0, sizeof hints);
        hints.ai_family = AF_UNSPEC; // 任何地址家族  
        hints.ai_socktype = type; // 任何套接字类型  
        hints.ai_protocol = (type == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP;
        hints.ai_flags = AI_PASSIVE;

        int ret = 0;
        struct addrinfo* res;
        if ((ret = getaddrinfo(nullptr, std::to_string(port).c_str(), &hints, &res)) != 0) {
            std::cout << "err getaddrinfo:" << gai_strerror(ret) << std::endl;
            return invalid_socket;
        }

        int reuseAddr = 1;
        socket_t skt = invalid_socket;
        for (struct addrinfo* p = res; p != 0; p = p->ai_next) {

            if ((p->ai_family != AF_INET && p->ai_family != AF_INET6)
                || (skt != invalid_socket && p->ai_family == AF_INET)) {
                continue;
            }
            //关闭之前的IPv4
            if (skt != invalid_socket) {
                closesocket(skt);
                skt = invalid_socket;
            }
            //创建socket 及 bind
            if (invalid_socket == (skt = socket(p->ai_family, p->ai_socktype, p->ai_protocol))
                || 0 != setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseAddr, sizeof(int))
                || 0 != bind(skt, (const struct sockaddr*)p->ai_addr, p->ai_addrlen)) {

                if (skt != invalid_socket) {
                    closesocket(skt);
                    skt = invalid_socket;
                }
                continue;
            }
            //如果是IPv6,就不必再继续了
            if (skt != invalid_socket && p->ai_family == AF_INET6) {
                break;
            }
        }
        freeaddrinfo(res);
        return skt;
    }

    //连接服务端
    static socket_t ConnectServer(const char* addr, int port, int timeout = 0)
    {
        init_socket();
        struct addrinfo hints;
        memset(&hints, 0, sizeof hints);
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;

        int ret = 0;
        struct addrinfo* res;
        if ((ret = getaddrinfo(addr, std::to_string(port).c_str(), &hints, &res)) != 0) {
            std::cerr << "getaddrinfo:" << gai_strerror(ret) << std::endl;
            return invalid_socket;
        }

        int reuseAddr = 1;
        socket_t skt = invalid_socket;
        for (struct addrinfo* p = res; p != 0 && skt == invalid_socket; p = p->ai_next) {

            if ((p->ai_family != AF_INET && p->ai_family != AF_INET6) 
                || invalid_socket == (skt = socket(p->ai_family, p->ai_socktype, p->ai_protocol))) {
                continue;
            }

            int oldSec = 0;
            if (timeout > 0 && GetSendTimeout(skt, oldSec)) {
                SetSendTimeout(skt, timeout);
            }

            if (0 != connect(skt, (const struct sockaddr*)p->ai_addr, p->ai_addrlen)) {
                std::cerr << "connect:" << strerror(errno)<<std::endl;
                closesocket(skt);
                skt = invalid_socket;
            }

            if (timeout > 0 && skt != invalid_socket) {
                SetSendTimeout(skt, oldSec);
            }
        }
        freeaddrinfo(res);
        return skt;
    }

    //获取发送超时
    static bool GetSendTimeout(socket_t skt, int& seconds) 
    {
        struct timeval tv;
        socklen_t len = sizeof(struct timeval);
        if (0 != getsockopt(skt, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, &len)) {
            return false;
        }
        seconds = tv.tv_sec;
        return true;
    }

    //设置发送超时
    static bool SetSendTimeout(socket_t skt, int seconds)
    {
        struct timeval tv;
        tv.tv_sec = seconds;
        tv.tv_usec = 0;
        socklen_t len = sizeof(struct timeval);
        return 0 == setsockopt(skt, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, len);
    }

    //获取接收超时
    static bool GetRecvTimeout(socket_t skt, int& seconds) 
    {
        struct timeval tv;
        socklen_t len = sizeof(struct timeval);
        if (0 != getsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, &len)) {
            return false;
        }
        seconds = tv.tv_sec;
        return true;
    }

    //设置接收超时
    static bool SetRecvTimeout(socket_t skt, int seconds)
    {
        struct timeval tv;
        tv.tv_sec = seconds;
        tv.tv_usec = 0;
        socklen_t len = sizeof(struct timeval);
        return 0 == setsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, len);
    }

    //获取发送缓冲区大小
    static bool GetSendBufferSize(socket_t skt, int& size) 
    {
        socklen_t len = sizeof(int);
        if (0 != getsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, (char*)&size, &len)) {
            return false;
        }
        return true;
    }

    //设置发送缓冲区大小
    static bool SetSendBufferSize(socket_t skt, int size)
    {
        socklen_t len = sizeof(int);
        return 0 == setsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, (char*)&size, len);
    }

    //获取接收缓冲区大小
    static bool GetRecvBufferSize(socket_t skt, int& size) 
    {   
        socklen_t len = sizeof(int);
        if (0 != getsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, (char*)&size, &len)) {
            return false;
        }
        return true;
    }

    //设置接收缓冲区大小
    static bool SetRecvBufferSize(socket_t skt, int size)
    {
        socklen_t len = sizeof(int);
        return 0 == setsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, (char*)&size, len);
    }

    //设置地址复用
    static bool SetReuseAddr(socket_t skt)
    {
        int reuse = 1;
        socklen_t len = sizeof(int);
        return 0 == setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, len);
    }

    //设置端口复用
    static bool SetReusePort(socket_t skt)
    {
#ifdef __linux__
        int reuse = 1;
        socklen_t len = sizeof(int);
        return 0 == setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, (char*)&reuse, len);
#endif
        return true;
    }

    //设置保持连接
    static bool SetKeepAlive(socket_t skt)
    {
        int alive = 1;
        socklen_t len = sizeof(int);
        return 0 == setsockopt(skt, SOL_SOCKET, SO_KEEPALIVE, (char*)&alive, len);
    }

    //获取LINGER配置
    static bool GetLinger(socket_t skt, int& onoff, int& seconds) 
    {
        struct linger val;
        socklen_t len = sizeof(struct linger);
        if (0 != getsockopt(skt, SOL_SOCKET, SO_LINGER, (char*)&val, &len)) {
            return false;
        }
        onoff = val.l_onoff;
        seconds = seconds;
        return true;
    }

    //设置LINGER配置
    static bool SetLinger(socket_t skt, int onoff, int seconds)
    {
        struct linger val;
        val.l_onoff = onoff;
        val.l_linger = seconds;
        socklen_t len = sizeof(struct linger);
        return 0 == setsockopt(skt, SOL_SOCKET, SO_LINGER, (char*)&val, len);
    }

    //获取IO模式
    static bool GetIOMode(socket_t skt, bool& blockMode)
    {
#ifdef __linux__
        int flags = 0;
        if (-1 == (flags = fcntl(skt, F_GETFL, 0))) {
            return false;
        }
        blockMode = 0 == (flags & O_NONBLOCK);
        return true;
#else
        int val = 0;
        socklen_t len = sizeof(int);
        if (0 != getsockopt(skt, SOL_SOCKET, SO_OOBINLINE, (char*)&val, &len)) {
            return false;
        }
        blockMode = val == 1;
        return true;
#endif
    }

    //设置IO模式
    static bool SetIOMode(socket_t skt, bool blockMode) 
    {
#ifdef __linux__
        int flags = 0;
        if (-1 == (flags = fcntl(skt, F_GETFL, 0))) {
            return false;
        }
        blockMode ? flags &= ~O_NONBLOCK : flags |= O_NONBLOCK;
        return -1 != fcntl(skt, F_SETFL, flags);
#else
        u_long mode = blockMode ? 1 : 0;
        return ioctlsocket(skt, FIONBIO, &mode) == SOCKET_ERROR;
#endif
    }

    //填充sockaddr_in
    static bool MakeAddress(struct sockaddr_in& addr, const char* ip, int port)
    {
        memset(&addr, 0, sizeof(struct sockaddr_in));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        //addr.sin_addr.s_addr = inet_addr(ip);
        return (ip == nullptr) ? true : 1 == inet_pton(AF_INET, ip, &addr.sin_addr.s_addr);
    }

    //填充sockaddr_in6
    static bool MakeAddress(struct sockaddr_in6& addr, const char* ip6, int port)
    {
        memset(&addr, 0, sizeof(struct sockaddr_in6));
        addr.sin6_family = AF_INET6;
        addr.sin6_port = htons(port);
        return (ip6 == nullptr) ? true : 1 == inet_pton(AF_INET6, ip6, &addr.sin6_addr);
    }

    //解析sockaddr_in, 得到IP串及地址
    static bool ParseAddress(const struct sockaddr_in* addr, std::string& ip, int& port)
    {
        char buf[32] = { 0 };
        port = ntohs(addr->sin_port);
        if (nullptr == inet_ntop(AF_INET, &addr->sin_addr.s_addr, buf, 32)) {
            return false;
        }
        ip = buf;
        return true;
    }

    //解析sockaddr_in, 得到IP串及地址
    static bool ParseAddress(const struct sockaddr_in6* addr, std::string& ip, int& port)
    {
        char buf[32] = { 0 };
        port = ntohs(addr->sin6_port);
        if (nullptr == inet_ntop(AF_INET, &addr->sin6_addr, buf, 32)) {
            return false;
        }
        ip = buf;
        return true;
    }
};
#endif

选项说明(参考下面地址, 就不搬运了)
SOL_SOCKET 套接字选项 (Winsock2.h) - Win32 apps | Microsoft Learn

socket(7) - Linux manual page



原文链接:,转发请注明来源!